search.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. /* eslint-disable */
  2. var SearchService = "";
  3. (function($) {
  4. /**
  5. * A super class of common logics for all search services
  6. * @param options : (object)
  7. */
  8. SearchService = function(options) {
  9. var self = this;
  10. self.config = $.extend({
  11. per_page: 10,
  12. selectors: {
  13. body: "body",
  14. form: ".u-search-form",
  15. input: ".u-search-input",
  16. container: "#u-search",
  17. modal: "#u-search .modal",
  18. modal_body: "#u-search .modal-body",
  19. modal_footer: "#u-search .modal-footer",
  20. modal_overlay: "#u-search .modal-overlay",
  21. modal_results: "#u-search .modal-results",
  22. modal_metadata: "#u-search .modal-metadata",
  23. modal_error: "#u-search .modal-error",
  24. modal_loading_bar: "#u-search .modal-loading-bar",
  25. modal_ajax_content: "#u-search .modal-ajax-content",
  26. modal_logo: '#u-search .modal-footer .logo',
  27. btn_close: "#u-search .btn-close",
  28. btn_next: "#u-search .btn-next",
  29. btn_prev: "#u-search .btn-prev"
  30. },
  31. brands: {
  32. 'google': {logo: 'google.svg', url: 'https://cse.google.com'},
  33. 'algolia': {logo: 'algolia.svg', url: 'https://www.algolia.com'},
  34. 'hexo': {logo: '', url: ''},
  35. 'azure': {logo: 'azure.svg', url: 'https://azure.microsoft.com/en-us/services/search/'},
  36. 'baidu': {logo: 'baidu.svg', url: 'http://zn.baidu.com/cse/home/index'}
  37. },
  38. imagePath: ROOT + "img/"
  39. }, options);
  40. self.dom = {};
  41. self.percentLoaded = 0;
  42. self.open = false;
  43. self.queryText = "";
  44. self.nav = {
  45. next: -1,
  46. prev: -1,
  47. total: 0,
  48. current: 1
  49. };
  50. self.parseSelectors = function() {
  51. for (var key in self.config.selectors) {
  52. self.dom[key] = $(self.config.selectors[key]);
  53. }
  54. };
  55. self.beforeQuery = function() {
  56. if (!self.open) {
  57. self.dom.container.fadeIn();
  58. self.dom.body.addClass('modal-active');
  59. }
  60. self.dom.input.each(function(index,elem) {
  61. $(elem).val(self.queryText);
  62. });
  63. document.activeElement.blur();
  64. self.dom.modal_error.hide();
  65. self.dom.modal_ajax_content.removeClass('loaded');
  66. self.startLoading();
  67. };
  68. self.afterQuery = function() {
  69. self.dom.modal_body.scrollTop(0);
  70. self.dom.modal_ajax_content.addClass('loaded');
  71. self.stopLoading();
  72. };
  73. /**
  74. * Perform a complete serach operation including UI updates and query
  75. * @param startIndex {int} start index or page number
  76. */
  77. self.search = function(startIndex, callback) {
  78. self.beforeQuery();
  79. if (self.search instanceof Function) {
  80. self.query(self.queryText, startIndex, function() {
  81. self.afterQuery();
  82. });
  83. }
  84. else {
  85. console.log("query() does not exist.");
  86. self.onQueryError(self.queryText, '');
  87. self.afterQuery();
  88. }
  89. };
  90. /**
  91. * Query error handler
  92. * @param queryText: (string)
  93. * @param status: (string)
  94. */
  95. self.onQueryError = function(queryText, status) {
  96. var errMsg = "";
  97. if (status === "success") errMsg = "No result found for \"" +queryText+ "\".";
  98. else if (status === "timeout") errMsg = "Unfortunate timeout.";
  99. else errMsg = "Mysterious failure.";
  100. self.dom.modal_results.html("");
  101. self.dom.modal_error.html(errMsg);
  102. self.dom.modal_error.show();
  103. };
  104. self.nextPage = function() {
  105. if (self.nav.next !== -1) {
  106. self.search(self.nav.next);
  107. }
  108. };
  109. self.prevPage = function() {
  110. if (self.nav.prev !== -1) {
  111. self.search(self.nav.prev);
  112. }
  113. };
  114. /**
  115. * Generate html for one result
  116. * @param url : (string) url
  117. * @param title : (string) title
  118. * @param digest : (string) digest
  119. */
  120. self.buildResult = function(url, title, digest) {
  121. var html = "";
  122. html = "<li>";
  123. html += "<a class='result' href='" +url+ "'>";
  124. html += "<span class='title'>" +title+ "</span>";
  125. html += "<span class='digest'>" +digest+ "</span>";
  126. html += "<span class='icon icon-chevron-thin-right'></span>";
  127. html += "</a>";
  128. html += "</li>";
  129. return html;
  130. };
  131. /**
  132. * Close the modal, resume body scrolling
  133. * no param
  134. */
  135. self.close = function() {
  136. self.open = false;
  137. self.dom.container.fadeOut();
  138. self.dom.body.removeClass('modal-active');
  139. };
  140. /**
  141. * Searchform submit event handler
  142. * @param queryText : (string) the query text
  143. */
  144. self.onSubmit = function(event) {
  145. event.preventDefault();
  146. self.queryText = $(this).find('.u-search-input').val();
  147. if (self.queryText) {
  148. self.search(1);
  149. }
  150. };
  151. /**
  152. * Start loading bar animation
  153. * no param
  154. */
  155. self.startLoading = function() {
  156. self.dom.modal_loading_bar.show();
  157. self.loadingTimer = setInterval(function() {
  158. self.percentLoaded = Math.min(self.percentLoaded+5,95);
  159. self.dom.modal_loading_bar.css('width', self.percentLoaded+'%');
  160. }, 100);
  161. };
  162. /**
  163. * Stop loading bar animation
  164. * no param
  165. */
  166. self.stopLoading = function() {
  167. clearInterval(self.loadingTimer);
  168. self.dom.modal_loading_bar.css('width', '100%');
  169. self.dom.modal_loading_bar.fadeOut();
  170. setTimeout(function() {
  171. self.percentLoaded = 0;
  172. self.dom.modal_loading_bar.css('width', '0%');
  173. }, 300);
  174. };
  175. /**
  176. * Add service branding
  177. * @param service {String} service name
  178. */
  179. self.addLogo = function(service) {
  180. var html = "";
  181. if (self.config.brands[service] && self.config.brands[service].logo) {
  182. html += "<a href='" +self.config.brands[service].url+ "' class='" +service+ "'>";
  183. html += '<img src="' +self.config.imagePath+self.config.brands[service].logo+ '" />';
  184. html += "</a>";
  185. self.dom.modal_logo.html(html);
  186. }
  187. };
  188. self.destroy = function() {
  189. self.dom.form.each(function(index,elem) {
  190. $(elem).off('submit');
  191. });
  192. self.dom.modal_overlay.off('click');
  193. self.dom.btn_close.off('click');
  194. self.dom.btn_next.off('click');
  195. self.dom.btn_prev.off('click');
  196. self.dom.container.remove();
  197. };
  198. /**
  199. * Load template and register event handlers
  200. * no param
  201. */
  202. self.init = function() {
  203. $('body').append(template);
  204. self.parseSelectors();
  205. self.dom.modal_footer.show();
  206. self.dom.form.each(function(index,elem) {
  207. $(elem).on('submit', self.onSubmit);
  208. });
  209. self.dom.modal_overlay.on('click', self.close);
  210. self.dom.btn_close.on('click', self.close);
  211. self.dom.btn_next.on('click', self.nextPage);
  212. self.dom.btn_prev.on('click', self.prevPage);
  213. };
  214. self.init();
  215. };
  216. var template = '<div id="u-search"><div class="modal"> <header class="modal-header" class="clearfix"><form id="u-search-modal-form" class="u-search-form" name="uSearchModalForm"> <input type="text" id="u-search-modal-input" class="u-search-input" /> <button type="submit" id="u-search-modal-btn-submit" class="u-search-btn-submit"> <span class="icon icon-search"></span> </button></form> <a class="btn-close"> <span class="icon icon-close"></span> </a><div class="modal-loading"><div class="modal-loading-bar"></div></div> </header> <main class="modal-body"><ul class="modal-results modal-ajax-content"></ul> </main> <footer class="modal-footer clearfix"><div class="modal-metadata modal-ajax-content"> <strong class="range"></strong> of <strong class="total"></strong></div><div class="modal-error"></div> <div class="logo"></div> <a class="nav btn-next modal-ajax-content"> <span class="text">NEXT</span> <span class="icon icon-chevron-right"></span> </a> <a class="nav btn-prev modal-ajax-content"> <span class="icon icon-chevron-left"></span> <span class="text">PREV</span> </a> </footer></div><div class="modal-overlay"></div></div>';
  217. })(jQuery);
  218. var AlgoliaSearch;
  219. (function($) {
  220. 'use strict';
  221. /**
  222. * Search by Algolia Search
  223. * @param options : (object)
  224. */
  225. AlgoliaSearch = function(options) {
  226. SearchService.apply(this, arguments);
  227. var self = this;
  228. var endpoint = "https://" +self.config.appId+ "-dsn.algolia.net/1/indexes/" +self.config.indexName;
  229. self.addLogo('algolia');
  230. /**
  231. * Generate result list html
  232. * @param data : (array) result items
  233. */
  234. self.buildResultList = function(data) {
  235. var html = "";
  236. $.each(data, function(index, row) {
  237. var url = row.permalink || row.path || "";
  238. if (!row.permalink && row.path) {
  239. url = ROOT + url;
  240. }
  241. var title = row.title;
  242. var digest = row._highlightResult.excerptStrip.value || "";
  243. html += self.buildResult(url, title, digest);
  244. });
  245. return html;
  246. };
  247. /**
  248. * Generate metadata after a successful query
  249. * @param data : (object) the raw search response data
  250. */
  251. self.buildMetadata = function(data) {
  252. self.nav.current = data.page * data.hitsPerPage + 1;
  253. self.nav.currentCount = data.hits.length;
  254. self.nav.total = parseInt(data.nbHits);
  255. self.dom.modal_metadata.children('.total').html(self.nav.total);
  256. self.dom.modal_metadata.children('.range').html(self.nav.current + "-" + (self.nav.current+self.nav.currentCount-1));
  257. if (self.nav.total > 0) {
  258. self.dom.modal_metadata.show();
  259. }
  260. else {
  261. self.dom.modal_metadata.hide();
  262. }
  263. if (data.page < data.nbPages-1) {
  264. self.nav.next = (data.page+1)+1;
  265. self.dom.btn_next.show();
  266. }
  267. else {
  268. self.nav.next = -1;
  269. self.dom.btn_next.hide();
  270. }
  271. if (data.page > 0) {
  272. self.nav.prev = (data.page+1)-1;
  273. self.dom.btn_prev.show();
  274. }
  275. else {
  276. self.nav.prev = -1;
  277. self.dom.btn_prev.hide();
  278. }
  279. };
  280. /**
  281. * Send a GET request
  282. * @param queryText : (string) the query text
  283. * @param page : (int) the current page (start from 1)
  284. * @param callback : (function)
  285. */
  286. self.query = function(queryText, page, callback) {
  287. $.get(endpoint, {
  288. query: queryText,
  289. page: page-1,
  290. hitsPerPage: self.config.per_page,
  291. "x-algolia-application-id": self.config.appId,
  292. "x-algolia-api-key": self.config.apiKey
  293. }, function(data, status) {
  294. if (status === 'success' && data.hits && data.hits.length > 0) {
  295. var results = self.buildResultList(data.hits);
  296. self.dom.modal_results.html(results);
  297. }
  298. else {
  299. self.onQueryError(queryText, status);
  300. }
  301. self.buildMetadata(data);
  302. if (callback) {
  303. callback(data);
  304. }
  305. });
  306. };
  307. return self;
  308. };
  309. })(jQuery);
  310. var AzureSearch;
  311. (function($) {
  312. 'use strict';
  313. /**
  314. * Search by Azure Search API
  315. * @param options : (object)
  316. */
  317. AzureSearch = function(options) {
  318. SearchService.apply(this, arguments);
  319. var self = this;
  320. var endpoint = "https://" +self.config.serviceName+ ".search.windows.net/indexes/" +self.config.indexName+ "/docs?api-version=2015-02-28";
  321. self.nav.current = 1;
  322. self.addLogo('azure');
  323. /**
  324. * Generate result list html
  325. * @param data : (array) result items
  326. */
  327. self.buildResultList = function(data) {
  328. var html = "";
  329. $.each(data, function(index, row) {
  330. var url = row.permalink || row.path || "";
  331. if (!row.permalink && row.path) {
  332. url = "/" + url;
  333. }
  334. var title = row.title;
  335. var digest = row.excerpt || "";
  336. html += self.buildResult(url, title, digest);
  337. });
  338. return html;
  339. };
  340. /**
  341. * Generate metadata after a successful query
  342. * @param data : (object) the raw response data
  343. * @param startIndex : (int) requested start index of current query
  344. */
  345. self.buildMetadata = function(data, startIndex) {
  346. self.nav.current = startIndex;
  347. self.nav.currentCount = data.value.length;
  348. self.nav.total = parseInt(data['@odata.count']);
  349. self.dom.modal_metadata.children('.total').html(self.nav.total);
  350. self.dom.modal_metadata.children('.range').html(self.nav.current + "-" + (self.nav.current+self.nav.currentCount-1));
  351. if (self.nav.total > 0) {
  352. self.dom.modal_metadata.show();
  353. }
  354. else {
  355. self.dom.modal_metadata.hide();
  356. }
  357. if (self.nav.current+self.nav.currentCount <= self.nav.total) {
  358. self.nav.next = self.nav.current+self.nav.currentCount;
  359. self.dom.btn_next.show();
  360. }
  361. else {
  362. self.nav.next = -1;
  363. self.dom.btn_next.hide();
  364. }
  365. if (self.nav.current > 1) {
  366. self.nav.prev = self.nav.current-self.config.per_page;
  367. self.dom.btn_prev.show();
  368. }
  369. else {
  370. self.nav.prev = -1;
  371. self.dom.btn_prev.hide();
  372. }
  373. };
  374. /**
  375. * Send a GET request
  376. * @param queryText : (string) the query text
  377. * @param page : (int) the current page (start from 1)
  378. * @param callback : (function)
  379. */
  380. self.query = function(queryText, startIndex, callback) {
  381. $.ajax({
  382. url: endpoint,
  383. headers: {
  384. "Accept": "application/json",
  385. "api-key": self.config.queryKey
  386. },
  387. data: {
  388. search: queryText,
  389. $orderby: "date desc",
  390. $skip: startIndex-1,
  391. $top: self.config.per_page,
  392. $count: true
  393. },
  394. type: "GET",
  395. success: function(data, status) {
  396. if (status === 'success' && data.value && data.value.length > 0) {
  397. var results = self.buildResultList(data.value);
  398. self.dom.modal_results.html(results);
  399. }
  400. else {
  401. self.onQueryError(queryText, status);
  402. }
  403. self.buildMetadata(data, startIndex);
  404. if (callback) {
  405. callback(data);
  406. }
  407. }
  408. });
  409. };
  410. return self;
  411. };
  412. })(jQuery);
  413. var BaiduSearch;
  414. (function($) {
  415. 'use strict';
  416. /**
  417. * TODO
  418. * Search by Baidu Search API
  419. * @param options : (object)
  420. */
  421. BaiduSearch = function(options) {
  422. SearchService.apply(this, arguments);
  423. var self = this;
  424. var endpoint = "";
  425. self.addLogo('baidu');
  426. /**
  427. * Generate result list html
  428. * @param data : (array) result items
  429. */
  430. self.buildResultList = function(data, queryText) {
  431. var results = [],
  432. html = "";
  433. $.each(data, function(index, post) {
  434. if (self.contentSearch(post, queryText))
  435. html += self.buildResult(post.linkUrl, post.title, post.abstract);
  436. });
  437. return html;
  438. };
  439. /**
  440. * Generate metadata after a successful query
  441. * @param data : (object) the raw google custom search response data
  442. */
  443. self.buildMetadata = function(data) {
  444. };
  445. self.loadScript = function() {
  446. self.dom.input.each(function(index,elem) {
  447. $(elem).attr('disabled', true);
  448. });
  449. var script = "<script src='http://zhannei.baidu.com/api/customsearch/apiaccept?sid=" +self.config.apiId+ "&v=2.0&callback=customSearch.initBaidu' type='text/javascript' charset='utf-8'></script>";
  450. self.dom.body.append(script);
  451. };
  452. self.initBaidu = function() {
  453. self.cse = new BCse.Search(self.config.apiId);
  454. //self.cse.setPageNum(self.config.per_page);
  455. self.dom.input.each(function(index,elem) {
  456. $(elem).attr('disabled', false);
  457. });
  458. };
  459. /**
  460. * Get search results
  461. * @param queryText {String}
  462. * @param page {Integer}
  463. * @param callback {Function}
  464. */
  465. self.query = function(queryText, page, callback) {
  466. self.cse.getResult(queryText, function(data) {
  467. console.log("Searching: " + queryText);
  468. console.log(data);
  469. self.cse.getError(function(data) {
  470. console.log(data);
  471. });
  472. if (data.length > 0) {
  473. self.buildResultList(data, queryText);
  474. self.cse.getSearchInfo(queryText, function(data) {
  475. console.log(data);
  476. self.buildMetadata(data);
  477. });
  478. }
  479. else {
  480. self.nav.total = 0;
  481. self.nav.next = -1;
  482. self.nav.prev = -1;
  483. self.dom.modal_metadata.hide();
  484. self.dom.btn_next.hide();
  485. self.dom.btn_prev.hide();
  486. self.onQueryError(queryText, "success");
  487. }
  488. if (callback instanceof Function) {
  489. callback();
  490. }
  491. });
  492. };
  493. self.loadScript();
  494. return self;
  495. };
  496. })(jQuery);
  497. var GoogleCustomSearch = "";
  498. (function($) {
  499. 'use strict';
  500. /**
  501. * Search by Google Custom Search Engine JSON API
  502. * @param options : (object)
  503. */
  504. GoogleCustomSearch = function(options) {
  505. SearchService.apply(this, arguments);
  506. var self = this;
  507. var endpoint = "https://www.googleapis.com/customsearch/v1";
  508. self.addLogo('google');
  509. /**
  510. * Generate result list html
  511. * @param data : (array) result items
  512. */
  513. self.buildResultList = function(data) {
  514. var html = "";
  515. $.each(data, function(index, row) {
  516. var url = row.link;
  517. var title = row.title;
  518. var digest = (row.htmlSnippet || "").replace('<br>','');
  519. html += self.buildResult(url, title, digest);
  520. });
  521. return html;
  522. };
  523. /**
  524. * Generate metadata after a successful query
  525. * @param data : (object) the raw google custom search response data
  526. */
  527. self.buildMetadata = function(data) {
  528. if (data.queries && data.queries.request && data.queries.request[0].totalResults !== '0') {
  529. self.nav.current = data.queries.request[0].startIndex;
  530. self.nav.currentCount = data.queries.request[0].count;
  531. self.nav.total = parseInt(data.queries.request[0].totalResults);
  532. self.dom.modal_metadata.children('.total').html(self.nav.total);
  533. self.dom.modal_metadata.children('.range').html(self.nav.current + "-" + (self.nav.current+self.nav.currentCount-1));
  534. self.dom.modal_metadata.show();
  535. }
  536. else {
  537. self.dom.modal_metadata.hide();
  538. }
  539. if (data.queries && data.queries.nextPage) {
  540. self.nav.next = data.queries.nextPage[0].startIndex;
  541. self.dom.btn_next.show();
  542. }
  543. else {
  544. self.nav.next = -1;
  545. self.dom.btn_next.hide();
  546. }
  547. if (data.queries && data.queries.previousPage) {
  548. self.nav.prev = data.queries.previousPage[0].startIndex;
  549. self.dom.btn_prev.show();
  550. }
  551. else {
  552. self.nav.prev = -1;
  553. self.dom.btn_prev.hide();
  554. }
  555. };
  556. /**
  557. * Send a GET request
  558. * @param queryText : (string) the query text
  559. * @param startIndex : (int) the index of first item (start from 1)
  560. * @param callback : (function)
  561. */
  562. self.query = function(queryText, startIndex, callback) {
  563. $.get(endpoint, {
  564. key: self.config.apiKey,
  565. cx: self.config.engineId,
  566. q: queryText,
  567. start: startIndex,
  568. num: self.config.per_page
  569. }, function(data, status) {
  570. if (status === 'success' && data.items && data.items.length > 0) {
  571. var results = self.buildResultList(data.items);
  572. self.dom.modal_results.html(results);
  573. }
  574. else {
  575. self.onQueryError(queryText, status);
  576. }
  577. self.buildMetadata(data);
  578. if (callback) {
  579. callback();
  580. }
  581. });
  582. };
  583. return self;
  584. };
  585. })(jQuery);
  586. var HexoSearch;
  587. (function($) {
  588. 'use strict';
  589. /**
  590. * Search by Hexo generator json content
  591. * @param options : (object)
  592. */
  593. HexoSearch = function(options) {
  594. SearchService.apply(this, arguments);
  595. var self = this;
  596. self.config.endpoint = ROOT + ((options||{}).endpoint || "content.json");
  597. self.config.endpoint = self.config.endpoint.replace("//","/"); //make sure the url is correct
  598. self.cache = "";
  599. /**
  600. * Search queryText in title and content of a post
  601. * Credit to: http://hahack.com/codes/local-search-engine-for-hexo/
  602. * @param post : the post object
  603. * @param queryText : the search query
  604. */
  605. self.contentSearch = function(post, queryText) {
  606. var post_title = post.title.trim().toLowerCase(),
  607. post_content = post.text.trim().toLowerCase(),
  608. keywords = queryText.trim().toLowerCase().split(" "),
  609. foundMatch = false,
  610. index_title = -1,
  611. index_content = -1,
  612. first_occur = -1;
  613. if (post_title !== '' && post_content !== '') {
  614. $.each(keywords, function(index, word) {
  615. index_title = post_title.indexOf(word);
  616. index_content = post_content.indexOf(word);
  617. if (index_title < 0 && index_content < 0) {
  618. foundMatch = false;
  619. }
  620. else {
  621. foundMatch = true;
  622. if (index_content < 0) {
  623. index_content = 0;
  624. }
  625. if (index === 0) {
  626. first_occur = index_content;
  627. }
  628. }
  629. if (foundMatch) {
  630. post_content = post.text.trim();
  631. var start = 0, end = 0;
  632. if (first_occur >= 0) {
  633. start = Math.max(first_occur-30, 0);
  634. end = (start === 0) ? Math.min(200, post_content.length) : Math.min(first_occur+170, post_content.length);
  635. var match_content = post_content.substring(start, end);
  636. keywords.forEach(function(keyword) {
  637. var regS = new RegExp(keyword, "gi");
  638. match_content = match_content.replace(regS, "<b>"+keyword+"</b>");
  639. });
  640. post.digest = match_content;
  641. }
  642. else {
  643. end = Math.min(200, post_content.length);
  644. post.digest = post_content.trim().substring(0, end);
  645. }
  646. }
  647. });
  648. }
  649. return foundMatch;
  650. };
  651. /**
  652. * Generate result list html
  653. * @param data : (array) result items
  654. */
  655. self.buildResultList = function(data, queryText) {
  656. var results = [],
  657. html = "";
  658. $.each(data, function(index, post) {
  659. if (self.contentSearch(post, queryText))
  660. html += self.buildResult(post.permalink, post.title, post.digest);
  661. });
  662. return html;
  663. };
  664. /**
  665. * Generate metadata after a successful query
  666. * @param data : (object) the raw google custom search response data
  667. */
  668. self.buildMetadata = function(data) {
  669. self.dom.modal_footer.hide();
  670. };
  671. /**
  672. * Send a GET request
  673. * @param queryText : (string) the query text
  674. * @param startIndex : (int) the index of first item (start from 1)
  675. * @param callback : (function)
  676. */
  677. self.query = function(queryText, startIndex, callback) {
  678. if (!self.cache) {
  679. $.get(self.config.endpoint, {
  680. key: self.config.apiKey,
  681. cx: self.config.engineId,
  682. q: queryText,
  683. start: startIndex,
  684. num: self.config.per_page
  685. }, function(data, status) {
  686. if (status !== 'success' ||
  687. !data ||
  688. (!data.posts && !data.pages) ||
  689. (data.posts.length < 1 && data.pages.length < 1)
  690. ) {
  691. self.onQueryError(queryText, status);
  692. }
  693. else {
  694. self.cache = data;
  695. var results = "";
  696. results += self.buildResultList(data.pages, queryText);
  697. results += self.buildResultList(data.posts, queryText);
  698. self.dom.modal_results.html(results);
  699. }
  700. self.buildMetadata(data);
  701. if (callback) {
  702. callback(data);
  703. }
  704. });
  705. }
  706. else {
  707. var results = "";
  708. results += self.buildResultList(self.cache.pages, queryText);
  709. results += self.buildResultList(self.cache.posts, queryText);
  710. self.dom.modal_results.html(results);
  711. self.buildMetadata(self.cache);
  712. if (callback) {
  713. callback(self.cache);
  714. }
  715. }
  716. };
  717. return self;
  718. };
  719. })(jQuery);