scripts.html 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. {{ $enquire := resources.Get "js/enquire.min.js" | resources.Fingerprint }}
  2. <script src="{{ $enquire.RelPermalink }}"></script>
  3. {{ $fuse := resources.Get "js/fuse.min.js" | resources.Fingerprint }}
  4. <script defer src="{{ $fuse.RelPermalink }}"></script>
  5. {{ $lazysizes := resources.Get "js/lazysizes.min.js" | resources.Fingerprint }}
  6. <script defer src="{{ $lazysizes.RelPermalink }}"></script>
  7. {{ $getParents := resources.Get "js/helper/getParents.js" | resources.Minify | resources.Fingerprint }}
  8. <script defer src="{{ $getParents.RelPermalink }}"></script>
  9. {{ $fadeinout := resources.Get "js/helper/fadeinout.js" | resources.Minify | resources.Fingerprint }}
  10. <script defer src="{{ $fadeinout.RelPermalink }}"></script>
  11. <script>
  12. "use strict";
  13. {{ $permalink := .Permalink }}
  14. var permalink = JSON.parse({{ $permalink | jsonify }});
  15. window.onload = function() {
  16. // ========================== expand ==========================
  17. var expandBtn = document.querySelectorAll('.expand__button');
  18. for (let i = 0; i < expandBtn.length; i++) {
  19. expandBtn[i].addEventListener("click", function () {
  20. var content = this.nextElementSibling;
  21. if (content.style.maxHeight) {
  22. content.style.maxHeight = null;
  23. this.querySelector('svg').classList.add('expand-icon__right');
  24. this.querySelector('svg').classList.remove('expand-icon__down');
  25. } else {
  26. content.style.maxHeight = content.scrollHeight + "px";
  27. this.querySelector('svg').classList.remove('expand-icon__right');
  28. this.querySelector('svg').classList.add('expand-icon__down');
  29. }
  30. });
  31. }
  32. // ============================================================
  33. // ======================= toggle theme =======================
  34. var root = document.getElementById('root');
  35. var toggleToLightBtn = document.getElementById('toggleToLight');
  36. var toggleToDarkBtn = document.getElementById('toggleToDark');
  37. toggleToDark.onclick = function(e) {
  38. root.className = 'theme__dark';
  39. localStorage.setItem('theme', 'dark');
  40. toggleToLightBtn.className = 'navbar__icons--icon';
  41. toggleToDarkBtn.className = 'hide';
  42. }
  43. toggleToLight.onclick = function (e) {
  44. root.className = 'theme__light';
  45. localStorage.setItem('theme', 'light');
  46. toggleToLightBtn.className = 'hide';
  47. toggleToDarkBtn.className = 'navbar__icons--icon';
  48. }
  49. // =================== section menu collapse ==================
  50. document.querySelectorAll('.menu__list').forEach(function(elem) {
  51. if (elem.classList.contains('active')) {
  52. elem.style.maxHeight = elem.scrollHeight + "px";
  53. }
  54. });
  55. document.querySelectorAll('.menu__title--collapse').forEach(function(elem) {
  56. elem.addEventListener('click', function (e) {
  57. var content = this.nextElementSibling;
  58. var menuTitleIcon = this.querySelector('.menu__title--icon');
  59. if (!content) {
  60. return null;
  61. }
  62. if (content.style.maxHeight) {
  63. content.style.maxHeight = null;
  64. content.classList.remove('active');
  65. menuTitleIcon.classList.add('right');
  66. menuTitleIcon.classList.remove('down');
  67. } else {
  68. content.style.maxHeight = content.scrollHeight + "px";
  69. content.classList.add('active');
  70. menuTitleIcon.classList.remove('right');
  71. menuTitleIcon.classList.add('down');
  72. }
  73. });
  74. });
  75. // ============================================================
  76. // ========================== drawer ==========================
  77. var mobileLogo = document.getElementById('mobileLogo');
  78. var modal = document.getElementById("myModal");
  79. var drawer = document.getElementById('myDrawer');
  80. var drawerCloseBtn = document.querySelector('.drawer__close');
  81. var openDrawer = function() {
  82. modal.style.left = 0;
  83. modal.style.opacity = 1;
  84. drawer.style.left = 0;
  85. drawer.style.transition = "left 0.3s ease-out, opacity 0.2s ease-out";
  86. drawer.style.webkitTransition = "left 0.3s ease-out, opacity 0.2s ease-out";
  87. }
  88. var closeDrawer = function() {
  89. modal.style.opacity = 0;
  90. drawer.style.left = '-100%';
  91. setTimeout(function () {
  92. modal.style.left = '-100%';
  93. }, 250);
  94. }
  95. mobileLogo.onclick = function () {
  96. openDrawer();
  97. localStorage.setItem('isDrawerOpen', 'true');
  98. }
  99. modal.onclick = function () {
  100. closeDrawer();
  101. localStorage.setItem('isDrawerOpen', 'false');
  102. }
  103. drawerCloseBtn.onclick = function () {
  104. closeDrawer();
  105. localStorage.setItem('isDrawerOpen', 'false');
  106. }
  107. // ==============================================================
  108. // =========================== scroll ===========================
  109. var lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;
  110. var tocElem = document.querySelector('.toc');
  111. var tableOfContentsElem = tocElem ? tocElem.querySelector('#TableOfContents') : null;
  112. var singleContentsElem = document.querySelector('.single__contents');
  113. window.onscroll = function () {
  114. var st = window.pageYOffset || document.documentElement.scrollTop;
  115. if (st > lastScrollTop) { // scroll down
  116. singleContentsElem ?
  117. singleContentsElem.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(function(elem) {
  118. if (document.documentElement.scrollTop >= elem.offsetTop) {
  119. if (tableOfContentsElem) {
  120. var id = elem.getAttribute('id');
  121. tocElem.querySelectorAll('a').forEach(function (elem) {
  122. elem.classList.remove('active');
  123. });
  124. tocElem.querySelector('a[href="#' + id + '"]') ?
  125. tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;
  126. }
  127. }
  128. }) : null;
  129. } else { // scroll up
  130. singleContentsElem ?
  131. singleContentsElem.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(function(elem) {
  132. if (document.documentElement.scrollTop >= elem.offsetTop) {
  133. if (tableOfContentsElem) {
  134. var id = elem.getAttribute('id');
  135. tocElem.querySelectorAll('a').forEach(function (elem) {
  136. elem.classList.remove('active');
  137. });
  138. tocElem.querySelector('a[href="#' + id + '"]') ?
  139. tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;
  140. }
  141. }
  142. }) : null;
  143. }
  144. lastScrollTop = st <= 0 ? 0 : st;
  145. };
  146. // ============================================================
  147. // ====================== mobile search =======================
  148. var mobileSearchInputElem = document.querySelector('#search-mobile');
  149. var mobileSearchClassElem = document.querySelector('.mobile-search');
  150. var mobileSearchBtnElem = document.querySelector('#mobileSearchBtn');
  151. var mobileSearchCloseBtnElem = document.querySelector('#search-mobile-close');
  152. var mobileSearchContainer = document.querySelector('#search-mobile-container');
  153. var mobileSearchResultsElem = document.querySelector('#search-mobile-results');
  154. var htmlElem = document.querySelector('html');
  155. if (mobileSearchClassElem) {
  156. mobileSearchClassElem.style.display = 'none';
  157. }
  158. mobileSearchBtnElem ?
  159. mobileSearchBtnElem.addEventListener('click', function () {
  160. if (mobileSearchContainer) {
  161. mobileSearchContainer.style.display = 'block';
  162. }
  163. if (mobileSearchInputElem) {
  164. mobileSearchInputElem.focus();
  165. }
  166. if (htmlElem) {
  167. htmlElem.style.overflowY = 'hidden';
  168. }
  169. }) : null;
  170. mobileSearchCloseBtnElem ?
  171. mobileSearchCloseBtnElem.addEventListener('click', function() {
  172. if (mobileSearchContainer) {
  173. mobileSearchContainer.style.display = 'none';
  174. }
  175. if (mobileSearchInputElem) {
  176. mobileSearchInputElem.value = '';
  177. }
  178. if (mobileSearchResultsElem) {
  179. while (mobileSearchResultsElem.firstChild) {
  180. mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
  181. }
  182. }
  183. if (htmlElem) {
  184. htmlElem.style.overflowY = 'visible';
  185. }
  186. }) : null;
  187. mobileSearchInputElem ?
  188. mobileSearchInputElem.addEventListener('keydown', function(e) {
  189. if (e.key === 'Escape') {
  190. if (mobileSearchContainer) {
  191. mobileSearchContainer.style.display = 'none';
  192. }
  193. if (mobileSearchInputElem) {
  194. mobileSearchInputElem.value = '';
  195. }
  196. if (mobileSearchResultsElem) {
  197. while (mobileSearchResultsElem.firstChild) {
  198. mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
  199. }
  200. }
  201. if (htmlElem) {
  202. htmlElem.style.overflowY = 'visible';
  203. }
  204. }
  205. }) : null;
  206. // ============================================================
  207. // ======================= theme change =======================
  208. var localTheme = localStorage.getItem('theme');
  209. var rootEleme = document.getElementById('root');
  210. var selectThemeElem = document.querySelectorAll('.select-theme');
  211. var selectThemeItemElem = document.querySelectorAll('.select-theme__item');
  212. if (localTheme) {
  213. selectThemeItemElem ?
  214. selectThemeItemElem.forEach(function (elem) {
  215. if (elem.text.trim() === localTheme) {
  216. elem.classList.add('is-active');
  217. } else {
  218. elem.classList.remove('is-active');
  219. }
  220. }) : null;
  221. }
  222. selectThemeItemElem ?
  223. selectThemeItemElem.forEach(function (v, i) {
  224. v.addEventListener('click', function (e) {
  225. var selectedThemeVariant = e.target.text.trim();
  226. localStorage.setItem('theme', selectedThemeVariant);
  227. rootEleme.removeAttribute('class');
  228. rootEleme.classList.add(`theme__${selectedThemeVariant}`);
  229. selectThemeElem.forEach(function(rootElem) {
  230. rootElem.querySelectorAll('a').forEach(function (elem) {
  231. if (elem.classList) {
  232. if (elem.text.trim() === selectedThemeVariant) {
  233. if (!elem.classList.contains('is-active')) {
  234. elem.classList.add('is-active');
  235. }
  236. } else {
  237. if (elem.classList.contains('is-active')) {
  238. elem.classList.remove('is-active');
  239. }
  240. }
  241. }
  242. });
  243. });
  244. if (window.mermaid) {
  245. if (selectedThemeVariant === "dark" || selectedThemeVariant === "hacker") {
  246. mermaid.initialize({ theme: 'dark' });
  247. location.reload();
  248. } else {
  249. mermaid.initialize({ theme: 'default' });
  250. location.reload();
  251. }
  252. }
  253. var utterances = document.querySelector('iframe');
  254. if (utterances) {
  255. utterances.contentWindow.postMessage({
  256. type: 'set-theme',
  257. theme: selectedThemeVariant === "dark" || selectedThemeVariant === "hacker" ? 'photon-dark' : selectedThemeVariant === 'kimbie' ? 'github-dark-orange' : 'github-light',
  258. }, 'https://utteranc.es');
  259. }
  260. });
  261. }) : null;
  262. // ============================================================
  263. // ========================== search ==========================
  264. var searchResults = null;
  265. var searchMenu = null;
  266. var searchText = null;
  267. {{ $enableSearchHighlight := ($.Param "enableSearchHighlight") }}
  268. var enableSearchHighlight = JSON.parse({{ $enableSearchHighlight | jsonify }});
  269. var fuse = null;
  270. var xhr = new XMLHttpRequest();
  271. xhr.open('GET', permalink + "index.json");
  272. xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
  273. xhr.onload = function () {
  274. if (xhr.status === 200) {
  275. fuse = new Fuse(JSON.parse(xhr.response.toString('utf-8')), {
  276. keys: ['title', 'description', 'content'],
  277. includeMatches: enableSearchHighlight,
  278. shouldSort: true,
  279. threshold: 0.4,
  280. location: 0,
  281. distance: 100,
  282. maxPatternLength: 32,
  283. minMatchCharLength: 1,
  284. });
  285. }
  286. else {
  287. console.error(`[${xhr.status}]Error:`, xhr.statusText);
  288. }
  289. };
  290. xhr.send();
  291. }
  292. function renderSearchResults(results) { // [{}, {}, ...] or [{item: {}, matches: []}, ...]
  293. searchResults = document.getElementById('search-results');
  294. searchMenu = document.getElementById('search-menu');
  295. searchResults.setAttribute('class', 'dd is-active');
  296. var content = document.createElement('div');
  297. content.setAttribute('class', 'dd-content search-content');
  298. if (results.length > 0) {
  299. results.forEach(function (result) {
  300. var item = document.createElement('a');
  301. item.setAttribute('href', result.uri);
  302. item.setAttribute('class', 'dd-item');
  303. item.innerHTML = `<div class="menu-item"><div class="menu-item__title">📄 ${result.title}</div><div class="menu-item__desc">${result.description ? result.description : result.content}</div></div>`;
  304. content.appendChild(item);
  305. });
  306. } else {
  307. var item = document.createElement('span');
  308. item.setAttribute('class', 'dd-item');
  309. item.innerText = 'No results found';
  310. content.appendChild(item);
  311. }
  312. while (searchMenu.hasChildNodes()) {
  313. searchMenu.removeChild(
  314. searchMenu.lastChild
  315. );
  316. }
  317. searchMenu.appendChild(content);
  318. }
  319. function renderSearchHighlightResults(results) {
  320. searchResults = document.getElementById('search-results');
  321. searchMenu = document.getElementById('search-menu');
  322. searchResults.setAttribute('class', 'dd is-active');
  323. var content = document.createElement('div');
  324. content.setAttribute('class', 'dd-content search-content');
  325. if (results.length > 0) {
  326. results.forEach(function (result) {
  327. var item = document.createElement('a');
  328. item.setAttribute('href', result.item.uri);
  329. item.setAttribute('class', 'dd-item');
  330. item.innerHTML = `<div class="menu-item"><div class="menu-item__title">📄 ${generateHighlightedText(result.item.title, result.matches[0].indices)}</div><div class="menu-item__desc">${result.matches[1] ? generateHighlightedText(result.item.description, result.matches[1].indices) : result.matches[2] ? generateHighlightedText(result.item.content, result.matches[2].indices) : ''}</div></div>`;
  331. content.appendChild(item);
  332. });
  333. } else {
  334. var item = document.createElement('span');
  335. item.setAttribute('class', 'dd-item');
  336. item.innerText = 'No results found';
  337. content.appendChild(item);
  338. }
  339. while (searchMenu.hasChildNodes()) {
  340. searchMenu.removeChild(
  341. searchMenu.lastChild
  342. );
  343. }
  344. searchMenu.appendChild(content);
  345. }
  346. function renderSearchResultsMobile(results) {
  347. searchResults = document.getElementById('search-mobile-results');
  348. var content = document.createElement('div');
  349. content.setAttribute('class', 'mobile-search__content');
  350. if (results.length > 0) {
  351. results.forEach(function (result) {
  352. var item = document.createElement('a');
  353. item.setAttribute('href', result.uri);
  354. item.innerHTML = `<div class="mobile-search__item"><div class="mobile-search__item--title">📄 ${result.title}</div><div class="mobile-search__item--desc">${result.description ? result.description : result.content}</div></div>`;
  355. content.appendChild(item);
  356. });
  357. } else {
  358. var item = document.createElement('span');
  359. content.appendChild(item);
  360. }
  361. let wrap = document.getElementById('search-mobile-results');
  362. while (wrap.firstChild) {
  363. wrap.removeChild(wrap.firstChild)
  364. }
  365. searchResults.appendChild(content);
  366. }
  367. function renderSearchHighlightResultsMobile(results) {
  368. searchResults = document.getElementById('search-mobile-results');
  369. var content = document.createElement('div');
  370. content.setAttribute('class', 'mobile-search__content');
  371. if (results.length > 0) {
  372. results.forEach(function (result) {
  373. var item = document.createElement('a');
  374. item.setAttribute('href', result.item.uri);
  375. item.innerHTML = `<div class="mobile-search__item"><div class="mobile-search__item--title">📄 ${generateHighlightedText(result.item.title, result.matches[0].indices)}</div><div class="mobile-search__item--desc">${result.matches[1] ? generateHighlightedText(result.item.description, result.matches[1].indices) : result.matches[2] ? generateHighlightedText(result.item.content, result.matches[2].indices) : ''}</div></div>`;
  376. content.appendChild(item);
  377. });
  378. } else {
  379. var item = document.createElement('span');
  380. content.appendChild(item);
  381. }
  382. let wrap = document.getElementById('search-mobile-results');
  383. while (wrap.firstChild) {
  384. wrap.removeChild(wrap.firstChild)
  385. }
  386. searchResults.appendChild(content);
  387. }
  388. function generateHighlightedText(text, regions) {
  389. if (!regions) {
  390. return text;
  391. }
  392. var content = '', nextUnhighlightedRegionStartingIndex = 0;
  393. regions.forEach(function(region) {
  394. if (region[0] === region[1]) {
  395. return null;
  396. }
  397. content += '' +
  398. text.substring(nextUnhighlightedRegionStartingIndex, region[0]) +
  399. '<span class="search__highlight">' +
  400. text.substring(region[0], region[1] + 1) +
  401. '</span>' +
  402. '';
  403. nextUnhighlightedRegionStartingIndex = region[1] + 1;
  404. });
  405. content += text.substring(nextUnhighlightedRegionStartingIndex);
  406. return content;
  407. };
  408. initFuse();
  409. var searchElem = document.getElementById('search');
  410. var searchMobile = document.getElementById('search-mobile');
  411. searchElem.addEventListener('input', function(e) {
  412. if (!e.target.value) {
  413. document.getElementById('search-results').setAttribute('class', 'dd');
  414. return null;
  415. }
  416. if (window.innerWidth < 770) {
  417. return null;
  418. }
  419. searchText = e.target.value;
  420. var results = fuse.search(e.target.value);
  421. if (enableSearchHighlight) {
  422. renderSearchHighlightResults(results);
  423. } else {
  424. renderSearchResults(results);
  425. }
  426. });
  427. searchElem.addEventListener('blur', function() {
  428. if (window.innerWidth < 770) {
  429. return null;
  430. }
  431. setTimeout(function () {
  432. document.getElementById('search-results').setAttribute('class', 'dd');
  433. }, 100);
  434. });
  435. searchElem.addEventListener('click', function(e) {
  436. if (window.innerWidth < 770) {
  437. return null;
  438. }
  439. if (!e.target.value) {
  440. document.getElementById('search-results').setAttribute('class', 'dd');
  441. return null;
  442. }
  443. searchText = e.target.value;
  444. var results = fuse.search(e.target.value);
  445. if (enableSearchHighlight) {
  446. renderSearchHighlightResults(results);
  447. } else {
  448. renderSearchResults(results);
  449. }
  450. });
  451. function indexInParent(node) {
  452. var children = node.parentNode.childNodes;
  453. var num = 0;
  454. for (var i = 0; i < children.length; i++) {
  455. if (children[i] == node) return num;
  456. if (children[i].nodeType == 1) num++;
  457. }
  458. return -1;
  459. }
  460. var searchMenuElem = document.getElementById("search-menu");
  461. var activeItem = document.querySelector('#search-menu .dd-item.is-active');
  462. var activeIndex = null;
  463. var items = null;
  464. var searchContainerMaxHeight = 350;
  465. searchElem.addEventListener('keydown', function(e) {
  466. if (window.innerWidth < 770) {
  467. return null;
  468. }
  469. var items = document.querySelectorAll('#search-menu .dd-item');
  470. if (e.key === 'ArrowDown') {
  471. if (activeIndex === null) {
  472. activeIndex = 0;
  473. items[activeIndex].classList.remove('is-active');
  474. } else {
  475. items[activeIndex].classList.remove('is-active');
  476. activeIndex = activeIndex === items.length - 1 ? 0 : activeIndex + 1;
  477. }
  478. items[activeIndex].classList.add('is-active');
  479. let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
  480. if (overflowedPixel > 0) {
  481. document.querySelector(".search-content").scrollTop += items[activeIndex].getBoundingClientRect().height;
  482. } else if (activeIndex === 0) {
  483. document.querySelector(".search-content").scrollTop = 0;
  484. }
  485. } else if (e.key === 'ArrowUp') {
  486. if (activeIndex === null) {
  487. activeIndex = items.length - 1;
  488. items[activeIndex].classList.remove('is-active');
  489. } else {
  490. items[activeIndex].classList.remove('is-active');
  491. activeIndex = activeIndex === 0 ? items.length - 1 : activeIndex - 1;
  492. }
  493. items[activeIndex].classList.add('is-active');
  494. let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
  495. if (overflowedPixel < 0) {
  496. document.querySelector(".search-content").scrollTop -= items[activeIndex].getBoundingClientRect().height;
  497. } else {
  498. document.querySelector(".search-content").scrollTop = overflowedPixel + items[activeIndex].getBoundingClientRect().height;
  499. }
  500. } else if (e.key === 'Enter') {
  501. var currentItemLink = items[activeIndex].getAttribute('href');
  502. if (currentItemLink) {
  503. location.href = currentItemLink;
  504. }
  505. } else if (e.key === 'Escape') {
  506. e.target.value = null;
  507. if (searchResults) {
  508. searchResults.classList.remove('is-active');
  509. }
  510. }
  511. });
  512. searchMobile.addEventListener('input', function(e) {
  513. if (!e.target.value) {
  514. let wrap = document.getElementById('search-mobile-results');
  515. while (wrap.firstChild) {
  516. wrap.removeChild(wrap.firstChild);
  517. }
  518. return null;
  519. }
  520. searchText = e.target.value;
  521. var results = fuse.search(e.target.value);
  522. renderSearchResultsMobile(results);
  523. if (enableSearchHighlight) {
  524. renderSearchHighlightResultsMobile(results);
  525. } else {
  526. renderSearchResultsMobile(results);
  527. }
  528. });
  529. // ============================================================
  530. }
  531. </script>