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.

1134 lines
35 KiB

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