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.

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