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.

1055 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
  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 from category results', value);
  294. $.each(results, function(index, category) {
  295. if(category.results !== undefined) {
  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. },
  321. buttonPressed: function() {
  322. $searchButton.addClass(className.pressed);
  323. }
  324. },
  325. remove: {
  326. loading: function() {
  327. $module.removeClass(className.loading);
  328. },
  329. focus: function() {
  330. $module.removeClass(className.focus);
  331. },
  332. buttonPressed: function() {
  333. $searchButton.removeClass(className.pressed);
  334. }
  335. },
  336. query: function() {
  337. var
  338. searchTerm = module.get.value(),
  339. cachedHTML = module.read.cachedHTML(searchTerm)
  340. ;
  341. if(cachedHTML) {
  342. module.debug('Reading result for ' + searchTerm + ' from cache');
  343. module.addResults(cachedHTML);
  344. }
  345. else {
  346. module.debug('Querying for ' + searchTerm);
  347. if($.isPlainObject(settings.source) || $.isArray(settings.source)) {
  348. module.search.local(searchTerm);
  349. }
  350. else if( module.can.useAPI() ) {
  351. if(settings.apiSettings) {
  352. module.debug('Searching with specified API settings', settings.apiSettings);
  353. module.search.remote(searchTerm);
  354. }
  355. else if($.api.settings.api.search !== undefined) {
  356. module.debug('Searching with default search API endpoint');
  357. module.search.remote(searchTerm);
  358. }
  359. }
  360. else {
  361. module.error(error.source);
  362. }
  363. settings.onSearchQuery.call(element, searchTerm);
  364. }
  365. },
  366. search: {
  367. local: function(searchTerm) {
  368. var
  369. searchResults = module.search.object(searchTerm, settings.content),
  370. searchHTML
  371. ;
  372. module.set.loading();
  373. module.save.results(searchResults);
  374. module.debug('Returned local search results', searchResults);
  375. searchHTML = module.generateResults({
  376. results: searchResults
  377. });
  378. module.remove.loading();
  379. module.write.cachedHTML(searchTerm, searchHTML);
  380. module.addResults(searchHTML);
  381. },
  382. remote: function(searchTerm) {
  383. var
  384. apiSettings = {
  385. onSuccess : function(response) {
  386. module.parse.response.call(element, response, searchTerm);
  387. },
  388. urlData: {
  389. query: searchTerm
  390. }
  391. }
  392. ;
  393. if( !$module.api('get request') ) {
  394. module.setup.api();
  395. }
  396. $.extend(true, apiSettings, settings.apiSettings);
  397. module.debug('Executing search', apiSettings);
  398. module.cancel.query();
  399. $module
  400. .api('setting', apiSettings)
  401. .api('query')
  402. ;
  403. },
  404. object: function(searchTerm, source) {
  405. var
  406. results = [],
  407. fullTextResults = [],
  408. searchFields = $.isArray(settings.searchFields)
  409. ? settings.searchFields
  410. : [settings.searchFields],
  411. searchRegExp = new RegExp(regExp.exact + searchTerm, 'i'),
  412. fullTextRegExp = new RegExp(searchTerm, 'i')
  413. ;
  414. source = source || settings.source;
  415. // exit conditions on no source
  416. if(source === undefined) {
  417. module.error(error.source);
  418. return [];
  419. }
  420. // iterate through search fields in array order
  421. $.each(searchFields, function(index, field) {
  422. $.each(source, function(label, content) {
  423. var
  424. fieldExists = (typeof content[field] == 'string'),
  425. notAlreadyResult = ($.inArray(content, results) == -1 && $.inArray(content, fullTextResults) == -1)
  426. ;
  427. if(fieldExists && notAlreadyResult) {
  428. if( content[field].match(searchRegExp) ) {
  429. results.push(content);
  430. }
  431. else if( settings.searchFullText && content[field].match(fullTextRegExp) ) {
  432. fullTextResults.push(content);
  433. }
  434. }
  435. });
  436. });
  437. return $.merge(results, fullTextResults);
  438. }
  439. },
  440. parse: {
  441. response: function(response, searchTerm) {
  442. var
  443. searchHTML = module.generateResults(response)
  444. ;
  445. module.verbose('Parsing server response', response);
  446. if(response !== undefined) {
  447. if(searchTerm) {
  448. module.write.cachedHTML(searchTerm, searchHTML);
  449. if(response.results !== undefined) {
  450. module.save.results(response.results);
  451. }
  452. }
  453. module.addResults(searchHTML);
  454. }
  455. }
  456. },
  457. throttle: function() {
  458. clearTimeout(module.timer);
  459. if(module.has.minimumCharacters()) {
  460. module.timer = setTimeout(module.query, settings.searchDelay);
  461. }
  462. else {
  463. module.hideResults();
  464. }
  465. },
  466. cancel: {
  467. query: function() {
  468. if( module.can.useAPI() ) {
  469. $module.api('abort');
  470. }
  471. }
  472. },
  473. has: {
  474. minimumCharacters: function() {
  475. var
  476. searchTerm = module.get.value(),
  477. numCharacters = searchTerm.length
  478. ;
  479. return (numCharacters >= settings.minCharacters);
  480. }
  481. },
  482. read: {
  483. cachedHTML: function(name) {
  484. var
  485. cache = $module.data(metadata.cache)
  486. ;
  487. if(settings.cache) {
  488. module.verbose('Checking cache for generated html for query', name);
  489. return (typeof cache == 'object') && (cache[name] !== undefined)
  490. ? cache[name]
  491. : false
  492. ;
  493. }
  494. return false;
  495. }
  496. },
  497. save: {
  498. results: function(results) {
  499. module.verbose('Saving current search results to metadata', results);
  500. $module.data(metadata.results, results);
  501. }
  502. },
  503. write: {
  504. cachedHTML: function(name, value) {
  505. var
  506. cache = ($module.data(metadata.cache) !== undefined)
  507. ? $module.data(metadata.cache)
  508. : {}
  509. ;
  510. if(settings.cache) {
  511. module.verbose('Writing generated html to cache', name, value);
  512. cache[name] = value;
  513. $module
  514. .data(metadata.cache, cache)
  515. ;
  516. }
  517. }
  518. },
  519. addResults: function(html) {
  520. if( $.isFunction(settings.onResultsAdd) ) {
  521. if( settings.onResultsAdd.call($results, html) === false ) {
  522. module.debug('onResultsAdd callback cancelled default action');
  523. return false;
  524. }
  525. }
  526. $results
  527. .html(html)
  528. ;
  529. module.showResults();
  530. },
  531. showResults: function() {
  532. if( !module.is.visible() && module.is.focused() && !module.is.empty() ) {
  533. if( module.can.transition() ) {
  534. module.debug('Showing results with css animations');
  535. $results
  536. .transition({
  537. animation : settings.transition + ' in',
  538. duration : settings.duration,
  539. queue : true
  540. })
  541. ;
  542. }
  543. else {
  544. module.debug('Showing results with javascript');
  545. $results
  546. .stop()
  547. .fadeIn(settings.duration, settings.easing)
  548. ;
  549. }
  550. settings.onResultsOpen.call($results);
  551. }
  552. },
  553. hideResults: function() {
  554. if( module.is.visible() ) {
  555. if( module.can.transition() ) {
  556. module.debug('Hiding results with css animations');
  557. $results
  558. .transition({
  559. animation : settings.transition + ' out',
  560. duration : settings.duration,
  561. queue : true
  562. })
  563. ;
  564. }
  565. else {
  566. module.debug('Hiding results with javascript');
  567. $results
  568. .stop()
  569. .fadeOut(settings.duration, settings.easing)
  570. ;
  571. }
  572. settings.onResultsClose.call($results);
  573. }
  574. },
  575. generateResults: function(response) {
  576. module.debug('Generating html from response', response);
  577. var
  578. template = settings.templates[settings.type],
  579. isProperObject = ($.isPlainObject(response.results) && !$.isEmptyObject(response.results)),
  580. isProperArray = ($.isArray(response.results) && response.results.length > 0),
  581. html = ''
  582. ;
  583. if(isProperObject || isProperArray ) {
  584. if(settings.maxResults > 0) {
  585. if(isProperObject) {
  586. module.error(error.maxResults);
  587. }
  588. else {
  589. response.results = response.results.slice(0, settings.maxResults);
  590. }
  591. }
  592. if($.isFunction(template)) {
  593. html = template(response);
  594. }
  595. else {
  596. module.error(error.noTemplate, false);
  597. }
  598. }
  599. else {
  600. html = module.displayMessage(error.noResults, 'empty');
  601. }
  602. settings.onResults.call(element, response);
  603. return html;
  604. },
  605. displayMessage: function(text, type) {
  606. type = type || 'standard';
  607. module.debug('Displaying message', text, type);
  608. module.addResults( settings.templates.message(text, type) );
  609. return settings.templates.message(text, type);
  610. },
  611. setting: function(name, value) {
  612. if( $.isPlainObject(name) ) {
  613. $.extend(true, settings, name);
  614. }
  615. else if(value !== undefined) {
  616. settings[name] = value;
  617. }
  618. else {
  619. return settings[name];
  620. }
  621. },
  622. internal: function(name, value) {
  623. if( $.isPlainObject(name) ) {
  624. $.extend(true, module, name);
  625. }
  626. else if(value !== undefined) {
  627. module[name] = value;
  628. }
  629. else {
  630. return module[name];
  631. }
  632. },
  633. debug: function() {
  634. if(settings.debug) {
  635. if(settings.performance) {
  636. module.performance.log(arguments);
  637. }
  638. else {
  639. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  640. module.debug.apply(console, arguments);
  641. }
  642. }
  643. },
  644. verbose: function() {
  645. if(settings.verbose && settings.debug) {
  646. if(settings.performance) {
  647. module.performance.log(arguments);
  648. }
  649. else {
  650. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  651. module.verbose.apply(console, arguments);
  652. }
  653. }
  654. },
  655. error: function() {
  656. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  657. module.error.apply(console, arguments);
  658. },
  659. performance: {
  660. log: function(message) {
  661. var
  662. currentTime,
  663. executionTime,
  664. previousTime
  665. ;
  666. if(settings.performance) {
  667. currentTime = new Date().getTime();
  668. previousTime = time || currentTime;
  669. executionTime = currentTime - previousTime;
  670. time = currentTime;
  671. performance.push({
  672. 'Name' : message[0],
  673. 'Arguments' : [].slice.call(message, 1) || '',
  674. 'Element' : element,
  675. 'Execution Time' : executionTime
  676. });
  677. }
  678. clearTimeout(module.performance.timer);
  679. module.performance.timer = setTimeout(module.performance.display, 100);
  680. },
  681. display: function() {
  682. var
  683. title = settings.name + ':',
  684. totalTime = 0
  685. ;
  686. time = false;
  687. clearTimeout(module.performance.timer);
  688. $.each(performance, function(index, data) {
  689. totalTime += data['Execution Time'];
  690. });
  691. title += ' ' + totalTime + 'ms';
  692. if(moduleSelector) {
  693. title += ' \'' + moduleSelector + '\'';
  694. }
  695. if($allModules.length > 1) {
  696. title += ' ' + '(' + $allModules.length + ')';
  697. }
  698. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  699. console.groupCollapsed(title);
  700. if(console.table) {
  701. console.table(performance);
  702. }
  703. else {
  704. $.each(performance, function(index, data) {
  705. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  706. });
  707. }
  708. console.groupEnd();
  709. }
  710. performance = [];
  711. }
  712. },
  713. invoke: function(query, passedArguments, context) {
  714. var
  715. object = instance,
  716. maxDepth,
  717. found,
  718. response
  719. ;
  720. passedArguments = passedArguments || queryArguments;
  721. context = element || context;
  722. if(typeof query == 'string' && object !== undefined) {
  723. query = query.split(/[\. ]/);
  724. maxDepth = query.length - 1;
  725. $.each(query, function(depth, value) {
  726. var camelCaseValue = (depth != maxDepth)
  727. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  728. : query
  729. ;
  730. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  731. object = object[camelCaseValue];
  732. }
  733. else if( object[camelCaseValue] !== undefined ) {
  734. found = object[camelCaseValue];
  735. return false;
  736. }
  737. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  738. object = object[value];
  739. }
  740. else if( object[value] !== undefined ) {
  741. found = object[value];
  742. return false;
  743. }
  744. else {
  745. return false;
  746. }
  747. });
  748. }
  749. if ( $.isFunction( found ) ) {
  750. response = found.apply(context, passedArguments);
  751. }
  752. else if(found !== undefined) {
  753. response = found;
  754. }
  755. if($.isArray(returnedValue)) {
  756. returnedValue.push(response);
  757. }
  758. else if(returnedValue !== undefined) {
  759. returnedValue = [returnedValue, response];
  760. }
  761. else if(response !== undefined) {
  762. returnedValue = response;
  763. }
  764. return found;
  765. }
  766. };
  767. if(methodInvoked) {
  768. if(instance === undefined) {
  769. module.initialize();
  770. }
  771. module.invoke(query);
  772. }
  773. else {
  774. if(instance !== undefined) {
  775. module.destroy();
  776. }
  777. module.initialize();
  778. }
  779. })
  780. ;
  781. return (returnedValue !== undefined)
  782. ? returnedValue
  783. : this
  784. ;
  785. };
  786. $.fn.search.settings = {
  787. name : 'Search Module',
  788. namespace : 'search',
  789. debug : false,
  790. verbose : true,
  791. performance : true,
  792. type : 'standard',
  793. minCharacters : 1,
  794. // api config
  795. apiSettings : false,
  796. source : false,
  797. searchFields : [
  798. 'title',
  799. 'description'
  800. ],
  801. searchFullText : true,
  802. automatic : 'true',
  803. hideDelay : 0,
  804. searchDelay : 100,
  805. maxResults : 7,
  806. cache : true,
  807. transition : 'scale',
  808. duration : 300,
  809. easing : 'easeOutExpo',
  810. onSelect : false,
  811. onResultsAdd : false,
  812. onSearchQuery : function(){},
  813. onResults : function(response){},
  814. onResultsOpen : function(){},
  815. onResultsClose : function(){},
  816. className: {
  817. active : 'active',
  818. empty : 'empty',
  819. focus : 'focus',
  820. loading : 'loading',
  821. pressed : 'down'
  822. },
  823. error : {
  824. source : 'Cannot search. No source used, and Semantic API module was not included',
  825. noResults : 'Your search returned no results',
  826. logging : 'Error in debug logging, exiting.',
  827. noTemplate : 'A valid template name was not specified.',
  828. serverError : 'There was an issue with querying the server.',
  829. maxResults : 'Results must be an array to use maxResults setting',
  830. method : 'The method you called is not defined.'
  831. },
  832. metadata: {
  833. cache : 'cache',
  834. results : 'results'
  835. },
  836. regExp: {
  837. exact: '(?:\s|^)'
  838. },
  839. selector : {
  840. prompt : '.prompt',
  841. searchButton : '.search.button',
  842. results : '.results',
  843. category : '.category',
  844. result : '.result',
  845. title : '.title, .name'
  846. },
  847. templates: {
  848. escape: function(string) {
  849. var
  850. badChars = /[&<>"'`]/g,
  851. shouldEscape = /[&<>"'`]/,
  852. escape = {
  853. "&": "&amp;",
  854. "<": "&lt;",
  855. ">": "&gt;",
  856. '"': "&quot;",
  857. "'": "&#x27;",
  858. "`": "&#x60;"
  859. },
  860. escapedChar = function(chr) {
  861. return escape[chr];
  862. }
  863. ;
  864. if(shouldEscape.test(string)) {
  865. return string.replace(badChars, escapedChar);
  866. }
  867. return string;
  868. },
  869. message: function(message, type) {
  870. var
  871. html = ''
  872. ;
  873. if(message !== undefined && type !== undefined) {
  874. html += ''
  875. + '<div class="message ' + type + '">'
  876. ;
  877. // message type
  878. if(type == 'empty') {
  879. html += ''
  880. + '<div class="header">No Results</div class="header">'
  881. + '<div class="description">' + message + '</div class="description">'
  882. ;
  883. }
  884. else {
  885. html += ' <div class="description">' + message + '</div>';
  886. }
  887. html += '</div>';
  888. }
  889. return html;
  890. },
  891. category: function(response) {
  892. var
  893. html = '',
  894. escape = $.fn.search.settings.templates.escape
  895. ;
  896. if(response.results !== undefined) {
  897. // each category
  898. $.each(response.results, function(index, category) {
  899. if(category.results !== undefined && category.results.length > 0) {
  900. html += ''
  901. + '<div class="category">'
  902. + '<div class="name">' + category.name + '</div>'
  903. ;
  904. // each item inside category
  905. $.each(category.results, function(index, result) {
  906. html += '<div class="result">';
  907. if(result.url) {
  908. html += '<a href="' + result.url + '"></a>';
  909. }
  910. if(result.image !== undefined) {
  911. result.image = escape(result.image);
  912. html += ''
  913. + '<div class="image">'
  914. + ' <img src="' + result.image + '" alt="">'
  915. + '</div>'
  916. ;
  917. }
  918. html += '<div class="content">';
  919. if(result.price !== undefined) {
  920. result.price = escape(result.price);
  921. html += '<div class="price">' + result.price + '</div>';
  922. }
  923. if(result.title !== undefined) {
  924. result.title = escape(result.title);
  925. html += '<div class="title">' + result.title + '</div>';
  926. }
  927. if(result.description !== undefined) {
  928. html += '<div class="description">' + result.description + '</div>';
  929. }
  930. html += ''
  931. + '</div>'
  932. + '</div>'
  933. ;
  934. });
  935. html += ''
  936. + '</div>'
  937. ;
  938. }
  939. });
  940. if(response.action) {
  941. html += ''
  942. + '<a href="' + response.action.url + '" class="action">'
  943. + response.action.text
  944. + '</a>';
  945. }
  946. return html;
  947. }
  948. return false;
  949. },
  950. standard: function(response) {
  951. var
  952. html = ''
  953. ;
  954. if(response.results !== undefined) {
  955. // each result
  956. $.each(response.results, function(index, result) {
  957. if(result.url) {
  958. html += '<a class="result" href="' + result.url + '">';
  959. }
  960. else {
  961. html += '<a class="result">';
  962. }
  963. if(result.image !== undefined) {
  964. html += ''
  965. + '<div class="image">'
  966. + ' <img src="' + result.image + '">'
  967. + '</div>'
  968. ;
  969. }
  970. html += '<div class="content">';
  971. if(result.price !== undefined) {
  972. html += '<div class="price">' + result.price + '</div>';
  973. }
  974. if(result.title !== undefined) {
  975. html += '<div class="title">' + result.title + '</div>';
  976. }
  977. if(result.description !== undefined) {
  978. html += '<div class="description">' + result.description + '</div>';
  979. }
  980. html += ''
  981. + '</div>'
  982. ;
  983. html += '</a>';
  984. });
  985. if(response.action) {
  986. html += ''
  987. + '<a href="' + response.action.url + '" class="action">'
  988. + response.action.text
  989. + '</a>';
  990. }
  991. return html;
  992. }
  993. return false;
  994. }
  995. }
  996. };
  997. })( jQuery, window , document );