entry_page.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // TODO: This file was created by bulk-decaffeinate.
  2. // Sanity-check the conversion and remove this comment.
  3. /*
  4. * decaffeinate suggestions:
  5. * DS002: Fix invalid constructor
  6. * DS101: Remove unnecessary use of Array.from
  7. * DS102: Remove unnecessary code created because of implicit returns
  8. * DS205: Consider reworking code to avoid use of IIFEs
  9. * DS206: Consider reworking classes to avoid initClass
  10. * DS207: Consider shorter variations of null checks
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
  12. */
  13. (function() {
  14. let LINKS = undefined;
  15. const Cls = (app.views.EntryPage = class EntryPage extends app.View {
  16. constructor(...args) {
  17. this.beforeRoute = this.beforeRoute.bind(this);
  18. this.onSuccess = this.onSuccess.bind(this);
  19. this.onError = this.onError.bind(this);
  20. this.onClick = this.onClick.bind(this);
  21. this.onAltC = this.onAltC.bind(this);
  22. this.onAltO = this.onAltO.bind(this);
  23. super(...args);
  24. }
  25. static initClass() {
  26. this.className = '_page';
  27. this.errorClass = '_page-error';
  28. this.events =
  29. {click: 'onClick'};
  30. this.shortcuts = {
  31. altC: 'onAltC',
  32. altO: 'onAltO'
  33. };
  34. this.routes =
  35. {before: 'beforeRoute'};
  36. LINKS = {
  37. home: 'Homepage',
  38. code: 'Source code'
  39. };
  40. }
  41. init() {
  42. this.cacheMap = {};
  43. this.cacheStack = [];
  44. }
  45. deactivate() {
  46. if (super.deactivate(...arguments)) {
  47. this.empty();
  48. this.entry = null;
  49. }
  50. }
  51. loading() {
  52. this.empty();
  53. this.trigger('loading');
  54. }
  55. render(content, fromCache) {
  56. if (content == null) { content = ''; }
  57. if (fromCache == null) { fromCache = false; }
  58. if (!this.activated) { return; }
  59. this.empty();
  60. this.subview = new (this.subViewClass())(this.el, this.entry);
  61. $.batchUpdate(this.el, () => {
  62. this.subview.render(content, fromCache);
  63. if (!fromCache) { this.addCopyButtons(); }
  64. });
  65. if (app.disabledDocs.findBy('slug', this.entry.doc.slug)) {
  66. this.hiddenView = new app.views.HiddenPage(this.el, this.entry);
  67. }
  68. setFaviconForDoc(this.entry.doc);
  69. this.delay(this.polyfillMathML);
  70. this.trigger('loaded');
  71. }
  72. addCopyButtons() {
  73. if (!this.copyButton) {
  74. this.copyButton = document.createElement('button');
  75. this.copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>';
  76. this.copyButton.type = 'button';
  77. this.copyButton.className = '_pre-clip';
  78. this.copyButton.title = 'Copy to clipboard';
  79. this.copyButton.setAttribute('aria-label', 'Copy to clipboard');
  80. }
  81. for (var el of Array.from(this.findAllByTag('pre'))) { el.appendChild(this.copyButton.cloneNode(true)); }
  82. }
  83. polyfillMathML() {
  84. if ((window.supportsMathML !== false) || !!this.polyfilledMathML || !this.findByTag('math')) { return; }
  85. this.polyfilledMathML = true;
  86. $.append(document.head, `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`);
  87. }
  88. prepareContent(content) {
  89. if (!this.entry.isIndex() || !this.entry.doc.links) { return content; }
  90. const links = (() => {
  91. const result = [];
  92. for (var link in this.entry.doc.links) {
  93. var url = this.entry.doc.links[link];
  94. result.push(`<a href="${url}" class="_links-link">${LINKS[link]}</a>`);
  95. }
  96. return result;
  97. })();
  98. return `<p class="_links">${links.join('')}</p>${content}`;
  99. }
  100. empty() {
  101. if (this.subview != null) {
  102. this.subview.deactivate();
  103. }
  104. this.subview = null;
  105. if (this.hiddenView != null) {
  106. this.hiddenView.deactivate();
  107. }
  108. this.hiddenView = null;
  109. this.resetClass();
  110. super.empty(...arguments);
  111. }
  112. subViewClass() {
  113. return app.views[`${$.classify(this.entry.doc.type)}Page`] || app.views.BasePage;
  114. }
  115. getTitle() {
  116. return this.entry.doc.fullName + (this.entry.isIndex() ? ' documentation' : ` / ${this.entry.name}`);
  117. }
  118. beforeRoute() {
  119. this.cache();
  120. this.abort();
  121. }
  122. onRoute(context) {
  123. const isSameFile = context.entry.filePath() === (this.entry != null ? this.entry.filePath() : undefined);
  124. this.entry = context.entry;
  125. if (!isSameFile) { this.restore() || this.load(); }
  126. }
  127. load() {
  128. this.loading();
  129. this.xhr = this.entry.loadFile(this.onSuccess, this.onError);
  130. }
  131. abort() {
  132. if (this.xhr) {
  133. this.xhr.abort();
  134. this.xhr = (this.entry = null);
  135. }
  136. }
  137. onSuccess(response) {
  138. if (!this.activated) { return; }
  139. this.xhr = null;
  140. this.render(this.prepareContent(response));
  141. }
  142. onError() {
  143. this.xhr = null;
  144. this.render(this.tmpl('pageLoadError'));
  145. this.resetClass();
  146. this.addClass(this.constructor.errorClass);
  147. if (app.serviceWorker != null) {
  148. app.serviceWorker.update();
  149. }
  150. }
  151. cache() {
  152. let path;
  153. if (this.xhr || !this.entry || this.cacheMap[(path = this.entry.filePath())]) { return; }
  154. this.cacheMap[path] = this.el.innerHTML;
  155. this.cacheStack.push(path);
  156. while (this.cacheStack.length > app.config.history_cache_size) {
  157. delete this.cacheMap[this.cacheStack.shift()];
  158. }
  159. }
  160. restore() {
  161. let path;
  162. if (this.cacheMap[(path = this.entry.filePath())]) {
  163. this.render(this.cacheMap[path], true);
  164. return true;
  165. }
  166. }
  167. onClick(event) {
  168. const target = $.eventTarget(event);
  169. if (target.hasAttribute('data-retry')) {
  170. $.stopEvent(event);
  171. this.load();
  172. } else if (target.classList.contains('_pre-clip')) {
  173. $.stopEvent(event);
  174. target.classList.add($.copyToClipboard(target.parentNode.textContent) ? '_pre-clip-success' : '_pre-clip-error');
  175. setTimeout((() => target.className = '_pre-clip'), 2000);
  176. }
  177. }
  178. onAltC() {
  179. let link;
  180. if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
  181. console.log(link.href + location.hash);
  182. navigator.clipboard.writeText(link.href + location.hash);
  183. }
  184. onAltO() {
  185. let link;
  186. if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
  187. this.delay(() => $.popup(link.href + location.hash));
  188. }
  189. });
  190. Cls.initClass();
  191. return Cls;
  192. })();