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.

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