router.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. app.Router = class Router extends Events {
  2. static routes = [
  3. ["*", "before"],
  4. ["/", "root"],
  5. ["/settings", "settings"],
  6. ["/offline", "offline"],
  7. ["/about", "about"],
  8. ["/news", "news"],
  9. ["/help", "help"],
  10. ["/:doc-:type/", "type"],
  11. ["/:doc/", "doc"],
  12. ["/:doc/:path(*)", "entry"],
  13. ["*", "notFound"],
  14. ];
  15. constructor() {
  16. super();
  17. for (var [path, method] of this.constructor.routes) {
  18. page(path, this[method].bind(this));
  19. }
  20. this.setInitialPath();
  21. }
  22. start() {
  23. page.start();
  24. }
  25. show(path) {
  26. page.show(path);
  27. }
  28. triggerRoute(name) {
  29. this.trigger(name, this.context);
  30. this.trigger("after", name, this.context);
  31. }
  32. before(context, next) {
  33. let res;
  34. const previousContext = this.context;
  35. this.context = context;
  36. this.trigger("before", context);
  37. if ((res = next())) {
  38. this.context = previousContext;
  39. return res;
  40. } else {
  41. return;
  42. }
  43. }
  44. doc(context, next) {
  45. let doc;
  46. if (
  47. (doc =
  48. app.docs.findBySlug(context.params.doc) ||
  49. app.disabledDocs.findBySlug(context.params.doc))
  50. ) {
  51. context.doc = doc;
  52. context.entry = doc.toEntry();
  53. this.triggerRoute("entry");
  54. return;
  55. } else {
  56. return next();
  57. }
  58. }
  59. type(context, next) {
  60. const doc = app.docs.findBySlug(context.params.doc);
  61. const type =
  62. doc != null ? doc.types.findBy("slug", context.params.type) : undefined;
  63. if (type) {
  64. context.doc = doc;
  65. context.type = type;
  66. this.triggerRoute("type");
  67. return;
  68. } else {
  69. return next();
  70. }
  71. }
  72. entry(context, next) {
  73. let entry;
  74. const doc = app.docs.findBySlug(context.params.doc);
  75. if (!doc) {
  76. return next();
  77. }
  78. let { path } = context.params;
  79. const { hash } = context;
  80. if ((entry = doc.findEntryByPathAndHash(path, hash))) {
  81. context.doc = doc;
  82. context.entry = entry;
  83. this.triggerRoute("entry");
  84. return;
  85. } else if (path.slice(-6) === "/index") {
  86. path = path.substr(0, path.length - 6);
  87. if ((entry = doc.findEntryByPathAndHash(path, hash))) {
  88. return entry.fullPath();
  89. }
  90. } else {
  91. path = `${path}/index`;
  92. if ((entry = doc.findEntryByPathAndHash(path, hash))) {
  93. return entry.fullPath();
  94. }
  95. }
  96. return next();
  97. }
  98. root() {
  99. if (app.isSingleDoc()) {
  100. return "/";
  101. }
  102. this.triggerRoute("root");
  103. }
  104. settings(context) {
  105. if (app.isSingleDoc()) {
  106. return `/#/${context.path}`;
  107. }
  108. this.triggerRoute("settings");
  109. }
  110. offline(context) {
  111. if (app.isSingleDoc()) {
  112. return `/#/${context.path}`;
  113. }
  114. this.triggerRoute("offline");
  115. }
  116. about(context) {
  117. if (app.isSingleDoc()) {
  118. return `/#/${context.path}`;
  119. }
  120. context.page = "about";
  121. this.triggerRoute("page");
  122. }
  123. news(context) {
  124. if (app.isSingleDoc()) {
  125. return `/#/${context.path}`;
  126. }
  127. context.page = "news";
  128. this.triggerRoute("page");
  129. }
  130. help(context) {
  131. if (app.isSingleDoc()) {
  132. return `/#/${context.path}`;
  133. }
  134. context.page = "help";
  135. this.triggerRoute("page");
  136. }
  137. notFound(context) {
  138. this.triggerRoute("notFound");
  139. }
  140. isIndex() {
  141. return (
  142. this.context?.path === "/" ||
  143. (app.isSingleDoc() && this.context?.entry?.isIndex())
  144. );
  145. }
  146. isSettings() {
  147. return this.context?.path === "/settings";
  148. }
  149. setInitialPath() {
  150. // Remove superfluous forward slashes at the beginning of the path
  151. let path;
  152. if (
  153. (path = location.pathname.replace(/^\/{2,}/g, "/")) !== location.pathname
  154. ) {
  155. page.replace(path + location.search + location.hash, null, true);
  156. }
  157. if (location.pathname === "/") {
  158. if ((path = this.getInitialPathFromHash())) {
  159. page.replace(path + location.search, null, true);
  160. } else if ((path = this.getInitialPathFromCookie())) {
  161. page.replace(path + location.search + location.hash, null, true);
  162. }
  163. }
  164. }
  165. getInitialPathFromHash() {
  166. try {
  167. return new RegExp("#/(.+)").exec(decodeURIComponent(location.hash))?.[1];
  168. } catch (error) {}
  169. }
  170. getInitialPathFromCookie() {
  171. let path;
  172. if ((path = Cookies.get("initial_path"))) {
  173. Cookies.expire("initial_path");
  174. return path;
  175. }
  176. }
  177. replaceHash(hash) {
  178. page.replace(
  179. location.pathname + location.search + (hash || ""),
  180. null,
  181. true,
  182. );
  183. }
  184. };