You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

886 lines
28 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - Search
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ($, window, document, undefined) {
  12. "use strict";
  13. $.fn.search = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. returnedValue
  23. ;
  24. $(this)
  25. .each(function() {
  26. var
  27. settings = $.extend(true, {}, $.fn.search.settings, parameters),
  28. className = settings.className,
  29. selector = settings.selector,
  30. error = settings.error,
  31. namespace = settings.namespace,
  32. eventNamespace = '.' + namespace,
  33. moduleNamespace = namespace + '-module',
  34. $module = $(this),
  35. $prompt = $module.find(selector.prompt),
  36. $searchButton = $module.find(selector.searchButton),
  37. $results = $module.find(selector.results),
  38. $result = $module.find(selector.result),
  39. $category = $module.find(selector.category),
  40. element = this,
  41. instance = $module.data(moduleNamespace),
  42. module
  43. ;
  44. module = {
  45. initialize: function() {
  46. module.verbose('Initializing module');
  47. var
  48. prompt = $prompt[0],
  49. inputEvent = (prompt !== undefined && prompt.oninput !== undefined)
  50. ? 'input'
  51. : (prompt !== undefined && prompt.onpropertychange !== undefined)
  52. ? 'propertychange'
  53. : 'keyup'
  54. ;
  55. if(settings.automatic) {
  56. $prompt
  57. .on(inputEvent + eventNamespace, module.search.throttle)
  58. ;
  59. }
  60. $prompt
  61. .on('focus' + eventNamespace, module.event.focus)
  62. .on('blur' + eventNamespace, module.event.blur)
  63. .on('keydown' + eventNamespace, module.handleKeyboard)
  64. ;
  65. $searchButton
  66. .on('click' + eventNamespace, module.search.query)
  67. ;
  68. $results
  69. .on('mousedown' + eventNamespace, module.event.mousedown)
  70. .on('mouseup' + eventNamespace, module.event.mouseup)
  71. .on('click' + eventNamespace, selector.result, module.results.select)
  72. ;
  73. module.instantiate();
  74. },
  75. instantiate: function() {
  76. module.verbose('Storing instance of module', module);
  77. instance = module;
  78. $module
  79. .data(moduleNamespace, module)
  80. ;
  81. },
  82. destroy: function() {
  83. module.verbose('Destroying instance');
  84. $module
  85. .removeData(moduleNamespace)
  86. ;
  87. $prompt
  88. .off(eventNamespace)
  89. ;
  90. $searchButton
  91. .off(eventNamespace)
  92. ;
  93. $results
  94. .off(eventNamespace)
  95. ;
  96. },
  97. event: {
  98. focus: function() {
  99. $module
  100. .addClass(className.focus)
  101. ;
  102. clearTimeout(module.timer);
  103. module.search.throttle();
  104. if(module.has.minimum()) {
  105. module.results.show();
  106. }
  107. },
  108. mousedown: function() {
  109. module.resultsClicked = true;
  110. },
  111. mouseup: function() {
  112. module.resultsClicked = false;
  113. },
  114. blur: function(event) {
  115. module.search.cancel();
  116. $module
  117. .removeClass(className.focus)
  118. ;
  119. if(!module.resultsClicked) {
  120. module.timer = setTimeout(module.results.hide, settings.hideDelay);
  121. }
  122. }
  123. },
  124. handleKeyboard: function(event) {
  125. var
  126. // force latest jq dom
  127. $result = $module.find(selector.result),
  128. $category = $module.find(selector.category),
  129. keyCode = event.which,
  130. keys = {
  131. backspace : 8,
  132. enter : 13,
  133. escape : 27,
  134. upArrow : 38,
  135. downArrow : 40
  136. },
  137. activeClass = className.active,
  138. currentIndex = $result.index( $result.filter('.' + activeClass) ),
  139. resultSize = $result.size(),
  140. newIndex
  141. ;
  142. // search shortcuts
  143. if(keyCode == keys.escape) {
  144. module.verbose('Escape key pressed, blurring search field');
  145. $prompt
  146. .trigger('blur')
  147. ;
  148. }
  149. // result shortcuts
  150. if($results.filter(':visible').size() > 0) {
  151. if(keyCode == keys.enter) {
  152. module.verbose('Enter key pressed, selecting active result');
  153. if( $result.filter('.' + activeClass).size() > 0 ) {
  154. $.proxy(module.results.select, $result.filter('.' + activeClass) )(event);
  155. event.preventDefault();
  156. return false;
  157. }
  158. }
  159. else if(keyCode == keys.upArrow) {
  160. module.verbose('Up key pressed, changing active result');
  161. newIndex = (currentIndex - 1 < 0)
  162. ? currentIndex
  163. : currentIndex - 1
  164. ;
  165. $category
  166. .removeClass(activeClass)
  167. ;
  168. $result
  169. .removeClass(activeClass)
  170. .eq(newIndex)
  171. .addClass(activeClass)
  172. .closest($category)
  173. .addClass(activeClass)
  174. ;
  175. event.preventDefault();
  176. }
  177. else if(keyCode == keys.downArrow) {
  178. module.verbose('Down key pressed, changing active result');
  179. newIndex = (currentIndex + 1 >= resultSize)
  180. ? currentIndex
  181. : currentIndex + 1
  182. ;
  183. $category
  184. .removeClass(activeClass)
  185. ;
  186. $result
  187. .removeClass(activeClass)
  188. .eq(newIndex)
  189. .addClass(activeClass)
  190. .closest($category)
  191. .addClass(activeClass)
  192. ;
  193. event.preventDefault();
  194. }
  195. }
  196. else {
  197. // query shortcuts
  198. if(keyCode == keys.enter) {
  199. module.verbose('Enter key pressed, executing query');
  200. module.search.query();
  201. $searchButton
  202. .addClass(className.down)
  203. ;
  204. $prompt
  205. .one('keyup', function(){
  206. $searchButton
  207. .removeClass(className.down)
  208. ;
  209. })
  210. ;
  211. }
  212. }
  213. },
  214. has: {
  215. minimum: function() {
  216. var
  217. searchTerm = $prompt.val(),
  218. numCharacters = searchTerm.length
  219. ;
  220. return (numCharacters >= settings.minCharacters);
  221. }
  222. },
  223. search: {
  224. cancel: function() {
  225. var
  226. xhr = $module.data('xhr') || false
  227. ;
  228. if( xhr && xhr.state() != 'resolved') {
  229. module.debug('Cancelling last search');
  230. xhr.abort();
  231. }
  232. },
  233. throttle: function() {
  234. clearTimeout(module.timer);
  235. if(module.has.minimum()) {
  236. module.timer = setTimeout(module.search.query, settings.searchDelay);
  237. }
  238. else {
  239. module.results.hide();
  240. }
  241. },
  242. query: function() {
  243. var
  244. searchTerm = $prompt.val(),
  245. cachedHTML = module.search.cache.read(searchTerm)
  246. ;
  247. if(cachedHTML) {
  248. module.debug("Reading result for '" + searchTerm + "' from cache");
  249. module.results.add(cachedHTML);
  250. }
  251. else {
  252. module.debug("Querying for '" + searchTerm + "'");
  253. if($.isPlainObject(settings.source) || $.isArray(settings.source)) {
  254. module.search.local(searchTerm);
  255. }
  256. else if(settings.apiSettings) {
  257. module.search.remote(searchTerm);
  258. }
  259. else if($.api !== undefined && $.api.settings.api.search !== undefined) {
  260. module.debug('Searching with default search API endpoint');
  261. settings.apiSettings = {
  262. action: 'search'
  263. };
  264. module.search.remote(searchTerm);
  265. }
  266. else {
  267. module.error(error.source);
  268. }
  269. $.proxy(settings.onSearchQuery, $module)(searchTerm);
  270. }
  271. },
  272. local: function(searchTerm) {
  273. var
  274. results = [],
  275. fullTextResults = [],
  276. searchFields = $.isArray(settings.searchFields)
  277. ? settings.searchFields
  278. : [settings.searchFields],
  279. searchRegExp = new RegExp('(?:\s|^)' + searchTerm, 'i'),
  280. fullTextRegExp = new RegExp(searchTerm, 'i'),
  281. searchHTML
  282. ;
  283. $module
  284. .addClass(className.loading)
  285. ;
  286. // iterate through search fields in array order
  287. $.each(searchFields, function(index, field) {
  288. $.each(settings.source, function(label, content) {
  289. var
  290. fieldExists = (typeof content[field] == 'string'),
  291. notAlreadyResult = ($.inArray(content, results) == -1 && $.inArray(content, fullTextResults) == -1)
  292. ;
  293. if(fieldExists && notAlreadyResult) {
  294. if( searchRegExp.test( content[field] ) ) {
  295. results.push(content);
  296. }
  297. else if( settings.searchFullText && fullTextRegExp.test( content[field] ) ) {
  298. fullTextResults.push(content);
  299. }
  300. }
  301. });
  302. });
  303. searchHTML = module.results.generate({
  304. results: $.merge(results, fullTextResults)
  305. });
  306. $module
  307. .removeClass(className.loading)
  308. ;
  309. module.search.cache.write(searchTerm, searchHTML);
  310. module.results.add(searchHTML);
  311. },
  312. remote: function(searchTerm) {
  313. var
  314. apiSettings = {
  315. stateContext : $module,
  316. urlData : {
  317. query: searchTerm
  318. },
  319. onSuccess : function(response) {
  320. searchHTML = module.results.generate(response);
  321. module.search.cache.write(searchTerm, searchHTML);
  322. module.results.add(searchHTML);
  323. },
  324. onFailure : module.error
  325. },
  326. searchHTML
  327. ;
  328. module.search.cancel();
  329. module.debug('Executing search');
  330. $.extend(true, apiSettings, settings.apiSettings);
  331. $.api(apiSettings);
  332. },
  333. cache: {
  334. read: function(name) {
  335. var
  336. cache = $module.data('cache')
  337. ;
  338. return (settings.cache && (typeof cache == 'object') && (cache[name] !== undefined) )
  339. ? cache[name]
  340. : false
  341. ;
  342. },
  343. write: function(name, value) {
  344. var
  345. cache = ($module.data('cache') !== undefined)
  346. ? $module.data('cache')
  347. : {}
  348. ;
  349. cache[name] = value;
  350. $module
  351. .data('cache', cache)
  352. ;
  353. }
  354. }
  355. },
  356. results: {
  357. generate: function(response) {
  358. module.debug('Generating html from response', response);
  359. var
  360. template = settings.templates[settings.type],
  361. html = ''
  362. ;
  363. if(($.isPlainObject(response.results) && !$.isEmptyObject(response.results)) || ($.isArray(response.results) && response.results.length > 0) ) {
  364. if(settings.maxResults > 0) {
  365. response.results = $.isArray(response.results)
  366. ? response.results.slice(0, settings.maxResults)
  367. : response.results
  368. ;
  369. }
  370. if($.isFunction(template)) {
  371. html = template(response);
  372. }
  373. else {
  374. module.error(error.noTemplate, false);
  375. }
  376. }
  377. else {
  378. html = module.message(error.noResults, 'empty');
  379. }
  380. $.proxy(settings.onResults, $module)(response);
  381. return html;
  382. },
  383. add: function(html) {
  384. if(settings.onResultsAdd == 'default' || $.proxy(settings.onResultsAdd, $results)(html) == 'default') {
  385. $results
  386. .html(html)
  387. ;
  388. }
  389. module.results.show();
  390. },
  391. show: function() {
  392. if( ($results.filter(':visible').size() === 0) && ($prompt.filter(':focus').size() > 0) && $results.html() !== '') {
  393. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported') && !$results.transition('is inward')) {
  394. module.debug('Showing results with css animations');
  395. $results
  396. .transition({
  397. animation : settings.transition + ' in',
  398. duration : settings.duration,
  399. queue : true
  400. })
  401. ;
  402. }
  403. else {
  404. module.debug('Showing results with javascript');
  405. $results
  406. .stop()
  407. .fadeIn(settings.duration, settings.easing)
  408. ;
  409. }
  410. $.proxy(settings.onResultsOpen, $results)();
  411. }
  412. },
  413. hide: function() {
  414. if($results.filter(':visible').size() > 0) {
  415. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported') && !$results.transition('is outward')) {
  416. module.debug('Hiding results with css animations');
  417. $results
  418. .transition({
  419. animation : settings.transition + ' out',
  420. duration : settings.duration,
  421. queue : true
  422. })
  423. ;
  424. }
  425. else {
  426. module.debug('Hiding results with javascript');
  427. $results
  428. .stop()
  429. .fadeIn(settings.duration, settings.easing)
  430. ;
  431. }
  432. $.proxy(settings.onResultsClose, $results)();
  433. }
  434. },
  435. select: function(event) {
  436. module.debug('Search result selected');
  437. var
  438. $result = $(this),
  439. $title = $result.find('.title'),
  440. title = $title.html()
  441. ;
  442. if(settings.onSelect == 'default' || $.proxy(settings.onSelect, this)(event) == 'default') {
  443. var
  444. $link = $result.find('a[href]').eq(0),
  445. $title = $result.find(selector.title).eq(0),
  446. href = $link.attr('href') || false,
  447. target = $link.attr('target') || false,
  448. name = ($title.size() > 0)
  449. ? $title.text()
  450. : false
  451. ;
  452. module.results.hide();
  453. if(name) {
  454. $prompt.val(name);
  455. }
  456. if(href) {
  457. if(target == '_blank' || event.ctrlKey) {
  458. window.open(href);
  459. }
  460. else {
  461. window.location.href = (href);
  462. }
  463. }
  464. }
  465. }
  466. },
  467. // displays mesage visibly in search results
  468. message: function(text, type) {
  469. type = type || 'standard';
  470. module.results.add( settings.templates.message(text, type) );
  471. return settings.templates.message(text, type);
  472. },
  473. setting: function(name, value) {
  474. if( $.isPlainObject(name) ) {
  475. $.extend(true, settings, name);
  476. }
  477. else if(value !== undefined) {
  478. settings[name] = value;
  479. }
  480. else {
  481. return settings[name];
  482. }
  483. },
  484. internal: function(name, value) {
  485. if( $.isPlainObject(name) ) {
  486. $.extend(true, module, name);
  487. }
  488. else if(value !== undefined) {
  489. module[name] = value;
  490. }
  491. else {
  492. return module[name];
  493. }
  494. },
  495. debug: function() {
  496. if(settings.debug) {
  497. if(settings.performance) {
  498. module.performance.log(arguments);
  499. }
  500. else {
  501. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  502. module.debug.apply(console, arguments);
  503. }
  504. }
  505. },
  506. verbose: function() {
  507. if(settings.verbose && settings.debug) {
  508. if(settings.performance) {
  509. module.performance.log(arguments);
  510. }
  511. else {
  512. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  513. module.verbose.apply(console, arguments);
  514. }
  515. }
  516. },
  517. error: function() {
  518. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  519. module.error.apply(console, arguments);
  520. },
  521. performance: {
  522. log: function(message) {
  523. var
  524. currentTime,
  525. executionTime,
  526. previousTime
  527. ;
  528. if(settings.performance) {
  529. currentTime = new Date().getTime();
  530. previousTime = time || currentTime;
  531. executionTime = currentTime - previousTime;
  532. time = currentTime;
  533. performance.push({
  534. 'Name' : message[0],
  535. 'Arguments' : [].slice.call(message, 1) || '',
  536. 'Element' : element,
  537. 'Execution Time' : executionTime
  538. });
  539. }
  540. clearTimeout(module.performance.timer);
  541. module.performance.timer = setTimeout(module.performance.display, 100);
  542. },
  543. display: function() {
  544. var
  545. title = settings.name + ':',
  546. totalTime = 0
  547. ;
  548. time = false;
  549. clearTimeout(module.performance.timer);
  550. $.each(performance, function(index, data) {
  551. totalTime += data['Execution Time'];
  552. });
  553. title += ' ' + totalTime + 'ms';
  554. if(moduleSelector) {
  555. title += ' \'' + moduleSelector + '\'';
  556. }
  557. if($allModules.size() > 1) {
  558. title += ' ' + '(' + $allModules.size() + ')';
  559. }
  560. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  561. console.groupCollapsed(title);
  562. if(console.table) {
  563. console.table(performance);
  564. }
  565. else {
  566. $.each(performance, function(index, data) {
  567. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  568. });
  569. }
  570. console.groupEnd();
  571. }
  572. performance = [];
  573. }
  574. },
  575. invoke: function(query, passedArguments, context) {
  576. var
  577. object = instance,
  578. maxDepth,
  579. found,
  580. response
  581. ;
  582. passedArguments = passedArguments || queryArguments;
  583. context = element || context;
  584. if(typeof query == 'string' && object !== undefined) {
  585. query = query.split(/[\. ]/);
  586. maxDepth = query.length - 1;
  587. $.each(query, function(depth, value) {
  588. var camelCaseValue = (depth != maxDepth)
  589. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  590. : query
  591. ;
  592. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  593. object = object[camelCaseValue];
  594. }
  595. else if( object[camelCaseValue] !== undefined ) {
  596. found = object[camelCaseValue];
  597. return false;
  598. }
  599. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  600. object = object[value];
  601. }
  602. else if( object[value] !== undefined ) {
  603. found = object[value];
  604. return false;
  605. }
  606. else {
  607. return false;
  608. }
  609. });
  610. }
  611. if ( $.isFunction( found ) ) {
  612. response = found.apply(context, passedArguments);
  613. }
  614. else if(found !== undefined) {
  615. response = found;
  616. }
  617. if($.isArray(returnedValue)) {
  618. returnedValue.push(response);
  619. }
  620. else if(returnedValue !== undefined) {
  621. returnedValue = [returnedValue, response];
  622. }
  623. else if(response !== undefined) {
  624. returnedValue = response;
  625. }
  626. return found;
  627. }
  628. };
  629. if(methodInvoked) {
  630. if(instance === undefined) {
  631. module.initialize();
  632. }
  633. module.invoke(query);
  634. }
  635. else {
  636. if(instance !== undefined) {
  637. module.destroy();
  638. }
  639. module.initialize();
  640. }
  641. })
  642. ;
  643. return (returnedValue !== undefined)
  644. ? returnedValue
  645. : this
  646. ;
  647. };
  648. $.fn.search.settings = {
  649. name : 'Search Module',
  650. namespace : 'search',
  651. debug : false,
  652. verbose : true,
  653. performance : true,
  654. // api config
  655. apiSettings : false,
  656. type : 'standard',
  657. minCharacters : 1,
  658. source : false,
  659. searchFields : [
  660. 'title',
  661. 'description'
  662. ],
  663. searchFullText : true,
  664. automatic : 'true',
  665. hideDelay : 0,
  666. searchDelay : 300,
  667. maxResults : 7,
  668. cache : true,
  669. transition : 'scale',
  670. duration : 300,
  671. easing : 'easeOutExpo',
  672. // onSelect default action is defined in module
  673. onSelect : 'default',
  674. onResultsAdd : 'default',
  675. onSearchQuery : function(){},
  676. onResults : function(response){},
  677. onResultsOpen : function(){},
  678. onResultsClose : function(){},
  679. className: {
  680. active : 'active',
  681. down : 'down',
  682. focus : 'focus',
  683. empty : 'empty',
  684. loading : 'loading'
  685. },
  686. error : {
  687. source : 'No source or api action specified',
  688. noResults : 'Your search returned no results',
  689. logging : 'Error in debug logging, exiting.',
  690. noTemplate : 'A valid template name was not specified.',
  691. serverError : 'There was an issue with querying the server.',
  692. method : 'The method you called is not defined.'
  693. },
  694. selector : {
  695. prompt : '.prompt',
  696. searchButton : '.search.button',
  697. results : '.results',
  698. category : '.category',
  699. result : '.result',
  700. title : '.title, .name'
  701. },
  702. templates: {
  703. escape: function(string) {
  704. var
  705. badChars = /[&<>"'`]/g,
  706. shouldEscape = /[&<>"'`]/,
  707. escape = {
  708. "&": "&amp;",
  709. "<": "&lt;",
  710. ">": "&gt;",
  711. '"': "&quot;",
  712. "'": "&#x27;",
  713. "`": "&#x60;"
  714. },
  715. escapedChar = function(chr) {
  716. return escape[chr];
  717. }
  718. ;
  719. if(shouldEscape.test(string)) {
  720. return string.replace(badChars, escapedChar);
  721. }
  722. return string;
  723. },
  724. message: function(message, type) {
  725. var
  726. html = ''
  727. ;
  728. if(message !== undefined && type !== undefined) {
  729. html += ''
  730. + '<div class="message ' + type + '">'
  731. ;
  732. // message type
  733. if(type == 'empty') {
  734. html += ''
  735. + '<div class="header">No Results</div class="header">'
  736. + '<div class="description">' + message + '</div class="description">'
  737. ;
  738. }
  739. else {
  740. html += ' <div class="description">' + message + '</div>';
  741. }
  742. html += '</div>';
  743. }
  744. return html;
  745. },
  746. category: function(response) {
  747. var
  748. html = '',
  749. escape = $.fn.search.settings.templates.escape
  750. ;
  751. if(response.results !== undefined) {
  752. // each category
  753. $.each(response.results, function(index, category) {
  754. if(category.results !== undefined && category.results.length > 0) {
  755. html += ''
  756. + '<div class="category">'
  757. + '<div class="name">' + category.name + '</div>'
  758. ;
  759. // each item inside category
  760. $.each(category.results, function(index, result) {
  761. html += '<div class="result">';
  762. if(result.url) {
  763. html += '<a href="' + result.url + '"></a>';
  764. }
  765. if(result.image !== undefined) {
  766. result.image = escape(result.image);
  767. html += ''
  768. + '<div class="image">'
  769. + ' <img src="' + result.image + '" alt="">'
  770. + '</div>'
  771. ;
  772. }
  773. html += '<div class="content">';
  774. if(result.price !== undefined) {
  775. result.price = escape(result.price);
  776. html += '<div class="price">' + result.price + '</div>';
  777. }
  778. if(result.title !== undefined) {
  779. result.title = escape(result.title);
  780. html += '<div class="title">' + result.title + '</div>';
  781. }
  782. if(result.description !== undefined) {
  783. html += '<div class="description">' + result.description + '</div>';
  784. }
  785. html += ''
  786. + '</div>'
  787. + '</div>'
  788. ;
  789. });
  790. html += ''
  791. + '</div>'
  792. ;
  793. }
  794. });
  795. if(response.action) {
  796. html += ''
  797. + '<a href="' + response.action.url + '" class="action">'
  798. + response.action.text
  799. + '</a>';
  800. }
  801. return html;
  802. }
  803. return false;
  804. },
  805. standard: function(response) {
  806. var
  807. html = ''
  808. ;
  809. if(response.results !== undefined) {
  810. // each result
  811. $.each(response.results, function(index, result) {
  812. if(result.url) {
  813. html += '<a class="result" href="' + result.url + '">';
  814. }
  815. else {
  816. html += '<a class="result">';
  817. }
  818. if(result.image !== undefined) {
  819. html += ''
  820. + '<div class="image">'
  821. + ' <img src="' + result.image + '">'
  822. + '</div>'
  823. ;
  824. }
  825. html += '<div class="content">';
  826. if(result.price !== undefined) {
  827. html += '<div class="price">' + result.price + '</div>';
  828. }
  829. if(result.title !== undefined) {
  830. html += '<div class="title">' + result.title + '</div>';
  831. }
  832. if(result.description !== undefined) {
  833. html += '<div class="description">' + result.description + '</div>';
  834. }
  835. html += ''
  836. + '</div>'
  837. ;
  838. html += '</a>';
  839. });
  840. if(response.action) {
  841. html += ''
  842. + '<a href="' + response.action.url + '" class="action">'
  843. + response.action.text
  844. + '</a>';
  845. }
  846. return html;
  847. }
  848. return false;
  849. }
  850. }
  851. };
  852. })( jQuery, window , document );