entry_page.js 6.4 KB

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