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.

1096 lines
34 KiB

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