search_main.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. jQuery.fn.highlight = function (pat) {
  2. function innerHighlight(node, pat) {
  3. var skip = 0;
  4. if (node.nodeType == 3) {
  5. var pos = node.data.toUpperCase().indexOf(pat);
  6. if (pos >= 0) {
  7. var spannode = document.createElement('span');
  8. spannode.className = 'search_highlight';
  9. var middlebit = node.splitText(pos);
  10. var endbit = middlebit.splitText(pat.length);
  11. var middleclone = middlebit.cloneNode(true);
  12. spannode.appendChild(middleclone);
  13. middlebit.parentNode.replaceChild(spannode, middlebit);
  14. skip = 1;
  15. }
  16. }
  17. else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
  18. for (var i = 0; i < node.childNodes.length; ++i) {
  19. i += innerHighlight(node.childNodes[i], pat);
  20. }
  21. }
  22. return skip;
  23. }
  24. return this.each(function () {
  25. innerHighlight(this, pat.toUpperCase());
  26. });
  27. };
  28. window.onload = function(){
  29. }
  30. $(document).ready(function(){
  31. var waiting_search = false;
  32. var search_index = null;
  33. var search_content = {
  34. "curr": null,
  35. "others":{}
  36. }
  37. function onDownloadOk(data, arg1, arg2){
  38. search_index = data;
  39. var pathname = window.location.pathname;
  40. var curr_url = null;
  41. var others_url = [];
  42. for(var url in search_index){
  43. if(pathname.indexOf(url) != -1){
  44. if(!curr_url){
  45. curr_url = url;
  46. }else{ // already have math item, e.g. `/get_started/zh/install/index.html /get_started/zh`
  47. // and now `/get_started/zh/install/index.html /`
  48. // choose longger one
  49. if(url.length > curr_url.length){
  50. others_url.push(curr_url);
  51. curr_url = url;
  52. }else{
  53. others_url.push(url);
  54. }
  55. }
  56. }else{
  57. others_url.push(url);
  58. }
  59. }
  60. if(search_index[curr_url]){
  61. downloadJson(search_index[curr_url][1], onIndexDownloadOk, curr_url, true, search_index[curr_url][0]);
  62. }
  63. for(var i in others_url){
  64. url = others_url[i];
  65. downloadJson(search_index[url][1], onIndexDownloadOk, url, false, search_index[url][0]);
  66. }
  67. }
  68. function onIndexDownloadOk(data, url, is_curr, doc_name){
  69. if(is_curr){
  70. search_content["curr"] = [url, doc_name, data];
  71. }else{
  72. search_content["others"][url] = [url, doc_name, data];
  73. }
  74. if(waiting_search == true){
  75. waiting_search = false;
  76. onSearch();
  77. }
  78. }
  79. downloadJson("/static/search_index/index.json", onDownloadOk);
  80. var input_hint = $("#search_input_hint").html();
  81. var loading_hint = $("#search_loading_hint").html();
  82. var download_err_hint = $("#search_download_err_hint").html();
  83. var other_docs_result_hint = $("#search_other_docs_result_hint").html();
  84. var curr_doc_result_hint = $("#search_curr_doc_result_hint").html();
  85. $("body").append('<div id="search_wrapper">\
  86. <div>\
  87. <div id="search_content">\
  88. <div id="search_title">\
  89. <div>\
  90. <input id="search_input" placeholder="'+ input_hint +'"/>\
  91. </div>\
  92. </div>\
  93. <div id="search_result">\
  94. <div id="search_result_name"></div>\
  95. <div id="search_result_content"></div>\
  96. </div>\
  97. </div>\
  98. </div><a class="close"></a></div>');
  99. $("#search").bind("click", function(e){
  100. $("body").css("overflow-y", "hidden");
  101. $("#search_wrapper").show();
  102. $("#search_input").focus();
  103. $("#wrapper").addClass("blur");
  104. $("#navbar").addClass("blur");
  105. });
  106. $("#search_wrapper .close").bind("click", function(e){
  107. $("body").css("overflow-y", "auto");
  108. $("#search_wrapper").hide();
  109. $("#wrapper").removeClass("blur");
  110. $("#navbar").removeClass("blur");
  111. });
  112. $("#search_input").bind("input propertychange", function(){
  113. setTimeout(() => {
  114. onSearch();
  115. }, 1000);
  116. });
  117. function onSearch(){
  118. $("#search_result_name").empty();
  119. $("#search_result_content").empty();
  120. $("#search_result_content").append('<ul id="search_curr_result"><div class="hint">'+ curr_doc_result_hint +'</div></ul>');
  121. $("#search_result_content").append('<ul id="search_others_result"><div class="hint">'+ other_docs_result_hint +'</div></ul>');
  122. if(!search_index){
  123. $("#search_result_content").append('<div class="search_loading_hint">'+ loading_hint +'</div>');
  124. waiting_search = true;
  125. return;
  126. }
  127. if(!search_content["curr"] && search_content["others"].length == 0){
  128. $("#search_result_content").append('<div class="search_loading_hint">'+ loading_hint +'</div>');
  129. waiting_search = true;
  130. return;
  131. }
  132. $("#search_curr_result > .hint").addClass("searching");
  133. var search_keywords = $("#search_input").val();
  134. search_doc(search_content["curr"], "#search_curr_result");
  135. var doc_id = 0;
  136. for(var url in search_content["others"]){
  137. search_doc(search_content["others"][url], "#search_others_result", doc_id);
  138. doc_id += 1;
  139. }
  140. addSearchResultClickListener();
  141. function search_doc(data, containerId, doc_id="curr"){
  142. var doc_id_str = 'result_wrapper_' + doc_id;
  143. var findFlag = false;
  144. var items = data[2];
  145. for(var url in items){
  146. var content = items[url];
  147. search_keywords = search_keywords.trim();
  148. if(search_keywords.length <= 0){
  149. return;
  150. }
  151. var keywords = search_keywords.split(" ");
  152. var find = false;
  153. var find_strs = "";
  154. for(var i in keywords){
  155. var keyword = keywords[i];
  156. if(content["title"] && content["title"].indexOf(keyword) >= 0){
  157. find = true;
  158. }
  159. }
  160. if(content["content"] && content["content"].length > 0){
  161. find_strs = search(keywords, content["content"]);
  162. if(find_strs.length > 0){
  163. find = true;
  164. }
  165. }
  166. if(find){
  167. if(!findFlag){
  168. $("#search_result_name").append('<li result_id="'+ doc_id_str +'" class="pointer">'+ data[1] +'</li>');
  169. $(containerId).append('<div id="'+ doc_id_str + '"><div class="hint">'+data[1]+'</div></div>');
  170. findFlag = true;
  171. }
  172. $("#"+doc_id_str).append('<li><a href="'+ url + '?highlight=' + search_keywords + '"><h1>'+ (content["title"]?content["title"]:url) +
  173. '</h1><div>' + find_strs + '</div></a></li>');
  174. }
  175. }
  176. }
  177. $("#search_curr_result > .hint").removeClass("searching");
  178. }
  179. function downloadJson(url, callback, arg1=null, arg2=null, arg3=null){
  180. $.ajax({
  181. type: "GET",
  182. url: url,
  183. contentType: "application/json",
  184. dataType: "json",
  185. success: function(data){
  186. callback(data, arg1, arg2, arg3);
  187. },
  188. error: function(){
  189. $("#search_result_content").empty();
  190. $("#search_result_content").append('<div class="search_download_err_hint">'+ download_err_hint + ': '+ url +'</div>');
  191. }
  192. });
  193. }
  194. highlightKeywords();
  195. });
  196. function focusItems(id, contrainerId, offset=0, classname=null){
  197. var elementTop = 0;
  198. if(classname){
  199. elementTop = $("."+classname)[0].offsetTop - offset;
  200. }else{
  201. elementTop = $("#"+id)[0].offsetTop - offset;
  202. }
  203. $("#"+contrainerId).animate({scrollTop: elementTop},500);
  204. }
  205. function addSearchResultClickListener(){
  206. $("#search_result_name > li").on("click", function(e){
  207. var targetId = e.target.attributes.result_id.value;
  208. focusItems(targetId, "search_result_content", $("#search_title").height() + $("#search_result .hint").height());
  209. });
  210. }
  211. function highlightKeywords(){
  212. var highlight_keywords = getQueryVariable("highlight");
  213. if(highlight_keywords){
  214. // add search result btn
  215. var html = document.getElementsByTagName("html")[0];
  216. var lang = html.lang.split("-")[0].toLowerCase()
  217. let strs = {
  218. "zh": {
  219. "Previous": "上一个",
  220. "Next": "下一个"
  221. }
  222. }
  223. if(lang in strs){
  224. var pre_name = strs[lang]["Previous"];
  225. var next_name = strs[lang]["Next"];
  226. }else{
  227. var pre_name = "Previous";
  228. var next_name = "Next";
  229. }
  230. $("body").append('<div id="search_ctrl_btn">' +
  231. '<div class="previous"><span class="icon"></span><span>'+ pre_name +'</span></div>' +
  232. '<div id="remove_search"><span class="icon"></span></div>' +
  233. '<div class="next"><span>' + next_name +'</span><span class="icon"></span></div>' +
  234. '</div>');
  235. var highlight_keywords = decodeURI(highlight_keywords);
  236. highlight_keywords = highlight_keywords.split(" ");
  237. for(var i=0; i<highlight_keywords.length; ++i){
  238. console.log(highlight_keywords[i]);
  239. $('#content_body').highlight(highlight_keywords[i]);
  240. }
  241. if($(".search_highlight").length <= 0){
  242. return;
  243. }
  244. window.scrollTo({
  245. top: $(".search_highlight")[0].offsetTop - window.screen.height / 3,
  246. behavior: "smooth"
  247. });
  248. $($(".search_highlight")[0]).addClass("selected_highlight")
  249. $("#remove_search").on("click", function(){
  250. $('.search_highlight').removeClass("search_highlight");
  251. $('.selected_highlight').removeClass("selected_highlight");
  252. $("#search_ctrl_btn").hide();
  253. });
  254. var currSearchIdx = 0
  255. $("#search_ctrl_btn > .previous").on("click", function(){
  256. let old = currSearchIdx;
  257. currSearchIdx -= 1;
  258. if (currSearchIdx < 0){
  259. currSearchIdx = $(".search_highlight").length - 1;
  260. }
  261. window.scrollTo({
  262. top: $(".search_highlight")[currSearchIdx].offsetTop - window.screen.height / 3,
  263. behavior: "smooth"
  264. });
  265. $($(".search_highlight")[old]).removeClass("selected_highlight")
  266. $($(".search_highlight")[currSearchIdx]).addClass("selected_highlight")
  267. });
  268. $("#search_ctrl_btn > .next").on("click", function(){
  269. let old = currSearchIdx;
  270. currSearchIdx += 1;
  271. if (currSearchIdx >= $(".search_highlight").length){
  272. currSearchIdx = 0;
  273. }
  274. window.scrollTo({
  275. top: $(".search_highlight")[currSearchIdx].offsetTop - window.screen.height / 3,
  276. behavior: "smooth"
  277. });
  278. $($(".search_highlight")[old]).removeClass("selected_highlight")
  279. $($(".search_highlight")[currSearchIdx]).addClass("selected_highlight")
  280. });
  281. }
  282. }
  283. function getQueryVariable(variable)
  284. {
  285. var query = window.location.search.substring(1);
  286. var vars = query.split("&");
  287. for (var i=0;i<vars.length;i++) {
  288. var pair = vars[i].split("=");
  289. if(pair[0] == variable){return pair[1];}
  290. }
  291. return(false);
  292. }
  293. function search(keywords, content, show_length = 15){
  294. if(keywords.length <= 0){
  295. return "";
  296. }
  297. function _search(keywords, content, idx_rel = 0){
  298. var idxs = [];
  299. for(var i in keywords){
  300. var keyword = keywords[i];
  301. var idx = content.indexOf(keyword);
  302. if(idx >= 0){
  303. idxs.push({
  304. "idx": idx + idx_rel,
  305. "len": keyword.length
  306. });
  307. _idxs = _search([keyword], content.substr(idx + keyword.length), idx_rel + idx + keyword.length);
  308. idxs = idxs.concat(_idxs);
  309. }
  310. }
  311. return idxs
  312. }
  313. var find_strs = "";
  314. idxs = _search(keywords, content);
  315. idxs = idxs.sort((a, b)=> a.idx-b.idx);
  316. var idx_last = -1;
  317. var len_last = 0;
  318. for(var i=0; i<idxs.length; ++i){
  319. var idx = idxs[i]['idx'];
  320. var len = idxs[i]['len'];
  321. if(idx_last >= 0 && (idx - idx_last -len_last) < show_length){ // last keyword too close
  322. find_strs += content.substr(idx_last + len_last, idx - (idx_last + len_last)) + '<code class="search_highlight">'+ content.substr(idx, len) +'</code>'
  323. }else{
  324. var start_idx = (idx - show_length < 0) ? 0 : (idx - show_length);
  325. find_strs += '...' + content.substr(start_idx, idx - start_idx) +
  326. '<code class="search_highlight">' + content.substr(idx, len) +
  327. '</code>';
  328. }
  329. var idx_next = -1;
  330. if(i < idxs.length -1){
  331. idx_next = idxs[i + 1]['idx'];
  332. }
  333. if(idx_next >= 0 && ((idx_next - idx - len) < show_length) ){ // next keywor too close
  334. }else{
  335. find_strs += content.substr(idx + len, show_length) + '...';
  336. }
  337. idx_last = idx;
  338. len_last = len;
  339. }
  340. return find_strs
  341. }