a zip code crypto-currency system good for red ONLY

ecmarkup.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. "use strict";
  2. function Search(menu) {
  3. this.menu = menu;
  4. this.$search = document.getElementById('menu-search');
  5. this.$searchBox = document.getElementById('menu-search-box');
  6. this.$searchResults = document.getElementById('menu-search-results');
  7. this.loadBiblio();
  8. document.addEventListener('keydown', this.documentKeydown.bind(this));
  9. this.$searchBox.addEventListener('keydown', debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true }));
  10. this.$searchBox.addEventListener('keyup', debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true }));
  11. }
  12. Search.prototype.loadBiblio = function () {
  13. var $biblio = document.getElementById('menu-search-biblio');
  14. if (!$biblio) {
  15. this.biblio = {};
  16. } else {
  17. this.biblio = JSON.parse($biblio.textContent);
  18. this.biblio.clauses = this.biblio.filter(function (e) { return e.type === 'clause' });
  19. this.biblio.clausesById = this.biblio.clauses.reduce(function (map, entry) {
  20. map[entry.id] = entry;
  21. return map;
  22. }, {});
  23. }
  24. }
  25. Search.prototype.documentKeydown = function (e) {
  26. if (e.keyCode === 191) {
  27. e.preventDefault();
  28. e.stopPropagation();
  29. this.triggerSearch();
  30. }
  31. }
  32. Search.prototype.searchBoxKeydown = function (e) {
  33. e.stopPropagation();
  34. e.preventDefault();
  35. if (e.keyCode === 191 && e.target.value.length === 0) {
  36. e.preventDefault();
  37. } else if (e.keyCode === 13) {
  38. e.preventDefault();
  39. this.selectResult();
  40. }
  41. }
  42. Search.prototype.searchBoxKeyup = function (e) {
  43. if (e.keyCode === 13 || e.keyCode === 9) {
  44. return;
  45. }
  46. this.search(e.target.value);
  47. }
  48. Search.prototype.triggerSearch = function (e) {
  49. if (this.menu.isVisible()) {
  50. this._closeAfterSearch = false;
  51. } else {
  52. this._closeAfterSearch = true;
  53. this.menu.show();
  54. }
  55. this.$searchBox.focus();
  56. this.$searchBox.select();
  57. }
  58. // bit 12 - Set if the result starts with searchString
  59. // bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1.
  60. // bits 1-7: 127 - length of the entry
  61. // General scheme: prefer case sensitive matches with fewer chunks, and otherwise
  62. // prefer shorter matches.
  63. function relevance(result, searchString) {
  64. var relevance = 0;
  65. relevance = Math.max(0, 8 - result.match.chunks) << 7;
  66. if (result.match.caseMatch) {
  67. relevance *= 2;
  68. }
  69. if (result.match.prefix) {
  70. relevance += 2048
  71. }
  72. relevance += Math.max(0, 255 - result.entry.key.length);
  73. return relevance;
  74. }
  75. Search.prototype.search = function (searchString) {
  76. var s = Date.now();
  77. if (searchString === '') {
  78. this.displayResults([]);
  79. this.hideSearch();
  80. return;
  81. } else {
  82. this.showSearch();
  83. }
  84. if (searchString.length === 1) {
  85. this.displayResults([]);
  86. return;
  87. }
  88. var results;
  89. if (/^[\d\.]*$/.test(searchString)) {
  90. results = this.biblio.clauses.filter(function (clause) {
  91. return clause.number.substring(0, searchString.length) === searchString;
  92. }).map(function (clause) {
  93. return { entry: clause };
  94. });
  95. } else {
  96. results = [];
  97. for (var i = 0; i < this.biblio.length; i++) {
  98. var entry = this.biblio[i];
  99. var match = fuzzysearch(searchString, entry.key);
  100. if (match) {
  101. results.push({ entry: entry, match: match });
  102. }
  103. }
  104. results.forEach(function (result) {
  105. result.relevance = relevance(result, searchString);
  106. });
  107. results = results.sort(function (a, b) { return b.relevance - a.relevance });
  108. }
  109. if (results.length > 50) {
  110. results = results.slice(0, 50);
  111. }
  112. this.displayResults(results);
  113. }
  114. Search.prototype.hideSearch = function () {
  115. this.$search.classList.remove('active');
  116. }
  117. Search.prototype.showSearch = function () {
  118. this.$search.classList.add('active');
  119. }
  120. Search.prototype.selectResult = function () {
  121. var $first = this.$searchResults.querySelector('li:first-child a');
  122. if ($first) {
  123. document.location = $first.getAttribute('href');
  124. }
  125. this.$searchBox.value = '';
  126. this.$searchBox.blur();
  127. this.displayResults([]);
  128. this.hideSearch();
  129. if (this._closeAfterSearch) {
  130. this.menu.hide();
  131. }
  132. }
  133. Search.prototype.displayResults = function (results) {
  134. if (results.length > 0) {
  135. this.$searchResults.classList.remove('no-results');
  136. var html = '<ul>';
  137. results.forEach(function (result) {
  138. var entry = result.entry;
  139. var id = entry.id;
  140. var cssClass = '';
  141. var text = '';
  142. if (entry.type === 'clause') {
  143. var number = entry.number ? entry.number + ' ' : '';
  144. text = number + entry.key;
  145. cssClass = 'clause';
  146. id = entry.id;
  147. } else if (entry.type === 'production') {
  148. text = entry.key;
  149. cssClass = 'prod';
  150. id = entry.id;
  151. } else if (entry.type === 'op') {
  152. text = entry.key;
  153. cssClass = 'op';
  154. id = entry.refId;
  155. } else if (entry.type === 'term') {
  156. text = entry.key;
  157. cssClass = 'term';
  158. id = entry.id || entry.refId;
  159. }
  160. if (text) {
  161. html += '<li class=menu-search-result-' + cssClass + '><a href="#' + id + '">' + text + '</a></li>'
  162. }
  163. });
  164. html += '</ul>'
  165. this.$searchResults.innerHTML = html;
  166. } else {
  167. this.$searchResults.innerHTML = '';
  168. this.$searchResults.classList.add('no-results');
  169. }
  170. }
  171. function Menu() {
  172. this.$toggle = document.getElementById('menu-toggle');
  173. this.$menu = document.getElementById('menu');
  174. this.$toc = document.querySelector('menu-toc > ol');
  175. this.$pins = document.querySelector('#menu-pins');
  176. this.$pinList = document.getElementById('menu-pins-list');
  177. this.$toc = document.querySelector('#menu-toc > ol');
  178. this.$specContainer = document.getElementById('spec-container');
  179. this.search = new Search(this);
  180. this._pinnedIds = {};
  181. this.loadPinEntries();
  182. // toggle menu
  183. this.$toggle.addEventListener('click', this.toggle.bind(this));
  184. // keydown events for pinned clauses
  185. document.addEventListener('keydown', this.documentKeydown.bind(this));
  186. // toc expansion
  187. var tocItems = this.$menu.querySelectorAll('#menu-toc li');
  188. for (var i = 0; i < tocItems.length; i++) {
  189. var $item = tocItems[i];
  190. $item.addEventListener('click', function($item, event) {
  191. $item.classList.toggle('active');
  192. event.stopPropagation();
  193. }.bind(null, $item));
  194. }
  195. // close toc on toc item selection
  196. var tocLinks = this.$menu.querySelectorAll('#menu-toc li > a');
  197. for (var i = 0; i < tocLinks.length; i++) {
  198. var $link = tocLinks[i];
  199. $link.addEventListener('click', function(event) {
  200. this.toggle();
  201. event.stopPropagation();
  202. }.bind(this));
  203. }
  204. // update active clause on scroll
  205. window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this)));
  206. this.updateActiveClause();
  207. // prevent menu scrolling from scrolling the body
  208. this.$toc.addEventListener('wheel', function (e) {
  209. var target = e.currentTarget;
  210. var offTop = e.deltaY < 0 && target.scrollTop === 0;
  211. if (offTop) {
  212. e.preventDefault();
  213. }
  214. var offBottom = e.deltaY > 0
  215. && target.offsetHeight + target.scrollTop >= target.scrollHeight;
  216. if (offBottom) {
  217. event.preventDefault();
  218. }
  219. })
  220. // handle pinning clauses via the pin link
  221. document.addEventListener('click', function (e) {
  222. if (e.target.classList.contains('utils-pin')) {
  223. var id = e.target.parentNode.parentNode.parentNode.parentNode.id;
  224. this.togglePinEntry(id);
  225. }
  226. }.bind(this))
  227. }
  228. Menu.prototype.documentKeydown = function (e) {
  229. e.stopPropagation();
  230. if (e.keyCode === 80) {
  231. this.togglePinEntry();
  232. } else if (e.keyCode > 48 && e.keyCode < 58) {
  233. this.selectPin(e.keyCode - 49);
  234. }
  235. }
  236. Menu.prototype.updateActiveClause = function () {
  237. this.setActiveClause(findActiveClause(this.$specContainer))
  238. }
  239. Menu.prototype.setActiveClause = function (clause) {
  240. this.$activeClause = clause;
  241. this.revealInToc(this.$activeClause);
  242. }
  243. Menu.prototype.revealInToc = function (path) {
  244. var current = this.$toc.querySelectorAll('li.revealed');
  245. for (var i = 0; i < current.length; i++) {
  246. current[i].classList.remove('revealed');
  247. current[i].classList.remove('revealed-leaf');
  248. }
  249. var current = this.$toc;
  250. var index = 0;
  251. while (index < path.length) {
  252. var children = current.children;
  253. for (var i = 0; i < children.length; i++) {
  254. if ('#' + path[index].id === children[i].children[1].getAttribute('href') ) {
  255. children[i].classList.add('revealed');
  256. if (index === path.length - 1) {
  257. children[i].classList.add('revealed-leaf');
  258. var rect = children[i].getBoundingClientRect();
  259. this.$toc.getBoundingClientRect().top
  260. var tocRect = this.$toc.getBoundingClientRect();
  261. if (rect.top + 10 > tocRect.bottom) {
  262. this.$toc.scrollTop = this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top);
  263. } else if (rect.top < tocRect.top) {
  264. this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top);
  265. }
  266. }
  267. current = children[i].querySelector('ol');
  268. index++;
  269. break;
  270. }
  271. }
  272. }
  273. }
  274. function findActiveClause(root, path) {
  275. var clauses = new ClauseWalker(root);
  276. var $clause;
  277. var found = false;
  278. var path = path || [];
  279. while ($clause = clauses.nextNode()) {
  280. var rect = $clause.getBoundingClientRect();
  281. var $header = $clause.children[0];
  282. var marginTop = parseInt(getComputedStyle($header)["margin-top"]);
  283. if ((rect.top - marginTop) <= 0 && rect.bottom > 0) {
  284. found = true;
  285. return findActiveClause($clause, path.concat($clause)) || path;
  286. }
  287. }
  288. return path;
  289. }
  290. function ClauseWalker(root) {
  291. var previous;
  292. var treeWalker = document.createTreeWalker(
  293. root,
  294. NodeFilter.SHOW_ELEMENT,
  295. {
  296. acceptNode: function (node) {
  297. if (previous === node.parentNode) {
  298. return NodeFilter.FILTER_REJECT;
  299. } else {
  300. previous = node;
  301. }
  302. if (node.nodeName === 'EMU-CLAUSE' || node.nodeName === 'EMU-INTRO' || node.nodeName === 'EMU-ANNEX') {
  303. return NodeFilter.FILTER_ACCEPT;
  304. } else {
  305. return NodeFilter.FILTER_SKIP;
  306. }
  307. }
  308. },
  309. false
  310. );
  311. return treeWalker;
  312. }
  313. Menu.prototype.toggle = function () {
  314. this.$menu.classList.toggle('active');
  315. }
  316. Menu.prototype.show = function () {
  317. this.$menu.classList.add('active');
  318. }
  319. Menu.prototype.hide = function () {
  320. this.$menu.classList.remove('active');
  321. }
  322. Menu.prototype.isVisible = function() {
  323. return this.$menu.classList.contains('active');
  324. }
  325. Menu.prototype.showPins = function () {
  326. this.$pins.classList.add('active');
  327. }
  328. Menu.prototype.hidePins = function () {
  329. this.$pins.classList.remove('active');
  330. }
  331. Menu.prototype.addPinEntry = function (id) {
  332. var entry = this.search.biblio.clausesById[id];
  333. var prefix;
  334. if (entry.number) {
  335. prefix = entry.number + ' ';
  336. } else {
  337. prefix = '';
  338. }
  339. this.$pinList.innerHTML += '<li><a href="#' + entry.id + '">' + prefix + entry.titleHTML + '</a></li>';
  340. if (Object.keys(this._pinnedIds).length === 0) {
  341. this.showPins();
  342. }
  343. this._pinnedIds[id] = true;
  344. this.persistPinEntries();
  345. }
  346. Menu.prototype.removePinEntry = function (id) {
  347. var item = this.$pinList.querySelector('a[href="#' + id + '"]').parentNode;
  348. this.$pinList.removeChild(item);
  349. delete this._pinnedIds[id];
  350. if (Object.keys(this._pinnedIds).length === 0) {
  351. this.hidePins();
  352. }
  353. this.persistPinEntries();
  354. }
  355. Menu.prototype.persistPinEntries = function () {
  356. try {
  357. if (!window.localStorage) return;
  358. } catch (e) {
  359. return;
  360. }
  361. localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds));
  362. }
  363. Menu.prototype.loadPinEntries = function () {
  364. try {
  365. if (!window.localStorage) return;
  366. } catch (e) {
  367. return;
  368. }
  369. var pinsString = window.localStorage.pinEntries;
  370. if (!pinsString) return;
  371. var pins = JSON.parse(pinsString);
  372. for(var i = 0; i < pins.length; i++) {
  373. this.addPinEntry(pins[i]);
  374. }
  375. }
  376. Menu.prototype.togglePinEntry = function (id) {
  377. if (!id) {
  378. id = this.$activeClause[this.$activeClause.length - 1].id;
  379. }
  380. if (this._pinnedIds[id]) {
  381. this.removePinEntry(id);
  382. } else {
  383. this.addPinEntry(id);
  384. }
  385. }
  386. Menu.prototype.selectPin = function (num) {
  387. document.location = this.$pinList.children[num].children[0].href;
  388. }
  389. function init() {
  390. var menu = new Menu();
  391. }
  392. document.addEventListener('DOMContentLoaded', init);
  393. function debounce(fn, opts) {
  394. opts = opts || {};
  395. var timeout;
  396. return function(e) {
  397. if (opts.stopPropagation) {
  398. e.stopPropagation();
  399. }
  400. var args = arguments;
  401. if (timeout) {
  402. clearTimeout(timeout);
  403. }
  404. timeout = setTimeout(function() {
  405. timeout = null;
  406. fn.apply(this, args);
  407. }.bind(this), 150);
  408. }
  409. }
  410. var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX'];
  411. function findLocalReferences ($elem) {
  412. var name = $elem.innerHTML;
  413. var references = [];
  414. var parentClause = $elem.parentNode;
  415. while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) {
  416. parentClause = parentClause.parentNode;
  417. }
  418. if(!parentClause) return;
  419. var vars = parentClause.querySelectorAll('var');
  420. for (var i = 0; i < vars.length; i++) {
  421. var $var = vars[i];
  422. if ($var.innerHTML === name) {
  423. references.push($var);
  424. }
  425. }
  426. return references;
  427. }
  428. function toggleFindLocalReferences($elem) {
  429. var references = findLocalReferences($elem);
  430. if ($elem.classList.contains('referenced')) {
  431. references.forEach(function ($reference) {
  432. $reference.classList.remove('referenced');
  433. });
  434. } else {
  435. references.forEach(function ($reference) {
  436. $reference.classList.add('referenced');
  437. });
  438. }
  439. }
  440. function installFindLocalReferences () {
  441. document.addEventListener('click', function (e) {
  442. if (e.target.nodeName === 'VAR') {
  443. toggleFindLocalReferences(e.target);
  444. }
  445. });
  446. }
  447. document.addEventListener('DOMContentLoaded', installFindLocalReferences);
  448. // The following license applies to the fuzzysearch function
  449. // The MIT License (MIT)
  450. // Copyright © 2015 Nicolas Bevacqua
  451. // Copyright © 2016 Brian Terlson
  452. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  453. // this software and associated documentation files (the "Software"), to deal in
  454. // the Software without restriction, including without limitation the rights to
  455. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  456. // the Software, and to permit persons to whom the Software is furnished to do so,
  457. // subject to the following conditions:
  458. // The above copyright notice and this permission notice shall be included in all
  459. // copies or substantial portions of the Software.
  460. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  461. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  462. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  463. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  464. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  465. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  466. function fuzzysearch (searchString, haystack, caseInsensitive) {
  467. var tlen = haystack.length;
  468. var qlen = searchString.length;
  469. var chunks = 1;
  470. var finding = false;
  471. var prefix = true;
  472. if (qlen > tlen) {
  473. return false;
  474. }
  475. if (qlen === tlen) {
  476. if (searchString === haystack) {
  477. return { caseMatch: true, chunks: 1, prefix: true };
  478. } else if (searchString.toLowerCase() === haystack.toLowerCase()) {
  479. return { caseMatch: false, chunks: 1, prefix: true };
  480. } else {
  481. return false;
  482. }
  483. }
  484. outer: for (var i = 0, j = 0; i < qlen; i++) {
  485. var nch = searchString[i];
  486. while (j < tlen) {
  487. var targetChar = haystack[j++];
  488. if (targetChar === nch) {
  489. finding = true;
  490. continue outer;
  491. }
  492. if (finding) {
  493. chunks++;
  494. finding = false;
  495. }
  496. }
  497. if (caseInsensitive) { return false }
  498. return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true);
  499. }
  500. return { caseMatch: !caseInsensitive, chunks: chunks, prefix: j <= qlen };
  501. }
  502. var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX'];
  503. function findLocalReferences ($elem) {
  504. var name = $elem.innerHTML;
  505. var references = [];
  506. var parentClause = $elem.parentNode;
  507. while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) {
  508. parentClause = parentClause.parentNode;
  509. }
  510. if(!parentClause) return;
  511. var vars = parentClause.querySelectorAll('var');
  512. for (var i = 0; i < vars.length; i++) {
  513. var $var = vars[i];
  514. if ($var.innerHTML === name) {
  515. references.push($var);
  516. }
  517. }
  518. return references;
  519. }
  520. function toggleFindLocalReferences($elem) {
  521. var references = findLocalReferences($elem);
  522. if ($elem.classList.contains('referenced')) {
  523. references.forEach(function ($reference) {
  524. $reference.classList.remove('referenced');
  525. });
  526. } else {
  527. references.forEach(function ($reference) {
  528. $reference.classList.add('referenced');
  529. });
  530. }
  531. }
  532. function installFindLocalReferences () {
  533. document.addEventListener('click', function (e) {
  534. if (e.target.nodeName === 'VAR') {
  535. toggleFindLocalReferences(e.target);
  536. }
  537. });
  538. }
  539. document.addEventListener('DOMContentLoaded', installFindLocalReferences);