History

Prettier.io

Simon Legner committed 2 years ago

Commit a905bdf1 parent 6b335fb80 files · +13159 −10611Browse files

Unified · Split

80 files
  1. assets/javascripts/app/app.js +191 −90
  2. assets/javascripts/app/db.js +214 −110
  3. assets/javascripts/app/router.js +101 −61
  4. assets/javascripts/app/searcher.js +190 −118
  5. assets/javascripts/app/serviceworker.js +26 −12
  6. assets/javascripts/app/settings.js +111 −80
  7. assets/javascripts/app/shortcuts.js +126 −72
  8. assets/javascripts/app/update_checker.js +18 −10
  9. assets/javascripts/application.js +3 −3
  10. assets/javascripts/collections/collection.js +23 −7
  11. assets/javascripts/collections/docs.js +43 −20
  12. assets/javascripts/collections/entries.js +1 −1
  13. assets/javascripts/collections/types.js +9 −6
  14. assets/javascripts/debug.js +58 −31
  15. assets/javascripts/lib/ajax.js +73 −52
  16. assets/javascripts/lib/cookies_store.js +21 −10
  17. assets/javascripts/lib/events.js +38 −14
  18. assets/javascripts/lib/favicon.js +59 −41
  19. assets/javascripts/lib/license.js +1 −1
  20. assets/javascripts/lib/page.js +153 −82
  21. assets/javascripts/lib/util.js +308 −157
  22. assets/javascripts/models/doc.js +63 −29
  23. assets/javascripts/models/entry.js +51 −48
  24. assets/javascripts/models/model.js +4 −1
  25. assets/javascripts/models/type.js +2 −2
  26. assets/javascripts/templates/base.js +6 −4
  27. assets/javascripts/templates/error_tmpl.js +53 −36
  28. assets/javascripts/templates/notice_tmpl.js +7 −7
  29. assets/javascripts/templates/notif_tmpl.js +69 −34
  30. assets/javascripts/templates/pages/about_tmpl.js +24 −13
  31. assets/javascripts/templates/pages/help_tmpl.js +35 −29
  32. assets/javascripts/templates/pages/offline_tmpl.js +20 −16
  33. assets/javascripts/templates/pages/settings_tmpl.js +50 −19
  34. assets/javascripts/templates/pages/type_tmpl.js +9 −3
  35. assets/javascripts/templates/path_tmpl.js +13 −5
  36. assets/javascripts/templates/sidebar_tmpl.js +75 −37
  37. assets/javascripts/templates/tip_tmpl.js +8 −2
  38. assets/javascripts/tracking.js +43 −20
  39. assets/javascripts/vendor/cookies.js +191 −158
  40. assets/javascripts/vendor/mathml.js +9 −4
  41. assets/javascripts/vendor/prism.js +5777 −5324
  42. assets/javascripts/vendor/raven.js +3290 −2911
  43. assets/javascripts/views/content/content.js +81 −56
  44. assets/javascripts/views/content/entry_page.js +94 −47
  45. assets/javascripts/views/content/offline_page.js +70 −37
  46. assets/javascripts/views/content/root_page.js +14 −14
  47. assets/javascripts/views/content/settings_page.js +51 −41
  48. assets/javascripts/views/content/static_page.js +7 −7
  49. assets/javascripts/views/content/type_page.js +2 −2
  50. assets/javascripts/views/layout/document.js +69 −40
  51. assets/javascripts/views/layout/menu.js +16 −9
  52. assets/javascripts/views/layout/mobile.js +77 −63
  53. assets/javascripts/views/layout/path.js +16 −13
  54. assets/javascripts/views/layout/resizer.js +32 −20
  55. assets/javascripts/views/layout/settings.js +41 −33
  56. assets/javascripts/views/list/list_focus.js +71 −44
  57. assets/javascripts/views/list/list_fold.js +44 −20
  58. assets/javascripts/views/list/list_select.js +25 −14
  59. assets/javascripts/views/list/paginated_list.js +57 −22
  60. assets/javascripts/views/misc/news.js +14 −9
  61. assets/javascripts/views/misc/notice.js +13 −6
  62. assets/javascripts/views/misc/notif.js +22 −16
  63. assets/javascripts/views/misc/tip.js +3 −4
  64. assets/javascripts/views/misc/updates.js +30 −12
  65. assets/javascripts/views/pages/base.js +39 −15
  66. assets/javascripts/views/pages/hidden.js +9 −5
  67. assets/javascripts/views/pages/jquery.js +30 −17
  68. assets/javascripts/views/pages/rdoc.js +11 −7
  69. assets/javascripts/views/pages/sqlite.js +15 −10
  70. assets/javascripts/views/pages/support_tables.js +11 −6
  71. assets/javascripts/views/search/search.js +113 −62
  72. assets/javascripts/views/search/search_scope.js +91 −53
  73. assets/javascripts/views/sidebar/doc_list.js +90 −55
  74. assets/javascripts/views/sidebar/doc_picker.js +62 −32
  75. assets/javascripts/views/sidebar/entry_list.js +10 −5
  76. assets/javascripts/views/sidebar/results.js +49 −28
  77. assets/javascripts/views/sidebar/sidebar.js +79 −43
  78. assets/javascripts/views/sidebar/sidebar_hover.js +35 −26
  79. assets/javascripts/views/sidebar/type_list.js +36 −15
  80. assets/javascripts/views/view.js +64 −23
assets/javascripts/app/app.js+191−90
@@ -14,33 +14,41 @@ this.app = { · 13 unmodified lines
1414 _$$: $$,
1515 _page: page,
1616 collections: {},
17− models: {},
18− templates: {},
19− views: {},
17+ models: {},
18+ templates: {},
19+ views: {},
2020
2121 init() {
22− try { this.initErrorTracking(); } catch (error) {}
23− if (!this.browserCheck()) { return; }
22+ try {
23+ this.initErrorTracking();
24+ } catch (error) {}
25+ if (!this.browserCheck()) {
26+ return;
27+ }
2428
25− this.el = $('._app');
26− this.localStorage = new LocalStorageStore;
27− if (app.ServiceWorker.isEnabled()) { this.serviceWorker = new app.ServiceWorker; }
28− this.settings = new app.Settings;
29+ this.el = $("._app");
30+ this.localStorage = new LocalStorageStore();
31+ if (app.ServiceWorker.isEnabled()) {
32+ this.serviceWorker = new app.ServiceWorker();
33+ }
34+ this.settings = new app.Settings();
2935 this.db = new app.DB();
3036
3137 this.settings.initLayout();
3238
33− this.docs = new app.collections.Docs;
34− this.disabledDocs = new app.collections.Docs;
35− this.entries = new app.collections.Entries;
39+ this.docs = new app.collections.Docs();
40+ this.disabledDocs = new app.collections.Docs();
41+ this.entries = new app.collections.Entries();
3642
37− this.router = new app.Router;
38− this.shortcuts = new app.Shortcuts;
39− this.document = new app.views.Document;
40− if (this.isMobile()) { this.mobile = new app.views.Mobile; }
43+ this.router = new app.Router();
44+ this.shortcuts = new app.Shortcuts();
45+ this.document = new app.views.Document();
46+ if (this.isMobile()) {
47+ this.mobile = new app.views.Mobile();
48+ }
4149
42− if (document.body.hasAttribute('data-doc')) {
43− this.DOC = JSON.parse(document.body.getAttribute('data-doc'));
50+ if (document.body.hasAttribute("data-doc")) {
51+ this.DOC = JSON.parse(document.body.getAttribute("data-doc"));
4452 this.bootOne();
4553 } else if (this.DOCS) {
4654 this.bootAll();
@@ -50,7 +58,9 @@ this.app = { · 3 unmodified lines
5058 },
5159
5260 browserCheck() {
53− if (this.isSupportedBrowser()) { return true; }
61+ if (this.isSupportedBrowser()) {
62+ return true;
63+ }
5464 document.body.innerHTML = app.templates.unsupportedBrowser;
5565 this.hideLoadingScreen();
5666 return false;
@@ -61,7 +71,7 @@ this.app = { · 4 unmodified lines
6171 // from a domain other than our own, because things are likely to break.
6272 // (e.g. cross-domain requests)
6373 if (this.isInvalidLocation()) {
64− new app.views.Notif('InvalidLocation');
74+ new app.views.Notif("InvalidLocation");
6575 } else {
6676 if (this.config.sentry_dsn) {
6777 Raven.config(this.config.sentry_dsn, {
@@ -70,9 +80,12 @@ this.app = { · 2 unmodified lines
7080 includePaths: [/devdocs/],
7181 ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/],
7282 tags: {
73− mode: this.isSingleDoc() ? 'single' : 'full',
83+ mode: this.isSingleDoc() ? "single" : "full",
7484 iframe: (window.top !== window).toString(),
75− electron: (!!__guard__(window.process != null ? window.process.versions : undefined, x => x.electron)).toString()
85+ electron: (!!__guard__(
86+ window.process != null ? window.process.versions : undefined,
87+ (x) => x.electron,
88+ )).toString(),
7689 },
7790 shouldSendCallback: () => {
7891 try {
@@ -89,12 +102,16 @@ this.app = { · 10 unmodified lines
89102 dataCallback(data) {
90103 try {
91104 $.extend(data.user || (data.user = {}), app.settings.dump());
92− if (data.user.docs) { data.user.docs = data.user.docs.split('/'); }
93− if (app.lastIDBTransaction) { data.user.lastIDBTransaction = app.lastIDBTransaction; }
105+ if (data.user.docs) {
106+ data.user.docs = data.user.docs.split("/");
107+ }
108+ if (app.lastIDBTransaction) {
109+ data.user.lastIDBTransaction = app.lastIDBTransaction;
110+ }
94111 data.tags.scriptCount = document.scripts.length;
95112 } catch (error) {}
96113 return data;
97− }
114+ },
98115 }).install();
99116 }
100117 this.previousErrorHandler = onerror;
@@ -106,8 +123,10 @@ this.app = { · 5 unmodified lines
106123 bootOne() {
107124 this.doc = new app.models.Doc(this.DOC);
108125 this.docs.reset([this.doc]);
109− this.doc.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true});
110− new app.views.Notice('singleDoc', this.doc);
126+ this.doc.load(this.start.bind(this), this.onBootError.bind(this), {
127+ readCache: true,
128+ });
129+ new app.views.Notice("singleDoc", this.doc);
111130 delete this.DOC;
112131 },
113132
@@ -117,40 +136,61 @@ this.app = { · 3 unmodified lines
117136 (docs.indexOf(doc.slug) >= 0 ? this.docs : this.disabledDocs).add(doc);
118137 }
119138 this.migrateDocs();
120− this.docs.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true, writeCache: true});
139+ this.docs.load(this.start.bind(this), this.onBootError.bind(this), {
140+ readCache: true,
141+ writeCache: true,
142+ });
121143 delete this.DOCS;
122144 },
123145
124146 start() {
125147 let doc;
126− for (doc of Array.from(this.docs.all())) { this.entries.add(doc.toEntry()); }
127− for (doc of Array.from(this.disabledDocs.all())) { this.entries.add(doc.toEntry()); }
128− for (doc of Array.from(this.docs.all())) { this.initDoc(doc); }
129− this.trigger('ready');
148+ for (doc of Array.from(this.docs.all())) {
149+ this.entries.add(doc.toEntry());
150+ }
151+ for (doc of Array.from(this.disabledDocs.all())) {
152+ this.entries.add(doc.toEntry());
153+ }
154+ for (doc of Array.from(this.docs.all())) {
155+ this.initDoc(doc);
156+ }
157+ this.trigger("ready");
130158 this.router.start();
131159 this.hideLoadingScreen();
132160 setTimeout(() => {
133− if (!this.doc) { this.welcomeBack(); }
134− return this.removeEvent('ready bootError');
135− }
136− , 50);
161+ if (!this.doc) {
162+ this.welcomeBack();
163+ }
164+ return this.removeEvent("ready bootError");
165+ }, 50);
137166 },
138167
139168 initDoc(doc) {
140− for (var type of Array.from(doc.types.all())) { doc.entries.add(type.toEntry()); }
169+ for (var type of Array.from(doc.types.all())) {
170+ doc.entries.add(type.toEntry());
171+ }
141172 this.entries.add(doc.entries.all());
142173 },
143174
144175 migrateDocs() {
145176 let needsSaving;
146177 for (var slug of Array.from(this.settings.getDocs())) {
147− if (!this.docs.findBy('slug', slug)) {var doc;
148−
178+ if (!this.docs.findBy("slug", slug)) {
179+ var doc;
180+
149181 needsSaving = true;
150− if (slug === 'webpack~2') { doc = this.disabledDocs.findBy('slug', 'webpack'); }
151− if (slug === 'angular~4_typescript') { doc = this.disabledDocs.findBy('slug', 'angular'); }
152− if (slug === 'angular~2_typescript') { doc = this.disabledDocs.findBy('slug', 'angular~2'); }
153− if (!doc) { doc = this.disabledDocs.findBy('slug_without_version', slug); }
182+ if (slug === "webpack~2") {
183+ doc = this.disabledDocs.findBy("slug", "webpack");
184+ }
185+ if (slug === "angular~4_typescript") {
186+ doc = this.disabledDocs.findBy("slug", "angular");
187+ }
188+ if (slug === "angular~2_typescript") {
189+ doc = this.disabledDocs.findBy("slug", "angular~2");
190+ }
191+ if (!doc) {
192+ doc = this.disabledDocs.findBy("slug_without_version", slug);
193+ }
154194 if (doc) {
155195 this.disabledDocs.remove(doc);
156196 this.docs.add(doc);
@@ -158,56 +198,70 @@ this.app = { · 1 unmodified line
158198 }
159199 }
160200
161− if (needsSaving) { this.saveDocs(); }
201+ if (needsSaving) {
202+ this.saveDocs();
203+ }
162204 },
163205
164206 enableDoc(doc, _onSuccess, onError) {
165− if (this.docs.contains(doc)) { return; }
207+ if (this.docs.contains(doc)) {
208+ return;
209+ }
166210
167211 const onSuccess = () => {
168− if (this.docs.contains(doc)) { return; }
212+ if (this.docs.contains(doc)) {
213+ return;
214+ }
169215 this.disabledDocs.remove(doc);
170216 this.docs.add(doc);
171217 this.docs.sort();
172218 this.initDoc(doc);
173219 this.saveDocs();
174− if (app.settings.get('autoInstall')) {
220+ if (app.settings.get("autoInstall")) {
175221 doc.install(_onSuccess, onError);
176222 } else {
177223 _onSuccess();
178224 }
179225 };
180226
181− doc.load(onSuccess, onError, {writeCache: true});
227+ doc.load(onSuccess, onError, { writeCache: true });
182228 },
183229
184230 saveDocs() {
185231 this.settings.setDocs(Array.from(this.docs.all()).map((doc) => doc.slug));
186232 this.db.migrate();
187− return (this.serviceWorker != null ? this.serviceWorker.updateInBackground() : undefined);
233+ return this.serviceWorker != null
234+ ? this.serviceWorker.updateInBackground()
235+ : undefined;
188236 },
189237
190238 welcomeBack() {
191− let visitCount = this.settings.get('count');
192− this.settings.set('count', ++visitCount);
193− if (visitCount === 5) { new app.views.Notif('Share', {autoHide: null}); }
239+ let visitCount = this.settings.get("count");
240+ this.settings.set("count", ++visitCount);
241+ if (visitCount === 5) {
242+ new app.views.Notif("Share", { autoHide: null });
243+ }
194244 new app.views.News();
195245 new app.views.Updates();
196− return this.updateChecker = new app.UpdateChecker();
246+ return (this.updateChecker = new app.UpdateChecker());
197247 },
198248
199249 reboot() {
200− if ((location.pathname !== '/') && (location.pathname !== '/settings')) {
250+ if (location.pathname !== "/" && location.pathname !== "/settings") {
201251 window.location = `/#${location.pathname}`;
202252 } else {
203− window.location = '/';
253+ window.location = "/";
204254 }
205255 },
206256
207257 reload() {
208258 this.docs.clearCache();
209259 this.disabledDocs.clearCache();
210− if (this.serviceWorker) { this.serviceWorker.reload(); } else { this.reboot(); }
260+ if (this.serviceWorker) {
261+ this.serviceWorker.reload();
262+ } else {
263+ this.reboot();
264+ }
211265 },
212266
213267 reset() {
@@ -219,11 +273,13 @@ this.app = { · 5 unmodified lines
219273 if (this.serviceWorker != null) {
220274 this.serviceWorker.update();
221275 }
222− window.location = '/';
276+ window.location = "/";
223277 },
224278
225279 showTip(tip) {
226− if (this.isSingleDoc()) { return; }
280+ if (this.isSingleDoc()) {
281+ return;
282+ }
227283 const tips = this.settings.getTips();
228284 if (tips.indexOf(tip) === -1) {
229285 tips.push(tip);
@@ -233,44 +289,61 @@ this.app = { · 3 unmodified lines
233289 },
234290
235291 hideLoadingScreen() {
236− if ($.overlayScrollbarsEnabled()) { document.body.classList.add('_overlay-scrollbars'); }
237− document.documentElement.classList.remove('_booting');
292+ if ($.overlayScrollbarsEnabled()) {
293+ document.body.classList.add("_overlay-scrollbars");
294+ }
295+ document.documentElement.classList.remove("_booting");
238296 },
239297
240298 indexHost() {
241299 // Can't load the index files from the host/CDN when service worker is
242300 // enabled because it doesn't support caching URLs that use CORS.
243− return this.config[this.serviceWorker && this.settings.hasDocs() ? 'index_path' : 'docs_origin'];
301+ return this.config[
302+ this.serviceWorker && this.settings.hasDocs()
303+ ? "index_path"
304+ : "docs_origin"
305+ ];
244306 },
245307
246308 onBootError(...args) {
247− this.trigger('bootError');
309+ this.trigger("bootError");
248310 this.hideLoadingScreen();
249311 },
250312
251313 onQuotaExceeded() {
252− if (this.quotaExceeded) { return; }
314+ if (this.quotaExceeded) {
315+ return;
316+ }
253317 this.quotaExceeded = true;
254− new app.views.Notif('QuotaExceeded', {autoHide: null});
318+ new app.views.Notif("QuotaExceeded", { autoHide: null });
255319 },
256320
257321 onCookieBlocked(key, value, actual) {
258− if (this.cookieBlocked) { return; }
322+ if (this.cookieBlocked) {
323+ return;
324+ }
259325 this.cookieBlocked = true;
260− new app.views.Notif('CookieBlocked', {autoHide: null});
261− Raven.captureMessage(`CookieBlocked/${key}`, {level: 'warning', extra: {value, actual}});
326+ new app.views.Notif("CookieBlocked", { autoHide: null });
327+ Raven.captureMessage(`CookieBlocked/${key}`, {
328+ level: "warning",
329+ extra: { value, actual },
330+ });
262331 },
263332
264333 onWindowError(...args) {
265− if (this.cookieBlocked) { return; }
334+ if (this.cookieBlocked) {
335+ return;
336+ }
266337 if (this.isInjectionError(...Array.from(args || []))) {
267338 this.onInjectionError();
268339 } else if (this.isAppError(...Array.from(args || []))) {
269− if (typeof this.previousErrorHandler === 'function') {
340+ if (typeof this.previousErrorHandler === "function") {
270341 this.previousErrorHandler(...Array.from(args || []));
271342 }
272343 this.hideLoadingScreen();
273− if (!this.errorNotif) { this.errorNotif = new app.views.Notif('Error'); }
344+ if (!this.errorNotif) {
345+ this.errorNotif = new app.views.Notif("Error");
346+ }
274347 this.errorNotif.show();
275348 }
276349 },
@@ -280,75 +353,103 @@ this.app = { · 3 unmodified lines
280353 this.injectionError = true;
281354 alert(`\
282355 JavaScript code has been injected in the page which prevents DevDocs from running correctly.
283−Please check your browser extensions/addons. `
284− );
285− Raven.captureMessage('injection error', {level: 'info'});
356+Please check your browser extensions/addons. `);
357+ Raven.captureMessage("injection error", { level: "info" });
286358 }
287359 },
288360
289361 isInjectionError() {
290362 // Some browser extensions expect the entire web to use jQuery.
291363 // I gave up trying to fight back.
292− return (window.$ !== app._$) || (window.$$ !== app._$$) || (window.page !== app._page) || (typeof $.empty !== 'function') || (typeof page.show !== 'function');
364+ return (
365+ window.$ !== app._$ ||
366+ window.$$ !== app._$$ ||
367+ window.page !== app._page ||
368+ typeof $.empty !== "function" ||
369+ typeof page.show !== "function"
370+ );
293371 },
294372
295373 isAppError(error, file) {
296374 // Ignore errors from external scripts.
297− return file && (file.indexOf('devdocs') !== -1) && (file.indexOf('.js') === (file.length - 3));
375+ return (
376+ file &&
377+ file.indexOf("devdocs") !== -1 &&
378+ file.indexOf(".js") === file.length - 3
379+ );
298380 },
299381
300382 isSupportedBrowser() {
301383 try {
302384 const features = {
303− bind: !!Function.prototype.bind,
304− pushState: !!history.pushState,
305− matchMedia: !!window.matchMedia,
385+ bind: !!Function.prototype.bind,
386+ pushState: !!history.pushState,
387+ matchMedia: !!window.matchMedia,
306388 insertAdjacentHTML: !!document.body.insertAdjacentHTML,
307− defaultPrevented: document.createEvent('CustomEvent').defaultPrevented === false,
308− cssVariables: !!__guardMethod__(CSS, 'supports', o => o.supports('(--t: 0)'))
389+ defaultPrevented:
390+ document.createEvent("CustomEvent").defaultPrevented === false,
391+ cssVariables: !!__guardMethod__(CSS, "supports", (o) =>
392+ o.supports("(--t: 0)"),
393+ ),
309394 };
310395
311396 for (var key in features) {
312397 var value = features[key];
313398 if (!value) {
314− Raven.captureMessage(`unsupported/${key}`, {level: 'info'});
399+ Raven.captureMessage(`unsupported/${key}`, { level: "info" });
315400 return false;
316401 }
317402 }
318403
319404 return true;
320405 } catch (error) {
321− Raven.captureMessage('unsupported/exception', {level: 'info', extra: { error }});
406+ Raven.captureMessage("unsupported/exception", {
407+ level: "info",
408+ extra: { error },
409+ });
322410 return false;
323411 }
324412 },
325413
326414 isSingleDoc() {
327− return document.body.hasAttribute('data-doc');
415+ return document.body.hasAttribute("data-doc");
328416 },
329417
330418 isMobile() {
331− return this._isMobile != null ? this._isMobile : (this._isMobile = app.views.Mobile.detect());
419+ return this._isMobile != null
420+ ? this._isMobile
421+ : (this._isMobile = app.views.Mobile.detect());
332422 },
333423
334424 isAndroidWebview() {
335− return this._isAndroidWebview != null ? this._isAndroidWebview : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview());
425+ return this._isAndroidWebview != null
426+ ? this._isAndroidWebview
427+ : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview());
336428 },
337429
338430 isInvalidLocation() {
339− return (this.config.env === 'production') && (location.host.indexOf(app.config.production_host) !== 0);
340− }
431+ return (
432+ this.config.env === "production" &&
433+ location.host.indexOf(app.config.production_host) !== 0
434+ );
435+ },
341436 };
342437
343438 $.extend(app, Events);
344439
345440 function __guard__(value, transform) {
346− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
441+ return typeof value !== "undefined" && value !== null
442+ ? transform(value)
443+ : undefined;
347444 }
348445 function __guardMethod__(obj, methodName, transform) {
349− if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') {
446+ if (
447+ typeof obj !== "undefined" &&
448+ obj !== null &&
449+ typeof obj[methodName] === "function"
450+ ) {
350451 return transform(obj, methodName);
351452 } else {
352453 return undefined;
353454 }
354−}
455+}
assets/javascripts/app/db.js+214−110
@@ -9,12 +9,12 @@ · 8 unmodified lines
99 * DS207: Consider shorter variations of null checks
1010 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1111 */
12−(function() {
12+(function () {
1313 let NAME = undefined;
1414 let VERSION = undefined;
1515 const Cls = (app.DB = class DB {
1616 static initClass() {
17− NAME = 'docs';
17+ NAME = "docs";
1818 VERSION = 15;
1919 }
2020
@@ -29,18 +29,27 @@ · 8 unmodified lines
2929 }
3030
3131 db(fn) {
32− if (!this.useIndexedDB) { return fn(); }
33− if (fn) { this.callbacks.push(fn); }
34− if (this.open) { return; }
32+ if (!this.useIndexedDB) {
33+ return fn();
34+ }
35+ if (fn) {
36+ this.callbacks.push(fn);
37+ }
38+ if (this.open) {
39+ return;
40+ }
3541
3642 try {
3743 this.open = true;
38− const req = indexedDB.open(NAME, (VERSION * this.versionMultipler) + this.userVersion());
44+ const req = indexedDB.open(
45+ NAME,
46+ VERSION * this.versionMultipler + this.userVersion(),
47+ );
3948 req.onsuccess = this.onOpenSuccess;
4049 req.onerror = this.onOpenError;
4150 req.onupgradeneeded = this.onUpgradeNeeded;
4251 } catch (error) {
43− this.fail('exception', error);
52+ this.fail("exception", error);
4453 }
4554 }
4655
@@ -49,13 +58,17 @@ · 2 unmodified lines
4958 const db = event.target.result;
5059
5160 if (db.objectStoreNames.length === 0) {
52− try { db.close(); } catch (error1) {}
61+ try {
62+ db.close();
63+ } catch (error1) {}
5364 this.open = false;
54− this.fail('empty');
55− } else if (error = this.buggyIDB(db)) {
56− try { db.close(); } catch (error2) {}
65+ this.fail("empty");
66+ } else if ((error = this.buggyIDB(db))) {
67+ try {
68+ db.close();
69+ } catch (error2) {}
5770 this.open = false;
58− this.fail('buggy', error);
71+ this.fail("buggy", error);
5972 } else {
6073 this.runCallbacks(db);
6174 this.open = false;
@@ -66,36 +79,43 @@ · 4 unmodified lines
6679 onOpenError(event) {
6780 event.preventDefault();
6881 this.open = false;
69− const {
70− error
71− } = event.target;
82+ const { error } = event.target;
7283
7384 switch (error.name) {
74− case 'QuotaExceededError':
85+ case "QuotaExceededError":
7586 this.onQuotaExceededError();
7687 break;
77− case 'VersionError':
88+ case "VersionError":
7889 this.onVersionError();
7990 break;
80− case 'InvalidStateError':
81− this.fail('private_mode');
91+ case "InvalidStateError":
92+ this.fail("private_mode");
8293 break;
8394 default:
84− this.fail('cant_open', error);
95+ this.fail("cant_open", error);
8596 }
8697 }
8798
8899 fail(reason, error) {
89100 this.cachedDocs = null;
90101 this.useIndexedDB = false;
91− if (!this.reason) { this.reason = reason; }
92− if (!this.error) { this.error = error; }
93− if (error) { if (typeof console.error === 'function') {
94− console.error('IDB error', error);
95− } }
102+ if (!this.reason) {
103+ this.reason = reason;
104+ }
105+ if (!this.error) {
106+ this.error = error;
107+ }
108+ if (error) {
109+ if (typeof console.error === "function") {
110+ console.error("IDB error", error);
111+ }
112+ }
96113 this.runCallbacks();
97− if (error && (reason === 'cant_open')) {
98− Raven.captureMessage(`${error.name}: ${error.message}`, {level: 'warning', fingerprint: [error.name]});
114+ if (error && reason === "cant_open") {
115+ Raven.captureMessage(`${error.name}: ${error.message}`, {
116+ level: "warning",
117+ fingerprint: [error.name],
118+ });
99119 }
100120 }
101121
@@ -103,34 +123,39 @@ · 1 unmodified line
103123 this.reset();
104124 this.db();
105125 app.onQuotaExceeded();
106− Raven.captureMessage('QuotaExceededError', {level: 'warning'});
126+ Raven.captureMessage("QuotaExceededError", { level: "warning" });
107127 }
108128
109129 onVersionError() {
110130 const req = indexedDB.open(NAME);
111− req.onsuccess = event => {
131+ req.onsuccess = (event) => {
112132 return this.handleVersionMismatch(event.target.result.version);
113133 };
114− req.onerror = function(event) {
134+ req.onerror = function (event) {
115135 event.preventDefault();
116− return this.fail('cant_open', error);
136+ return this.fail("cant_open", error);
117137 };
118138 }
119139
120140 handleVersionMismatch(actualVersion) {
121141 if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) {
122− this.fail('version');
142+ this.fail("version");
123143 } else {
124− this.setUserVersion(actualVersion - (VERSION * this.versionMultipler));
144+ this.setUserVersion(actualVersion - VERSION * this.versionMultipler);
125145 this.db();
126146 }
127147 }
128148
129149 buggyIDB(db) {
130− if (this.checkedBuggyIDB) { return; }
150+ if (this.checkedBuggyIDB) {
151+ return;
152+ }
131153 this.checkedBuggyIDB = true;
132154 try {
133− this.idbTransaction(db, {stores: $.makeArray(db.objectStoreNames).slice(0, 2), mode: 'readwrite'}).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937
155+ this.idbTransaction(db, {
156+ stores: $.makeArray(db.objectStoreNames).slice(0, 2),
157+ mode: "readwrite",
158+ }).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937
134159 return;
135160 } catch (error) {
136161 return error;
@@ -139,53 +164,72 @@ · 2 unmodified lines
139164
140165 runCallbacks(db) {
141166 let fn;
142− while ((fn = this.callbacks.shift())) { fn(db); }
167+ while ((fn = this.callbacks.shift())) {
168+ fn(db);
169+ }
143170 }
144171
145172 onUpgradeNeeded(event) {
146173 let db;
147− if (!(db = event.target.result)) { return; }
174+ if (!(db = event.target.result)) {
175+ return;
176+ }
148177
149178 const objectStoreNames = $.makeArray(db.objectStoreNames);
150179
151− if (!$.arrayDelete(objectStoreNames, 'docs')) {
152− try { db.createObjectStore('docs'); } catch (error) {}
180+ if (!$.arrayDelete(objectStoreNames, "docs")) {
181+ try {
182+ db.createObjectStore("docs");
183+ } catch (error) {}
153184 }
154185
155186 for (var doc of Array.from(app.docs.all())) {
156187 if (!$.arrayDelete(objectStoreNames, doc.slug)) {
157− try { db.createObjectStore(doc.slug); } catch (error1) {}
188+ try {
189+ db.createObjectStore(doc.slug);
190+ } catch (error1) {}
158191 }
159192 }
160193
161194 for (var name of Array.from(objectStoreNames)) {
162− try { db.deleteObjectStore(name); } catch (error2) {}
195+ try {
196+ db.deleteObjectStore(name);
197+ } catch (error2) {}
163198 }
164199 }
165200
166201 store(doc, data, onSuccess, onError, _retry) {
167− if (_retry == null) { _retry = true; }
168− this.db(db => {
202+ if (_retry == null) {
203+ _retry = true;
204+ }
205+ this.db((db) => {
169206 if (!db) {
170207 onError();
171208 return;
172209 }
173210
174− const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false});
211+ const txn = this.idbTransaction(db, {
212+ stores: ["docs", doc.slug],
213+ mode: "readwrite",
214+ ignoreError: false,
215+ });
175216 txn.oncomplete = () => {
176217 if (this.cachedDocs != null) {
177218 this.cachedDocs[doc.slug] = doc.mtime;
178219 }
179220 onSuccess();
180221 };
181− txn.onerror = event => {
222+ txn.onerror = (event) => {
182223 event.preventDefault();
183− if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) {
224+ if (
225+ (txn.error != null ? txn.error.name : undefined) ===
226+ "NotFoundError" &&
227+ _retry
228+ ) {
184229 this.migrate();
185230 setTimeout(() => {
186231 return this.store(doc, data, onSuccess, onError, false);
187− }
188− , 0);
232+ }, 0);
189233 } else {
190234 onError(event);
191235 }
@@ -193,42 +237,54 @@ · 1 unmodified line
193237
194238 let store = txn.objectStore(doc.slug);
195239 store.clear();
196− for (var path in data) { var content = data[path]; store.add(content, path); }
240+ for (var path in data) {
241+ var content = data[path];
242+ store.add(content, path);
243+ }
197244
198− store = txn.objectStore('docs');
245+ store = txn.objectStore("docs");
199246 store.put(doc.mtime, doc.slug);
200247 });
201248 }
202249
203250 unstore(doc, onSuccess, onError, _retry) {
204− if (_retry == null) { _retry = true; }
205− this.db(db => {
251+ if (_retry == null) {
252+ _retry = true;
253+ }
254+ this.db((db) => {
206255 if (!db) {
207256 onError();
208257 return;
209258 }
210259
211− const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false});
260+ const txn = this.idbTransaction(db, {
261+ stores: ["docs", doc.slug],
262+ mode: "readwrite",
263+ ignoreError: false,
264+ });
212265 txn.oncomplete = () => {
213266 if (this.cachedDocs != null) {
214267 delete this.cachedDocs[doc.slug];
215268 }
216269 onSuccess();
217270 };
218− txn.onerror = function(event) {
271+ txn.onerror = function (event) {
219272 event.preventDefault();
220− if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) {
273+ if (
274+ (txn.error != null ? txn.error.name : undefined) ===
275+ "NotFoundError" &&
276+ _retry
277+ ) {
221278 this.migrate();
222279 setTimeout(() => {
223280 return this.unstore(doc, onSuccess, onError, false);
224− }
225− , 0);
281+ }, 0);
226282 } else {
227283 onError(event);
228284 }
229285 };
230286
231− let store = txn.objectStore('docs');
287+ let store = txn.objectStore("docs");
232288 store.delete(doc.slug);
233289
234290 store = txn.objectStore(doc.slug);
@@ -243,20 +299,23 @@ · 8 unmodified lines
243299 return;
244300 }
245301
246− this.db(db => {
302+ this.db((db) => {
247303 if (!db) {
248304 fn(false);
249305 return;
250306 }
251307
252− const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
253− const store = txn.objectStore('docs');
308+ const txn = this.idbTransaction(db, {
309+ stores: ["docs"],
310+ mode: "readonly",
311+ });
312+ const store = txn.objectStore("docs");
254313
255314 const req = store.get(doc.slug);
256− req.onsuccess = function() {
315+ req.onsuccess = function () {
257316 fn(req.result);
258317 };
259− req.onerror = function(event) {
318+ req.onerror = function (event) {
260319 event.preventDefault();
261320 fn(false);
262321 };
@@ -264,36 +323,41 @@ · 1 unmodified line
264323 }
265324
266325 cachedVersion(doc) {
267− if (!this.cachedDocs) { return; }
326+ if (!this.cachedDocs) {
327+ return;
328+ }
268329 return this.cachedDocs[doc.slug] || false;
269330 }
270331
271332 versions(docs, fn) {
272333 let versions;
273− if (versions = this.cachedVersions(docs)) {
334+ if ((versions = this.cachedVersions(docs))) {
274335 fn(versions);
275336 return;
276337 }
277338
278− return this.db(db => {
339+ return this.db((db) => {
279340 if (!db) {
280341 fn(false);
281342 return;
282343 }
283344
284− const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
285− txn.oncomplete = function() {
345+ const txn = this.idbTransaction(db, {
346+ stores: ["docs"],
347+ mode: "readonly",
348+ });
349+ txn.oncomplete = function () {
286350 fn(result);
287351 };
288− const store = txn.objectStore('docs');
352+ const store = txn.objectStore("docs");
289353 var result = {};
290354
291− docs.forEach(function(doc) {
355+ docs.forEach(function (doc) {
292356 const req = store.get(doc.slug);
293− req.onsuccess = function() {
357+ req.onsuccess = function () {
294358 result[doc.slug] = req.result;
295359 };
296− req.onerror = function(event) {
360+ req.onerror = function (event) {
297361 event.preventDefault();
298362 result[doc.slug] = false;
299363 };
@@ -302,9 +366,13 @@ · 2 unmodified lines
302366 }
303367
304368 cachedVersions(docs) {
305− if (!this.cachedDocs) { return; }
369+ if (!this.cachedDocs) {
370+ return;
371+ }
306372 const result = {};
307− for (var doc of Array.from(docs)) { result[doc.slug] = this.cachedVersion(doc); }
373+ for (var doc of Array.from(docs)) {
374+ result[doc.slug] = this.cachedVersion(doc);
375+ }
308376 return result;
309377 }
310378
@@ -320,14 +388,14 @@ · 9 unmodified lines
320388 loadWithXHR(entry, onSuccess, onError) {
321389 return ajax({
322390 url: entry.fileUrl(),
323− dataType: 'html',
391+ dataType: "html",
324392 success: onSuccess,
325− error: onError
393+ error: onError,
326394 });
327395 }
328396
329397 loadWithIDB(entry, onSuccess, onError) {
330− return this.db(db => {
398+ return this.db((db) => {
331399 if (!db) {
332400 onError();
333401 return;
@@ -339,14 +407,21 @@ · 5 unmodified lines
339407 return;
340408 }
341409
342− const txn = this.idbTransaction(db, {stores: [entry.doc.slug], mode: 'readonly'});
410+ const txn = this.idbTransaction(db, {
411+ stores: [entry.doc.slug],
412+ mode: "readonly",
413+ });
343414 const store = txn.objectStore(entry.doc.slug);
344415
345416 const req = store.get(entry.dbPath());
346− req.onsuccess = function() {
347− if (req.result) { onSuccess(req.result); } else { onError(); }
417+ req.onsuccess = function () {
418+ if (req.result) {
419+ onSuccess(req.result);
420+ } else {
421+ onError();
422+ }
348423 };
349− req.onerror = function(event) {
424+ req.onerror = function (event) {
350425 event.preventDefault();
351426 onError();
352427 };
@@ -355,31 +430,38 @@ · 2 unmodified lines
355430 }
356431
357432 loadDocsCache(db) {
358− if (this.cachedDocs) { return; }
433+ if (this.cachedDocs) {
434+ return;
435+ }
359436 this.cachedDocs = {};
360437
361− const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'});
438+ const txn = this.idbTransaction(db, {
439+ stores: ["docs"],
440+ mode: "readonly",
441+ });
362442 txn.oncomplete = () => {
363443 setTimeout(this.checkForCorruptedDocs, 50);
364444 };
365445
366− const req = txn.objectStore('docs').openCursor();
367− req.onsuccess = event => {
446+ const req = txn.objectStore("docs").openCursor();
447+ req.onsuccess = (event) => {
368448 let cursor;
369− if (!(cursor = event.target.result)) { return; }
449+ if (!(cursor = event.target.result)) {
450+ return;
451+ }
370452 this.cachedDocs[cursor.key] = cursor.value;
371453 cursor.continue();
372454 };
373− req.onerror = function(event) {
455+ req.onerror = function (event) {
374456 event.preventDefault();
375457 };
376458 }
377459
378460 checkForCorruptedDocs() {
379− this.db(db => {
461+ this.db((db) => {
380462 let slug;
381463 this.corruptedDocs = [];
382− const docs = ((() => {
464+ const docs = (() => {
383465 const result = [];
384466 for (var key in this.cachedDocs) {
385467 var value = this.cachedDocs[key];
@@ -388,11 +470,13 @@ · 2 unmodified lines
388470 }
389471 }
390472 return result;
391− })());
392− if (docs.length === 0) { return; }
473+ })();
474+ if (docs.length === 0) {
475+ return;
476+ }
393477
394478 for (slug of Array.from(docs)) {
395− if (!app.docs.findBy('slug', slug)) {
479+ if (!app.docs.findBy("slug", slug)) {
396480 this.corruptedDocs.push(slug);
397481 }
398482 }
@@ -406,46 +490,64 @@ · 7 unmodified lines
406490 return;
407491 }
408492
409− const txn = this.idbTransaction(db, {stores: docs, mode: 'readonly', ignoreError: false});
493+ const txn = this.idbTransaction(db, {
494+ stores: docs,
495+ mode: "readonly",
496+ ignoreError: false,
497+ });
410498 txn.oncomplete = () => {
411− if (this.corruptedDocs.length > 0) { setTimeout(this.deleteCorruptedDocs, 0); }
499+ if (this.corruptedDocs.length > 0) {
500+ setTimeout(this.deleteCorruptedDocs, 0);
501+ }
412502 };
413503
414504 for (var doc of Array.from(docs)) {
415− txn.objectStore(doc).get('index').onsuccess = event => {
416− if (!event.target.result) { this.corruptedDocs.push(event.target.source.name); }
505+ txn.objectStore(doc).get("index").onsuccess = (event) => {
506+ if (!event.target.result) {
507+ this.corruptedDocs.push(event.target.source.name);
508+ }
417509 };
418510 }
419511 });
420512 }
421513
422514 deleteCorruptedDocs() {
423− this.db(db => {
515+ this.db((db) => {
424516 let doc;
425− const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readwrite', ignoreError: false});
426− const store = txn.objectStore('docs');
517+ const txn = this.idbTransaction(db, {
518+ stores: ["docs"],
519+ mode: "readwrite",
520+ ignoreError: false,
521+ });
522+ const store = txn.objectStore("docs");
427523 while ((doc = this.corruptedDocs.pop())) {
428524 this.cachedDocs[doc] = false;
429525 store.delete(doc);
430526 }
431527 });
432− Raven.captureMessage('corruptedDocs', {level: 'info', extra: { docs: this.corruptedDocs.join(',') }});
528+ Raven.captureMessage("corruptedDocs", {
529+ level: "info",
530+ extra: { docs: this.corruptedDocs.join(",") },
531+ });
433532 }
434533
435534 shouldLoadWithIDB(entry) {
436− return this.useIndexedDB && (!this.cachedDocs || this.cachedDocs[entry.doc.slug]);
535+ return (
536+ this.useIndexedDB &&
537+ (!this.cachedDocs || this.cachedDocs[entry.doc.slug])
538+ );
437539 }
438540
439541 idbTransaction(db, options) {
440542 app.lastIDBTransaction = [options.stores, options.mode];
441543 const txn = db.transaction(options.stores, options.mode);
442544 if (options.ignoreError !== false) {
443− txn.onerror = function(event) {
545+ txn.onerror = function (event) {
444546 event.preventDefault();
445547 };
446548 }
447549 if (options.ignoreAbort !== false) {
448− txn.onabort = function(event) {
550+ txn.onabort = function (event) {
449551 event.preventDefault();
450552 };
451553 }
@@ -453,9 +555,11 @@ · 1 unmodified line
453555 }
454556
455557 reset() {
456− try { if (typeof indexedDB !== 'undefined' && indexedDB !== null) {
457− indexedDB.deleteDatabase(NAME);
458− } } catch (error) {}
558+ try {
559+ if (typeof indexedDB !== "undefined" && indexedDB !== null) {
560+ indexedDB.deleteDatabase(NAME);
561+ }
562+ } catch (error) {}
459563 }
460564
461565 useIndexedDB() {
@@ -463,7 +567,7 @@ · 1 unmodified line
463567 if (!app.isSingleDoc() && window.indexedDB) {
464568 return true;
465569 } else {
466− this.reason = 'not_supported';
570+ this.reason = "not_supported";
467571 return false;
468572 }
469573 } catch (error) {
@@ -472,15 +576,15 @@ · 2 unmodified lines
472576 }
473577
474578 migrate() {
475− app.settings.set('schema', this.userVersion() + 1);
579+ app.settings.set("schema", this.userVersion() + 1);
476580 }
477581
478582 setUserVersion(version) {
479− app.settings.set('schema', version);
583+ app.settings.set("schema", version);
480584 }
481585
482586 userVersion() {
483− return app.settings.get('schema');
587+ return app.settings.get("schema");
484588 }
485589 });
486590 Cls.initClass();
assets/javascripts/app/router.js+101−61
@@ -12,19 +12,19 @@ · 11 unmodified lines
1212 const Cls = (app.Router = class Router {
1313 static initClass() {
1414 $.extend(this.prototype, Events);
15−
15+
1616 this.routes = [
17− ['*', 'before' ],
18− ['/', 'root' ],
19− ['/settings', 'settings' ],
20− ['/offline', 'offline' ],
21− ['/about', 'about' ],
22− ['/news', 'news' ],
23− ['/help', 'help' ],
24− ['/:doc-:type/', 'type' ],
25− ['/:doc/', 'doc' ],
26− ['/:doc/:path(*)', 'entry' ],
27− ['*', 'notFound' ]
17+ ["*", "before"],
18+ ["/", "root"],
19+ ["/settings", "settings"],
20+ ["/offline", "offline"],
21+ ["/about", "about"],
22+ ["/news", "news"],
23+ ["/help", "help"],
24+ ["/:doc-:type/", "type"],
25+ ["/:doc/", "doc"],
26+ ["/:doc/:path(*)", "entry"],
27+ ["*", "notFound"],
2828 ];
2929 }
3030
@@ -45,16 +45,16 @@ const Cls = (app.Router = class Router { · 14 unmodified lines
4545
4646 triggerRoute(name) {
4747 this.trigger(name, this.context);
48− this.trigger('after', name, this.context);
48+ this.trigger("after", name, this.context);
4949 }
5050
5151 before(context, next) {
5252 let res;
5353 const previousContext = this.context;
5454 this.context = context;
55− this.trigger('before', context);
55+ this.trigger("before", context);
5656
57− if (res = next()) {
57+ if ((res = next())) {
5858 this.context = previousContext;
5959 return res;
6060 } else {
@@ -64,10 +64,14 @@ const Cls = (app.Router = class Router { · 3 unmodified lines
6464
6565 doc(context, next) {
6666 let doc;
67− if (doc = app.docs.findBySlug(context.params.doc) || app.disabledDocs.findBySlug(context.params.doc)) {
67+ if (
68+ (doc =
69+ app.docs.findBySlug(context.params.doc) ||
70+ app.disabledDocs.findBySlug(context.params.doc))
71+ ) {
6872 context.doc = doc;
6973 context.entry = doc.toEntry();
70− this.triggerRoute('entry');
74+ this.triggerRoute("entry");
7175 return;
7276 } else {
7377 return next();
@@ -78,10 +82,13 @@ const Cls = (app.Router = class Router { · 4 unmodified lines
7882 let type;
7983 const doc = app.docs.findBySlug(context.params.doc);
8084
81− if (type = doc != null ? doc.types.findBy('slug', context.params.type) : undefined) {
85+ if (
86+ (type =
87+ doc != null ? doc.types.findBy("slug", context.params.type) : undefined)
88+ ) {
8289 context.doc = doc;
8390 context.type = type;
84− this.triggerRoute('type');
91+ this.triggerRoute("type");
8592 return;
8693 } else {
8794 return next();
@@ -91,86 +98,110 @@ const Cls = (app.Router = class Router { · 3 unmodified lines
9198 entry(context, next) {
9299 let entry;
93100 const doc = app.docs.findBySlug(context.params.doc);
94− if (!doc) { return next(); }
95− let {
96− path
97− } = context.params;
98− const {
99− hash
100− } = context;
101−
102− if (entry = doc.findEntryByPathAndHash(path, hash)) {
101+ if (!doc) {
102+ return next();
103+ }
104+ let { path } = context.params;
105+ const { hash } = context;
106+
107+ if ((entry = doc.findEntryByPathAndHash(path, hash))) {
103108 context.doc = doc;
104109 context.entry = entry;
105− this.triggerRoute('entry');
110+ this.triggerRoute("entry");
106111 return;
107− } else if (path.slice(-6) === '/index') {
112+ } else if (path.slice(-6) === "/index") {
108113 path = path.substr(0, path.length - 6);
109− if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); }
114+ if ((entry = doc.findEntryByPathAndHash(path, hash))) {
115+ return entry.fullPath();
116+ }
110117 } else {
111118 path = `${path}/index`;
112− if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); }
119+ if ((entry = doc.findEntryByPathAndHash(path, hash))) {
120+ return entry.fullPath();
121+ }
113122 }
114123
115124 return next();
116125 }
117126
118127 root() {
119− if (app.isSingleDoc()) { return '/'; }
120− this.triggerRoute('root');
128+ if (app.isSingleDoc()) {
129+ return "/";
130+ }
131+ this.triggerRoute("root");
121132 }
122133
123134 settings(context) {
124− if (app.isSingleDoc()) { return `/#/${context.path}`; }
125− this.triggerRoute('settings');
135+ if (app.isSingleDoc()) {
136+ return `/#/${context.path}`;
137+ }
138+ this.triggerRoute("settings");
126139 }
127140
128− offline(context){
129− if (app.isSingleDoc()) { return `/#/${context.path}`; }
130− this.triggerRoute('offline');
141+ offline(context) {
142+ if (app.isSingleDoc()) {
143+ return `/#/${context.path}`;
144+ }
145+ this.triggerRoute("offline");
131146 }
132147
133148 about(context) {
134− if (app.isSingleDoc()) { return `/#/${context.path}`; }
135− context.page = 'about';
136− this.triggerRoute('page');
149+ if (app.isSingleDoc()) {
150+ return `/#/${context.path}`;
151+ }
152+ context.page = "about";
153+ this.triggerRoute("page");
137154 }
138155
139156 news(context) {
140− if (app.isSingleDoc()) { return `/#/${context.path}`; }
141− context.page = 'news';
142− this.triggerRoute('page');
157+ if (app.isSingleDoc()) {
158+ return `/#/${context.path}`;
159+ }
160+ context.page = "news";
161+ this.triggerRoute("page");
143162 }
144163
145164 help(context) {
146− if (app.isSingleDoc()) { return `/#/${context.path}`; }
147− context.page = 'help';
148− this.triggerRoute('page');
165+ if (app.isSingleDoc()) {
166+ return `/#/${context.path}`;
167+ }
168+ context.page = "help";
169+ this.triggerRoute("page");
149170 }
150171
151172 notFound(context) {
152− this.triggerRoute('notFound');
173+ this.triggerRoute("notFound");
153174 }
154175
155176 isIndex() {
156− return ((this.context != null ? this.context.path : undefined) === '/') || (app.isSingleDoc() && __guard__(this.context != null ? this.context.entry : undefined, x => x.isIndex()));
177+ return (
178+ (this.context != null ? this.context.path : undefined) === "/" ||
179+ (app.isSingleDoc() &&
180+ __guard__(this.context != null ? this.context.entry : undefined, (x) =>
181+ x.isIndex(),
182+ ))
183+ );
157184 }
158185
159186 isSettings() {
160− return (this.context != null ? this.context.path : undefined) === '/settings';
187+ return (
188+ (this.context != null ? this.context.path : undefined) === "/settings"
189+ );
161190 }
162191
163192 setInitialPath() {
164193 // Remove superfluous forward slashes at the beginning of the path
165194 let path;
166− if ((path = location.pathname.replace(/^\/{2,}/g, '/')) !== location.pathname) {
195+ if (
196+ (path = location.pathname.replace(/^\/{2,}/g, "/")) !== location.pathname
197+ ) {
167198 page.replace(path + location.search + location.hash, null, true);
168199 }
169200
170− if (location.pathname === '/') {
171− if (path = this.getInitialPathFromHash()) {
201+ if (location.pathname === "/") {
202+ if ((path = this.getInitialPathFromHash())) {
172203 page.replace(path + location.search, null, true);
173− } else if (path = this.getInitialPathFromCookie()) {
204+ } else if ((path = this.getInitialPathFromCookie())) {
174205 page.replace(path + location.search + location.hash, null, true);
175206 }
176207 }
@@ -178,24 +209,33 @@ const Cls = (app.Router = class Router { · 1 unmodified line
178209
179210 getInitialPathFromHash() {
180211 try {
181− return __guard__((new RegExp("#/(.+)")).exec(decodeURIComponent(location.hash)), x => x[1]);
212+ return __guard__(
213+ new RegExp("#/(.+)").exec(decodeURIComponent(location.hash)),
214+ (x) => x[1],
215+ );
182216 } catch (error) {}
183217 }
184218
185219 getInitialPathFromCookie() {
186220 let path;
187− if (path = Cookies.get('initial_path')) {
188− Cookies.expire('initial_path');
221+ if ((path = Cookies.get("initial_path"))) {
222+ Cookies.expire("initial_path");
189223 return path;
190224 }
191225 }
192226
193227 replaceHash(hash) {
194− page.replace(location.pathname + location.search + (hash || ''), null, true);
228+ page.replace(
229+ location.pathname + location.search + (hash || ""),
230+ null,
231+ true,
232+ );
195233 }
196234 });
197235 Cls.initClass();
198236
199237 function __guard__(value, transform) {
200− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
201−}
238+ return typeof value !== "undefined" && value !== null
239+ ? transform(value)
240+ : undefined;
241+}
assets/javascripts/app/searcher.js+190−118
@@ -16,116 +16,150 @@ · 15 unmodified lines
1616 // Match functions
1717 //
1818
19−let fuzzyRegexp, i, index, lastIndex, match, matcher, matchIndex, matchLength, queryLength, score, separators, value, valueLength;
20−const SEPARATOR = '.';
19+let fuzzyRegexp,
20+ i,
21+ index,
22+ lastIndex,
23+ match,
24+ matcher,
25+ matchIndex,
26+ matchLength,
27+ queryLength,
28+ score,
29+ separators,
30+ value,
31+ valueLength;
32+const SEPARATOR = ".";
2133
2234 let query =
23−(queryLength =
24−(value =
25−(valueLength =
26−(matcher = // current match function
27−(fuzzyRegexp = // query fuzzy regexp
28−(index = // position of the query in the string being matched
29−(lastIndex = // last position of the query in the string being matched
30−(match = // regexp match data
31−(matchIndex =
32−(matchLength =
33−(score = // score for the current match
34−(separators = // counter
35−(i = null))))))))))))); // cursor
35+ (queryLength =
36+ value =
37+ valueLength =
38+ matcher = // current match function
39+ fuzzyRegexp = // query fuzzy regexp
40+ index = // position of the query in the string being matched
41+ lastIndex = // last position of the query in the string being matched
42+ match = // regexp match data
43+ matchIndex =
44+ matchLength =
45+ score = // score for the current match
46+ separators = // counter
47+ i =
48+ null); // cursor
3649
3750 function exactMatch() {
38−index = value.indexOf(query);
39−if (!(index >= 0)) { return; }
51+ index = value.indexOf(query);
52+ if (!(index >= 0)) {
53+ return;
54+ }
4055
41−lastIndex = value.lastIndexOf(query);
56+ lastIndex = value.lastIndexOf(query);
4257
43−if (index !== lastIndex) {
44− return Math.max(scoreExactMatch(), ((index = lastIndex) && scoreExactMatch()) || 0);
45−} else {
46− return scoreExactMatch();
47−}
58+ if (index !== lastIndex) {
59+ return Math.max(
60+ scoreExactMatch(),
61+ ((index = lastIndex) && scoreExactMatch()) || 0,
62+ );
63+ } else {
64+ return scoreExactMatch();
65+ }
4866 }
4967
5068 function scoreExactMatch() {
51−// Remove one point for each unmatched character.
52−score = 100 - (valueLength - queryLength);
53−
54−if (index > 0) {
55− // If the character preceding the query is a dot, assign the same score
56− // as if the query was found at the beginning of the string, minus one.
57− if (value.charAt(index - 1) === SEPARATOR) {
58− score += index - 1;
59− // Don't match a single-character query unless it's found at the beginning
60− // of the string or is preceded by a dot.
61− } else if (queryLength === 1) {
62− return;
63− // (1) Remove one point for each unmatched character up to the nearest
64− // preceding dot or the beginning of the string.
65− // (2) Remove one point for each unmatched character following the query.
66− } else {
69+ // Remove one point for each unmatched character.
70+ score = 100 - (valueLength - queryLength);
71+
72+ if (index > 0) {
73+ // If the character preceding the query is a dot, assign the same score
74+ // as if the query was found at the beginning of the string, minus one.
75+ if (value.charAt(index - 1) === SEPARATOR) {
76+ score += index - 1;
77+ // Don't match a single-character query unless it's found at the beginning
78+ // of the string or is preceded by a dot.
79+ } else if (queryLength === 1) {
80+ return;
81+ // (1) Remove one point for each unmatched character up to the nearest
82+ // preceding dot or the beginning of the string.
83+ // (2) Remove one point for each unmatched character following the query.
84+ } else {
85+ i = index - 2;
86+ while (i >= 0 && value.charAt(i) !== SEPARATOR) {
87+ i--;
88+ }
89+ score -=
90+ index -
91+ i + // (1)
92+ (valueLength - queryLength - index); // (2)
93+ }
94+
95+ // Remove one point for each dot preceding the query, except for the one
96+ // immediately before the query.
97+ separators = 0;
6798 i = index - 2;
68− while ((i >= 0) && (value.charAt(i) !== SEPARATOR)) { i--; }
69− score -= (index - i) + // (1)
70− (valueLength - queryLength - index); // (2)
99+ while (i >= 0) {
100+ if (value.charAt(i) === SEPARATOR) {
101+ separators++;
102+ }
103+ i--;
104+ }
105+ score -= separators;
71106 }
72107
73− // Remove one point for each dot preceding the query, except for the one
74− // immediately before the query.
108+ // Remove five points for each dot following the query.
75109 separators = 0;
76− i = index - 2;
110+ i = valueLength - queryLength - index - 1;
77111 while (i >= 0) {
78− if (value.charAt(i) === SEPARATOR) { separators++; }
112+ if (value.charAt(index + queryLength + i) === SEPARATOR) {
113+ separators++;
114+ }
79115 i--;
80116 }
81− score -= separators;
82−}
83−
84−// Remove five points for each dot following the query.
85−separators = 0;
86−i = valueLength - queryLength - index - 1;
87−while (i >= 0) {
88− if (value.charAt(index + queryLength + i) === SEPARATOR) { separators++; }
89− i--;
90−}
91−score -= separators * 5;
117+ score -= separators * 5;
92118
93−return Math.max(1, score);
119+ return Math.max(1, score);
94120 }
95121
96122 function fuzzyMatch() {
97−if ((valueLength <= queryLength) || (value.indexOf(query) >= 0)) { return; }
98−if (!(match = fuzzyRegexp.exec(value))) { return; }
99−matchIndex = match.index;
100−matchLength = match[0].length;
101−score = scoreFuzzyMatch();
102−if (match = fuzzyRegexp.exec(value.slice(i = value.lastIndexOf(SEPARATOR) + 1))) {
103− matchIndex = i + match.index;
123+ if (valueLength <= queryLength || value.indexOf(query) >= 0) {
124+ return;
125+ }
126+ if (!(match = fuzzyRegexp.exec(value))) {
127+ return;
128+ }
129+ matchIndex = match.index;
104130 matchLength = match[0].length;
105− return Math.max(score, scoreFuzzyMatch());
106−} else {
107− return score;
108−}
131+ score = scoreFuzzyMatch();
132+ if (
133+ (match = fuzzyRegexp.exec(
134+ value.slice((i = value.lastIndexOf(SEPARATOR) + 1)),
135+ ))
136+ ) {
137+ matchIndex = i + match.index;
138+ matchLength = match[0].length;
139+ return Math.max(score, scoreFuzzyMatch());
140+ } else {
141+ return score;
142+ }
109143 }
110144
111145 function scoreFuzzyMatch() {
112−// When the match is at the beginning of the string or preceded by a dot.
113−if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) {
114− return Math.max(66, 100 - matchLength);
115−// When the match is at the end of the string.
116−} else if ((matchIndex + matchLength) === valueLength) {
117− return Math.max(33, 67 - matchLength);
118−// When the match is in the middle of the string.
119−} else {
120− return Math.max(1, 34 - matchLength);
121−}
146+ // When the match is at the beginning of the string or preceded by a dot.
147+ if (matchIndex === 0 || value.charAt(matchIndex - 1) === SEPARATOR) {
148+ return Math.max(66, 100 - matchLength);
149+ // When the match is at the end of the string.
150+ } else if (matchIndex + matchLength === valueLength) {
151+ return Math.max(33, 67 - matchLength);
152+ // When the match is in the middle of the string.
153+ } else {
154+ return Math.max(1, 34 - matchLength);
155+ }
122156 }
123157
124158 //
125159 // Searchers
126160 //
127161
128−(function() {
162+(function () {
129163 let CHUNK_SIZE = undefined;
130164 let DEFAULTS = undefined;
131165 let SEPARATORS_REGEXP = undefined;
@@ -141,25 +175,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 9 unmodified lines
141175 const Cls = (app.Searcher = class Searcher {
142176 static initClass() {
143177 $.extend(this.prototype, Events);
144−
178+
145179 CHUNK_SIZE = 20000;
146−
180+
147181 DEFAULTS = {
148182 max_results: app.config.max_results,
149− fuzzy_min_length: 3
183+ fuzzy_min_length: 3,
150184 };
151−
152− SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g;
185+
186+ SEPARATORS_REGEXP =
187+ /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g;
153188 EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/;
154189 INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/;
155190 EMPTY_PARANTHESES_REGEXP = /\(\)/;
156191 EVENT_REGEXP = /\ event$/;
157192 DOT_REGEXP = /\.+/g;
158193 WHITESPACE_REGEXP = /\s/g;
159−
160− EMPTY_STRING = '';
161− ELLIPSIS = '...';
162− STRING = 'string';
194+
195+ EMPTY_STRING = "";
196+ ELLIPSIS = "...";
197+ STRING = "string";
163198 }
164199
165200 static normalizeString(string) {
@@ -176,13 +211,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 10 unmodified lines
176211
177212 static normalizeQuery(string) {
178213 string = this.normalizeString(string);
179− return string.replace(EOS_SEPARATORS_REGEXP, '$1.');
214+ return string.replace(EOS_SEPARATORS_REGEXP, "$1.");
180215 }
181216
182217 constructor(options) {
183218 this.match = this.match.bind(this);
184219 this.matchChunks = this.matchChunks.bind(this);
185− if (options == null) { options = {}; }
220+ if (options == null) {
221+ options = {};
222+ }
186223 this.options = $.extend({}, DEFAULTS, options);
187224 }
188225
@@ -194,11 +231,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 5 unmodified lines
194231 this.query = q;
195232 this.setup();
196233
197− if (this.isValid()) { this.match(); } else { this.end(); }
234+ if (this.isValid()) {
235+ this.match();
236+ } else {
237+ this.end();
238+ }
198239 }
199240
200241 setup() {
201− query = (this.query = this.constructor.normalizeQuery(this.query));
242+ query = this.query = this.constructor.normalizeQuery(this.query);
202243 queryLength = query.length;
203244 this.dataLength = this.data.length;
204245 this.matchers = [exactMatch];
@@ -216,12 +257,14 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 11 unmodified lines
216257 }
217258
218259 isValid() {
219− return (queryLength > 0) && (query !== SEPARATOR);
260+ return queryLength > 0 && query !== SEPARATOR;
220261 }
221262
222263 end() {
223− if (!this.totalResults) { this.triggerResults([]); }
224− this.trigger('end');
264+ if (!this.totalResults) {
265+ this.triggerResults([]);
266+ }
267+ this.trigger("end");
225268 this.free();
226269 }
227270
@@ -233,8 +276,17 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 5 unmodified lines
233276 }
234277
235278 free() {
236− this.data = (this.attr = (this.dataLength = (this.matchers = (this.matcher = (this.query =
237− (this.totalResults = (this.scoreMap = (this.cursor = (this.timeout = null)))))))));
279+ this.data =
280+ this.attr =
281+ this.dataLength =
282+ this.matchers =
283+ this.matcher =
284+ this.query =
285+ this.totalResults =
286+ this.scoreMap =
287+ this.cursor =
288+ this.timeout =
289+ null;
238290 }
239291
240292 match() {
@@ -254,7 +306,7 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 13 unmodified lines
254306 matchChunks() {
255307 this.matchChunk();
256308
257− if ((this.cursor === this.dataLength) || this.scoredEnough()) {
309+ if (this.cursor === this.dataLength || this.scoredEnough()) {
258310 this.delay(this.match);
259311 this.sendResults();
260312 } else {
@@ -263,28 +315,36 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 2 unmodified lines
263315 }
264316
265317 matchChunk() {
266− ({
267− matcher
268− } = this);
269− for (let j = 0, end = this.chunkSize(), asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) {
318+ ({ matcher } = this);
319+ for (
320+ let j = 0, end = this.chunkSize(), asc = 0 <= end;
321+ asc ? j < end : j > end;
322+ asc ? j++ : j--
323+ ) {
270324 value = this.data[this.cursor][this.attr];
271− if (value.split) { // string
325+ if (value.split) {
326+ // string
272327 valueLength = value.length;
273− if (score = matcher()) { this.addResult(this.data[this.cursor], score); }
274− } else { // array
328+ if ((score = matcher())) {
329+ this.addResult(this.data[this.cursor], score);
330+ }
331+ } else {
332+ // array
275333 score = 0;
276334 for (value of Array.from(this.data[this.cursor][this.attr])) {
277335 valueLength = value.length;
278336 score = Math.max(score, matcher() || 0);
279337 }
280− if (score > 0) { this.addResult(this.data[this.cursor], score); }
338+ if (score > 0) {
339+ this.addResult(this.data[this.cursor], score);
340+ }
281341 }
282342 this.cursor++;
283343 }
284344 }
285345
286346 chunkSize() {
287− if ((this.cursor + CHUNK_SIZE) > this.dataLength) {
347+ if (this.cursor + CHUNK_SIZE > this.dataLength) {
288348 return this.dataLength % CHUNK_SIZE;
289349 } else {
290350 return CHUNK_SIZE;
@@ -292,8 +352,11 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 1 unmodified line
292352 }
293353
294354 scoredEnough() {
295− return (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= this.options.max_results;
296− }
355+ return (
356+ (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >=
357+ this.options.max_results
358+ );
359+ }
297360
298361 foundEnough() {
299362 return this.totalResults >= this.options.max_results;
@@ -301,7 +364,9 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 1 unmodified line
301364
302365 addResult(object, score) {
303366 let name;
304− (this.scoreMap[name = Math.round(score)] || (this.scoreMap[name] = [])).push(object);
367+ (
368+ this.scoreMap[(name = Math.round(score))] || (this.scoreMap[name] = [])
369+ ).push(object);
305370 this.totalResults++;
306371 }
307372
@@ -318,21 +383,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 10 unmodified lines
318383
319384 sendResults() {
320385 const results = this.getResults();
321− if (results.length) { this.triggerResults(results); }
386+ if (results.length) {
387+ this.triggerResults(results);
388+ }
322389 }
323390
324391 triggerResults(results) {
325− this.trigger('results', results);
392+ this.trigger("results", results);
326393 }
327394
328395 delay(fn) {
329− return this.timeout = setTimeout(fn, 1);
396+ return (this.timeout = setTimeout(fn, 1));
330397 }
331398
332399 queryToFuzzyRegexp(string) {
333− const chars = string.split('');
334− for (i = 0; i < chars.length; i++) { var char = chars[i]; chars[i] = $.escapeRegexp(char); }
335− return new RegExp(chars.join('.*?'));
400+ const chars = string.split("");
401+ for (i = 0; i < chars.length; i++) {
402+ var char = chars[i];
403+ chars[i] = $.escapeRegexp(char);
404+ }
405+ return new RegExp(chars.join(".*?"));
336406 }
337407 });
338408 Cls.initClass();
@@ -347,7 +417,9 @@ app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher { · 8 unmodified lines
347417
348418 match() {
349419 if (this.matcher) {
350− if (!this.allResults) { this.allResults = []; }
420+ if (!this.allResults) {
421+ this.allResults = [];
422+ }
351423 this.allResults.push.apply(this.allResults, this.getResults());
352424 }
353425 return super.match(...arguments);
assets/javascripts/app/serviceworker.js+26−12
@@ -21,22 +21,28 @@ const Cls = (app.ServiceWorker = class ServiceWorker { · 20 unmodified lines
2121 this.registration = null;
2222 this.notifyUpdate = true;
2323
24− navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'})
24+ navigator.serviceWorker
25+ .register(app.config.service_worker_path, { scope: "/" })
2526 .then(
26− registration => this.updateRegistration(registration),
27− error => console.error('Could not register service worker:', error));
27+ (registration) => this.updateRegistration(registration),
28+ (error) => console.error("Could not register service worker:", error),
29+ );
2830 }
2931
3032 update() {
31− if (!this.registration) { return; }
33+ if (!this.registration) {
34+ return;
35+ }
3236 this.notifyUpdate = true;
33− return this.registration.update().catch(function() {});
37+ return this.registration.update().catch(function () {});
3438 }
3539
3640 updateInBackground() {
37− if (!this.registration) { return; }
41+ if (!this.registration) {
42+ return;
43+ }
3844 this.notifyUpdate = false;
39− return this.registration.update().catch(function() {});
45+ return this.registration.update().catch(function () {});
4046 }
4147
4248 reload() {
@@ -45,24 +51,32 @@ const Cls = (app.ServiceWorker = class ServiceWorker { · 2 unmodified lines
4551
4652 updateRegistration(registration) {
4753 this.registration = registration;
48− $.on(this.registration, 'updatefound', this.onUpdateFound);
54+ $.on(this.registration, "updatefound", this.onUpdateFound);
4955 }
5056
5157 onUpdateFound() {
52− if (this.installingRegistration) { $.off(this.installingRegistration, 'statechange', this.onStateChange()); }
58+ if (this.installingRegistration) {
59+ $.off(this.installingRegistration, "statechange", this.onStateChange());
60+ }
5361 this.installingRegistration = this.registration.installing;
54− $.on(this.installingRegistration, 'statechange', this.onStateChange);
62+ $.on(this.installingRegistration, "statechange", this.onStateChange);
5563 }
5664
5765 onStateChange() {
58− if (this.installingRegistration && (this.installingRegistration.state === 'installed') && navigator.serviceWorker.controller) {
66+ if (
67+ this.installingRegistration &&
68+ this.installingRegistration.state === "installed" &&
69+ navigator.serviceWorker.controller
70+ ) {
5971 this.installingRegistration = null;
6072 this.onUpdateReady();
6173 }
6274 }
6375
6476 onUpdateReady() {
65− if (this.notifyUpdate) { this.trigger('updateready'); }
77+ if (this.notifyUpdate) {
78+ this.trigger("updateready");
79+ }
6680 }
6781 });
6882 Cls.initClass();
assets/javascripts/app/settings.js+111−80
@@ -10,44 +10,39 @@ · 9 unmodified lines
1010 * DS207: Consider shorter variations of null checks
1111 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1212 */
13−(function() {
13+(function () {
1414 let PREFERENCE_KEYS = undefined;
1515 let INTERNAL_KEYS = undefined;
1616 const Cls = (app.Settings = class Settings {
1717 static initClass() {
1818 PREFERENCE_KEYS = [
19− 'hideDisabled',
20− 'hideIntro',
21− 'manualUpdate',
22− 'fastScroll',
23− 'arrowScroll',
24− 'analyticsConsent',
25− 'docs',
26− 'dark', // legacy
27− 'theme',
28− 'layout',
29− 'size',
30− 'tips',
31− 'noAutofocus',
32− 'autoInstall',
33− 'spaceScroll',
34− 'spaceTimeout'
19+ "hideDisabled",
20+ "hideIntro",
21+ "manualUpdate",
22+ "fastScroll",
23+ "arrowScroll",
24+ "analyticsConsent",
25+ "docs",
26+ "dark", // legacy
27+ "theme",
28+ "layout",
29+ "size",
30+ "tips",
31+ "noAutofocus",
32+ "autoInstall",
33+ "spaceScroll",
34+ "spaceTimeout",
3535 ];
36−
37− INTERNAL_KEYS = [
38− 'count',
39− 'schema',
40− 'version',
41− 'news'
42− ];
43−
36+
37+ INTERNAL_KEYS = ["count", "schema", "version", "news"];
38+
4439 this.prototype.LAYOUTS = [
45− '_max-width',
46− '_sidebar-hidden',
47− '_native-scrollbars',
48− '_text-justify-hyphenate'
40+ "_max-width",
41+ "_sidebar-hidden",
42+ "_native-scrollbars",
43+ "_text-justify-hyphenate",
4944 ];
50−
45+
5146 this.defaults = {
5247 count: 0,
5348 hideDisabled: false,
@@ -56,29 +51,38 @@ · 2 unmodified lines
5651 manualUpdate: false,
5752 schema: 1,
5853 analyticsConsent: false,
59− theme: 'auto',
54+ theme: "auto",
6055 spaceScroll: 1,
61− spaceTimeout: 0.5
56+ spaceTimeout: 0.5,
6257 };
6358 }
6459
6560 constructor() {
66− this.store = new CookiesStore;
61+ this.store = new CookiesStore();
6762 this.cache = {};
68− this.autoSupported = window.matchMedia('(prefers-color-scheme)').media !== 'not all';
63+ this.autoSupported =
64+ window.matchMedia("(prefers-color-scheme)").media !== "not all";
6965 if (this.autoSupported) {
70− this.darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
71− this.darkModeQuery.addListener(() => this.setTheme(this.get('theme')));
66+ this.darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
67+ this.darkModeQuery.addListener(() => this.setTheme(this.get("theme")));
7268 }
7369 }
7470
75−
7671 get(key) {
7772 let left;
78− if (this.cache.hasOwnProperty(key)) { return this.cache[key]; }
79− this.cache[key] = (left = this.store.get(key)) != null ? left : this.constructor.defaults[key];
80− if ((key === 'theme') && (this.cache[key] === 'auto') && !this.darkModeQuery) {
81− return this.cache[key] = 'default';
73+ if (this.cache.hasOwnProperty(key)) {
74+ return this.cache[key];
75+ }
76+ this.cache[key] =
77+ (left = this.store.get(key)) != null
78+ ? left
79+ : this.constructor.defaults[key];
80+ if (
81+ key === "theme" &&
82+ this.cache[key] === "auto" &&
83+ !this.darkModeQuery
84+ ) {
85+ return (this.cache[key] = "default");
8286 } else {
8387 return this.cache[key];
8488 }
@@ -87,7 +91,9 @@ · 2 unmodified lines
8791 set(key, value) {
8892 this.store.set(key, value);
8993 delete this.cache[key];
90− if (key === 'theme') { this.setTheme(value); }
94+ if (key === "theme") {
95+ this.setTheme(value);
96+ }
9197 }
9298
9399 del(key) {
@@ -96,51 +102,58 @@ · 2 unmodified lines
96102 }
97103
98104 hasDocs() {
99− try { return !!this.store.get('docs'); } catch (error) {}
105+ try {
106+ return !!this.store.get("docs");
107+ } catch (error) {}
100108 }
101109
102110 getDocs() {
103− return __guard__(this.store.get('docs'), x => x.split('/')) || app.config.default_docs;
111+ return (
112+ __guard__(this.store.get("docs"), (x) => x.split("/")) ||
113+ app.config.default_docs
114+ );
104115 }
105116
106117 setDocs(docs) {
107− this.set('docs', docs.join('/'));
118+ this.set("docs", docs.join("/"));
108119 }
109120
110121 getTips() {
111− return __guard__(this.store.get('tips'), x => x.split('/')) || [];
122+ return __guard__(this.store.get("tips"), (x) => x.split("/")) || [];
112123 }
113124
114125 setTips(tips) {
115− this.set('tips', tips.join('/'));
126+ this.set("tips", tips.join("/"));
116127 }
117128
118129 setLayout(name, enable) {
119130 this.toggleLayout(name, enable);
120131
121− const layout = (this.store.get('layout') || '').split(' ');
122− $.arrayDelete(layout, '');
132+ const layout = (this.store.get("layout") || "").split(" ");
133+ $.arrayDelete(layout, "");
123134
124135 if (enable) {
125− if (layout.indexOf(name) === -1) { layout.push(name); }
136+ if (layout.indexOf(name) === -1) {
137+ layout.push(name);
138+ }
126139 } else {
127140 $.arrayDelete(layout, name);
128141 }
129142
130143 if (layout.length > 0) {
131− this.set('layout', layout.join(' '));
144+ this.set("layout", layout.join(" "));
132145 } else {
133− this.del('layout');
146+ this.del("layout");
134147 }
135148 }
136149
137150 hasLayout(name) {
138− const layout = (this.store.get('layout') || '').split(' ');
151+ const layout = (this.store.get("layout") || "").split(" ");
139152 return layout.indexOf(name) !== -1;
140153 }
141154
142155 setSize(value) {
143− this.set('size', value);
156+ this.set("size", value);
144157 }
145158
146159 dump() {
@@ -149,7 +162,9 @@ · 2 unmodified lines
149162
150163 export() {
151164 const data = this.dump();
152− for (var key of Array.from(INTERNAL_KEYS)) { delete data[key]; }
165+ for (var key of Array.from(INTERNAL_KEYS)) {
166+ delete data[key];
167+ }
153168 return data;
154169 }
155170
@@ -158,11 +173,15 @@ · 2 unmodified lines
158173 const object = this.export();
159174 for (key in object) {
160175 value = object[key];
161− if (!data.hasOwnProperty(key)) { this.del(key); }
176+ if (!data.hasOwnProperty(key)) {
177+ this.del(key);
178+ }
162179 }
163180 for (key in data) {
164181 value = data[key];
165− if (PREFERENCE_KEYS.indexOf(key) !== -1) { this.set(key, value); }
182+ if (PREFERENCE_KEYS.indexOf(key) !== -1) {
183+ this.set(key, value);
184+ }
166185 }
167186 }
168187
@@ -172,44 +191,54 @@ · 3 unmodified lines
172191 }
173192
174193 initLayout() {
175− if (this.get('dark') === 1) {
176− this.set('theme', 'dark');
177− this.del('dark');
194+ if (this.get("dark") === 1) {
195+ this.set("theme", "dark");
196+ this.del("dark");
197+ }
198+ this.setTheme(this.get("theme"));
199+ for (var layout of Array.from(this.LAYOUTS)) {
200+ this.toggleLayout(layout, this.hasLayout(layout));
178201 }
179− this.setTheme(this.get('theme'));
180− for (var layout of Array.from(this.LAYOUTS)) { this.toggleLayout(layout, this.hasLayout(layout)); }
181202 this.initSidebarWidth();
182203 }
183204
184205 setTheme(theme) {
185− if (theme === 'auto') {
186− theme = this.darkModeQuery.matches ? 'dark' : 'default';
206+ if (theme === "auto") {
207+ theme = this.darkModeQuery.matches ? "dark" : "default";
187208 }
188− const {
189− classList
190− } = document.documentElement;
191− classList.remove('_theme-default', '_theme-dark');
192− classList.add('_theme-' + theme);
209+ const { classList } = document.documentElement;
210+ classList.remove("_theme-default", "_theme-dark");
211+ classList.add("_theme-" + theme);
193212 this.updateColorMeta();
194213 }
195214
196215 updateColorMeta() {
197− const color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim();
198− $('meta[name=theme-color]').setAttribute('content', color);
216+ const color = getComputedStyle(document.documentElement)
217+ .getPropertyValue("--headerBackground")
218+ .trim();
219+ $("meta[name=theme-color]").setAttribute("content", color);
199220 }
200221
201222 toggleLayout(layout, enable) {
202− const {
203− classList
204− } = document.body;
223+ const { classList } = document.body;
205224 // sidebar is always shown for settings; its state is updated in app.views.Settings
206− if ((layout !== '_sidebar-hidden') || !(app.router != null ? app.router.isSettings : undefined)) { classList.toggle(layout, enable); }
207− classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled());
225+ if (
226+ layout !== "_sidebar-hidden" ||
227+ !(app.router != null ? app.router.isSettings : undefined)
228+ ) {
229+ classList.toggle(layout, enable);
230+ }
231+ classList.toggle("_overlay-scrollbars", $.overlayScrollbarsEnabled());
208232 }
209233
210234 initSidebarWidth() {
211− const size = this.get('size');
212− if (size) { document.documentElement.style.setProperty('--sidebarWidth', size + 'px'); }
235+ const size = this.get("size");
236+ if (size) {
237+ document.documentElement.style.setProperty(
238+ "--sidebarWidth",
239+ size + "px",
240+ );
241+ }
213242 }
214243 });
215244 Cls.initClass();
@@ -217,5 +246,7 @@ · 1 unmodified line
217246 })();
218247
219248 function __guard__(value, transform) {
220− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
221−}
249+ return typeof value !== "undefined" && value !== null
250+ ? transform(value)
251+ : undefined;
252+}
assets/javascripts/app/shortcuts.js+126−72
@@ -21,111 +21,151 @@ const Cls = (app.Shortcuts = class Shortcuts { · 20 unmodified lines
2121 }
2222
2323 start() {
24− $.on(document, 'keydown', this.onKeydown);
25− $.on(document, 'keypress', this.onKeypress);
24+ $.on(document, "keydown", this.onKeydown);
25+ $.on(document, "keypress", this.onKeypress);
2626 }
2727
2828 stop() {
29− $.off(document, 'keydown', this.onKeydown);
30− $.off(document, 'keypress', this.onKeypress);
29+ $.off(document, "keydown", this.onKeydown);
30+ $.off(document, "keypress", this.onKeypress);
3131 }
3232
3333 swapArrowKeysBehavior() {
34− return app.settings.get('arrowScroll');
34+ return app.settings.get("arrowScroll");
3535 }
3636
3737 spaceScroll() {
38− return app.settings.get('spaceScroll');
38+ return app.settings.get("spaceScroll");
3939 }
4040
4141 showTip() {
42− app.showTip('KeyNav');
43− return this.showTip = null;
42+ app.showTip("KeyNav");
43+ return (this.showTip = null);
4444 }
4545
4646 spaceTimeout() {
47− return app.settings.get('spaceTimeout');
47+ return app.settings.get("spaceTimeout");
4848 }
4949
5050 onKeydown(event) {
51− if (this.buggyEvent(event)) { return; }
51+ if (this.buggyEvent(event)) {
52+ return;
53+ }
5254 const result = (() => {
5355 if (event.ctrlKey || event.metaKey) {
54− if (!event.altKey && !event.shiftKey) { return this.handleKeydownSuperEvent(event); }
55− } else if (event.shiftKey) {
56− if (!event.altKey) { return this.handleKeydownShiftEvent(event); }
57− } else if (event.altKey) {
58− return this.handleKeydownAltEvent(event);
59− } else {
60− return this.handleKeydownEvent(event);
61− }
56+ if (!event.altKey && !event.shiftKey) {
57+ return this.handleKeydownSuperEvent(event);
58+ }
59+ } else if (event.shiftKey) {
60+ if (!event.altKey) {
61+ return this.handleKeydownShiftEvent(event);
62+ }
63+ } else if (event.altKey) {
64+ return this.handleKeydownAltEvent(event);
65+ } else {
66+ return this.handleKeydownEvent(event);
67+ }
6268 })();
6369
64− if (result === false) { event.preventDefault(); }
70+ if (result === false) {
71+ event.preventDefault();
72+ }
6573 }
6674
6775 onKeypress(event) {
68− if (this.buggyEvent(event) || ((event.charCode === 63) && (document.activeElement.tagName === 'INPUT'))) { return; }
76+ if (
77+ this.buggyEvent(event) ||
78+ (event.charCode === 63 && document.activeElement.tagName === "INPUT")
79+ ) {
80+ return;
81+ }
6982 if (!event.ctrlKey && !event.metaKey) {
7083 const result = this.handleKeypressEvent(event);
71− if (result === false) { event.preventDefault(); }
84+ if (result === false) {
85+ event.preventDefault();
86+ }
7287 }
7388 }
7489
7590 handleKeydownEvent(event, _force) {
76− if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownAltEvent(event, true); }
91+ if (
92+ !_force &&
93+ [37, 38, 39, 40].includes(event.which) &&
94+ this.swapArrowKeysBehavior()
95+ ) {
96+ return this.handleKeydownAltEvent(event, true);
97+ }
7798
78− if (!event.target.form && ((48 <= event.which && event.which <= 57) || (65 <= event.which && event.which <= 90))) {
79− this.trigger('typing');
99+ if (
100+ !event.target.form &&
101+ ((48 <= event.which && event.which <= 57) ||
102+ (65 <= event.which && event.which <= 90))
103+ ) {
104+ this.trigger("typing");
80105 return;
81106 }
82107
83108 switch (event.which) {
84109 case 8:
85− if (!event.target.form) { return this.trigger('typing'); }
110+ if (!event.target.form) {
111+ return this.trigger("typing");
112+ }
86113 break;
87114 case 13:
88− return this.trigger('enter');
115+ return this.trigger("enter");
89116 case 27:
90− this.trigger('escape');
117+ this.trigger("escape");
91118 return false;
92119 case 32:
93− if ((event.target.type === 'search') && this.spaceScroll() && (!this.lastKeypress || (this.lastKeypress < (Date.now() - (this.spaceTimeout() * 1000))))) {
94− this.trigger('pageDown');
120+ if (
121+ event.target.type === "search" &&
122+ this.spaceScroll() &&
123+ (!this.lastKeypress ||
124+ this.lastKeypress < Date.now() - this.spaceTimeout() * 1000)
125+ ) {
126+ this.trigger("pageDown");
95127 return false;
96128 }
97129 break;
98130 case 33:
99− return this.trigger('pageUp');
131+ return this.trigger("pageUp");
100132 case 34:
101− return this.trigger('pageDown');
133+ return this.trigger("pageDown");
102134 case 35:
103− if (!event.target.form) { return this.trigger('pageBottom'); }
135+ if (!event.target.form) {
136+ return this.trigger("pageBottom");
137+ }
104138 break;
105139 case 36:
106− if (!event.target.form) { return this.trigger('pageTop'); }
140+ if (!event.target.form) {
141+ return this.trigger("pageTop");
142+ }
107143 break;
108144 case 37:
109− if (!event.target.value) { return this.trigger('left'); }
145+ if (!event.target.value) {
146+ return this.trigger("left");
147+ }
110148 break;
111149 case 38:
112− this.trigger('up');
113− if (typeof this.showTip === 'function') {
150+ this.trigger("up");
151+ if (typeof this.showTip === "function") {
114152 this.showTip();
115153 }
116154 return false;
117155 case 39:
118− if (!event.target.value) { return this.trigger('right'); }
156+ if (!event.target.value) {
157+ return this.trigger("right");
158+ }
119159 break;
120160 case 40:
121− this.trigger('down');
122− if (typeof this.showTip === 'function') {
161+ this.trigger("down");
162+ if (typeof this.showTip === "function") {
123163 this.showTip();
124164 }
125165 return false;
126166 case 191:
127167 if (!event.target.form) {
128− this.trigger('typing');
168+ this.trigger("typing");
129169 return false;
130170 }
131171 break;
@@ -135,52 +175,58 @@ const Cls = (app.Shortcuts = class Shortcuts { · 3 unmodified lines
135175 handleKeydownSuperEvent(event) {
136176 switch (event.which) {
137177 case 13:
138− return this.trigger('superEnter');
178+ return this.trigger("superEnter");
139179 case 37:
140180 if (this.isMac) {
141− this.trigger('superLeft');
181+ this.trigger("superLeft");
142182 return false;
143183 }
144184 break;
145185 case 38:
146− this.trigger('pageTop');
186+ this.trigger("pageTop");
147187 return false;
148188 case 39:
149189 if (this.isMac) {
150− this.trigger('superRight');
190+ this.trigger("superRight");
151191 return false;
152192 }
153193 break;
154194 case 40:
155− this.trigger('pageBottom');
195+ this.trigger("pageBottom");
156196 return false;
157197 case 188:
158− this.trigger('preferences');
198+ this.trigger("preferences");
159199 return false;
160200 }
161201 }
162202
163203 handleKeydownShiftEvent(event, _force) {
164− if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); }
204+ if (
205+ !_force &&
206+ [37, 38, 39, 40].includes(event.which) &&
207+ this.swapArrowKeysBehavior()
208+ ) {
209+ return this.handleKeydownEvent(event, true);
210+ }
165211
166− if (!event.target.form && (65 <= event.which && event.which <= 90)) {
167− this.trigger('typing');
212+ if (!event.target.form && 65 <= event.which && event.which <= 90) {
213+ this.trigger("typing");
168214 return;
169215 }
170216
171217 switch (event.which) {
172218 case 32:
173− this.trigger('pageUp');
219+ this.trigger("pageUp");
174220 return false;
175221 case 38:
176− if (!__guard__(getSelection(), x => x.toString())) {
177− this.trigger('altUp');
222+ if (!__guard__(getSelection(), (x) => x.toString())) {
223+ this.trigger("altUp");
178224 return false;
179225 }
180226 break;
181227 case 40:
182− if (!__guard__(getSelection(), x1 => x1.toString())) {
183− this.trigger('altDown');
228+ if (!__guard__(getSelection(), (x1) => x1.toString())) {
229+ this.trigger("altDown");
184230 return false;
185231 }
186232 break;
@@ -188,58 +234,64 @@ const Cls = (app.Shortcuts = class Shortcuts { · 1 unmodified line
188234 }
189235
190236 handleKeydownAltEvent(event, _force) {
191− if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); }
237+ if (
238+ !_force &&
239+ [37, 38, 39, 40].includes(event.which) &&
240+ this.swapArrowKeysBehavior()
241+ ) {
242+ return this.handleKeydownEvent(event, true);
243+ }
192244
193245 switch (event.which) {
194246 case 9:
195− return this.trigger('altRight', event);
247+ return this.trigger("altRight", event);
196248 case 37:
197249 if (!this.isMac) {
198− this.trigger('superLeft');
250+ this.trigger("superLeft");
199251 return false;
200252 }
201253 break;
202254 case 38:
203− this.trigger('altUp');
255+ this.trigger("altUp");
204256 return false;
205257 case 39:
206258 if (!this.isMac) {
207− this.trigger('superRight');
259+ this.trigger("superRight");
208260 return false;
209261 }
210262 break;
211263 case 40:
212− this.trigger('altDown');
264+ this.trigger("altDown");
213265 return false;
214266 case 67:
215− this.trigger('altC');
267+ this.trigger("altC");
216268 return false;
217269 case 68:
218− this.trigger('altD');
270+ this.trigger("altD");
219271 return false;
220272 case 70:
221− return this.trigger('altF', event);
273+ return this.trigger("altF", event);
222274 case 71:
223− this.trigger('altG');
275+ this.trigger("altG");
224276 return false;
225277 case 79:
226− this.trigger('altO');
278+ this.trigger("altO");
227279 return false;
228280 case 82:
229− this.trigger('altR');
281+ this.trigger("altR");
230282 return false;
231283 case 83:
232− this.trigger('altS');
284+ this.trigger("altS");
233285 return false;
234286 }
235287 }
236288
237289 handleKeypressEvent(event) {
238− if ((event.which === 63) && !event.target.value) {
239− this.trigger('help');
290+ if (event.which === 63 && !event.target.value) {
291+ this.trigger("help");
240292 return false;
241293 } else {
242− return this.lastKeypress = Date.now();
294+ return (this.lastKeypress = Date.now());
243295 }
244296 }
245297
@@ -257,5 +309,7 @@ const Cls = (app.Shortcuts = class Shortcuts { · 11 unmodified lines
257309 Cls.initClass();
258310
259311 function __guard__(value, transform) {
260− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
261−}
312+ return typeof value !== "undefined" && value !== null
313+ ? transform(value)
314+ : undefined;
315+}
assets/javascripts/app/update_checker.js+18−10
@@ -11,9 +11,9 @@ app.UpdateChecker = class UpdateChecker { · 10 unmodified lines
1111 this.onFocus = this.onFocus.bind(this);
1212 this.lastCheck = Date.now();
1313
14− $.on(window, 'focus', this.onFocus);
14+ $.on(window, "focus", this.onFocus);
1515 if (app.serviceWorker != null) {
16− app.serviceWorker.on('updateready', this.onUpdateReady);
16+ app.serviceWorker.on("updateready", this.onUpdateReady);
1717 }
1818
1919 setTimeout(this.checkDocs, 0);
@@ -24,31 +24,39 @@ app.UpdateChecker = class UpdateChecker { · 4 unmodified lines
2424 app.serviceWorker.update();
2525 } else {
2626 ajax({
27− url: $('script[src*="application"]').getAttribute('src'),
28− dataType: 'application/javascript',
29− error: (_, xhr) => { if (xhr.status === 404) { return this.onUpdateReady(); } }
27+ url: $('script[src*="application"]').getAttribute("src"),
28+ dataType: "application/javascript",
29+ error: (_, xhr) => {
30+ if (xhr.status === 404) {
31+ return this.onUpdateReady();
32+ }
33+ },
3034 });
3135 }
3236 }
3337
3438 onUpdateReady() {
35− new app.views.Notif('UpdateReady', {autoHide: null});
39+ new app.views.Notif("UpdateReady", { autoHide: null });
3640 }
3741
3842 checkDocs() {
39− if (!app.settings.get('manualUpdate')) {
43+ if (!app.settings.get("manualUpdate")) {
4044 app.docs.updateInBackground();
4145 } else {
42− app.docs.checkForUpdates(i => { if (i > 0) { return this.onDocsUpdateReady(); } });
46+ app.docs.checkForUpdates((i) => {
47+ if (i > 0) {
48+ return this.onDocsUpdateReady();
49+ }
50+ });
4351 }
4452 }
4553
4654 onDocsUpdateReady() {
47− new app.views.Notif('UpdateDocs', {autoHide: null});
55+ new app.views.Notif("UpdateDocs", { autoHide: null });
4856 }
4957
5058 onFocus() {
51− if ((Date.now() - this.lastCheck) > 21600e3) {
59+ if (Date.now() - this.lastCheck > 21600e3) {
5260 this.lastCheck = Date.now();
5361 this.check();
5462 }
assets/javascripts/application.js+3−3
@@ -27,8 +27,8 @@ · 26 unmodified lines
2727
2828 //= require tracking
2929
30−var init = function() {
31− document.removeEventListener('DOMContentLoaded', init, false);
30+var init = function () {
31+ document.removeEventListener("DOMContentLoaded", init, false);
3232
3333 if (document.body) {
3434 return app.init();
@@ -37,4 +37,4 @@ var init = function() { · 2 unmodified lines
3737 }
3838 };
3939
40−document.addEventListener('DOMContentLoaded', init, false);
40+document.addEventListener("DOMContentLoaded", init, false);
assets/javascripts/collections/collection.js+23−7
@@ -9,7 +9,9 @@ · 8 unmodified lines
99 */
1010 app.Collection = class Collection {
1111 constructor(objects) {
12− if (objects == null) { objects = []; }
12+ if (objects == null) {
13+ objects = [];
14+ }
1315 this.reset(objects);
1416 }
1517
@@ -18,16 +20,22 @@ app.Collection = class Collection { · 2 unmodified lines
1820 }
1921
2022 reset(objects) {
21− if (objects == null) { objects = []; }
23+ if (objects == null) {
24+ objects = [];
25+ }
2226 this.models = [];
23− for (var object of Array.from(objects)) { this.add(object); }
27+ for (var object of Array.from(objects)) {
28+ this.add(object);
29+ }
2430 }
2531
2632 add(object) {
2733 if (object instanceof app.Model) {
2834 this.models.push(object);
2935 } else if (object instanceof Array) {
30− for (var obj of Array.from(object)) { this.add(obj); }
36+ for (var obj of Array.from(object)) {
37+ this.add(obj);
38+ }
3139 } else if (object instanceof app.Collection) {
3240 this.models.push(...Array.from(object.all() || []));
3341 } else {
@@ -48,7 +56,9 @@ app.Collection = class Collection { · 14 unmodified lines
4856 }
4957
5058 each(fn) {
51− for (var model of Array.from(this.models)) { fn(model); }
59+ for (var model of Array.from(this.models)) {
60+ fn(model);
61+ }
5262 }
5363
5464 all() {
@@ -61,7 +71,9 @@ app.Collection = class Collection { · 6 unmodified lines
6171
6272 findBy(attr, value) {
6373 for (var model of Array.from(this.models)) {
64− if (model[attr] === value) { return model; }
74+ if (model[attr] === value) {
75+ return model;
76+ }
6577 }
6678 }
6779
@@ -71,7 +83,11 @@ app.Collection = class Collection { · 3 unmodified lines
7183
7284 countAllBy(attr, value) {
7385 let i = 0;
74− for (var model of Array.from(this.models)) { if (model[attr] === value) { i += 1; } }
86+ for (var model of Array.from(this.models)) {
87+ if (model[attr] === value) {
88+ i += 1;
89+ }
90+ }
7591 return i;
7692 }
7793 };
assets/javascripts/collections/docs.js+43−20
@@ -8,29 +8,35 @@ · 7 unmodified lines
88 * DS206: Consider reworking classes to avoid initClass
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
11−(function() {
11+(function () {
1212 let NORMALIZE_VERSION_RGX = undefined;
1313 let NORMALIZE_VERSION_SUB = undefined;
1414 let CONCURRENCY = undefined;
1515 const Cls = (app.collections.Docs = class Docs extends app.Collection {
1616 static initClass() {
17− this.model = 'Doc';
18−
17+ this.model = "Doc";
18+
1919 NORMALIZE_VERSION_RGX = /\.(\d)$/;
20− NORMALIZE_VERSION_SUB = '.0$1';
21−
20+ NORMALIZE_VERSION_SUB = ".0$1";
21+
2222 // Load models concurrently.
2323 // It's not pretty but I didn't want to import a promise library only for this.
2424 CONCURRENCY = 3;
2525 }
2626
2727 findBySlug(slug) {
28− return this.findBy('slug', slug) || this.findBy('slug_without_version', slug);
28+ return (
29+ this.findBy("slug", slug) || this.findBy("slug_without_version", slug)
30+ );
2931 }
3032 sort() {
31− return this.models.sort(function(a, b) {
33+ return this.models.sort(function (a, b) {
3234 if (a.name === b.name) {
33− if (!a.version || (a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB))) {
35+ if (
36+ !a.version ||
37+ a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) >
38+ b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB)
39+ ) {
3440 return -1;
3541 } else {
3642 return 1;
@@ -48,13 +54,13 @@ · 11 unmodified lines
4854 var next = () => {
4955 if (i < this.models.length) {
5056 this.models[i].load(next, fail, options);
51− } else if (i === ((this.models.length + CONCURRENCY) - 1)) {
57+ } else if (i === this.models.length + CONCURRENCY - 1) {
5258 onComplete();
5359 }
5460 i++;
5561 };
5662
57− var fail = function(...args) {
63+ var fail = function (...args) {
5864 if (onError) {
5965 onError(...Array.from(args || []));
6066 onError = null;
@@ -62,11 +68,19 @@ · 1 unmodified line
6268 next();
6369 };
6470
65− for (let j = 0, end = CONCURRENCY, asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { next(); }
71+ for (
72+ let j = 0, end = CONCURRENCY, asc = 0 <= end;
73+ asc ? j < end : j > end;
74+ asc ? j++ : j--
75+ ) {
76+ next();
77+ }
6678 }
6779
6880 clearCache() {
69− for (var doc of Array.from(this.models)) { doc.clearCache(); }
81+ for (var doc of Array.from(this.models)) {
82+ doc.clearCache();
83+ }
7084 }
7185
7286 uninstall(callback) {
@@ -82,11 +96,11 @@ · 9 unmodified lines
8296 }
8397
8498 getInstallStatuses(callback) {
85− app.db.versions(this.models, function(statuses) {
99+ app.db.versions(this.models, function (statuses) {
86100 if (statuses) {
87101 for (var key in statuses) {
88102 var value = statuses[key];
89− statuses[key] = {installed: !!value, mtime: value};
103+ statuses[key] = { installed: !!value, mtime: value };
90104 }
91105 }
92106 callback(statuses);
@@ -94,22 +108,31 @@ · 1 unmodified line
94108 }
95109
96110 checkForUpdates(callback) {
97− this.getInstallStatuses(statuses => {
111+ this.getInstallStatuses((statuses) => {
98112 let i = 0;
99113 if (statuses) {
100− for (var slug in statuses) { var status = statuses[slug]; if (this.findBy('slug', slug).isOutdated(status)) { i += 1; } }
114+ for (var slug in statuses) {
115+ var status = statuses[slug];
116+ if (this.findBy("slug", slug).isOutdated(status)) {
117+ i += 1;
118+ }
119+ }
101120 }
102121 callback(i);
103122 });
104123 }
105124
106125 updateInBackground() {
107− this.getInstallStatuses(statuses => {
108− if (!statuses) { return; }
126+ this.getInstallStatuses((statuses) => {
127+ if (!statuses) {
128+ return;
129+ }
109130 for (var slug in statuses) {
110131 var status = statuses[slug];
111− var doc = this.findBy('slug', slug);
112− if (doc.isOutdated(status)) { doc.install($.noop, $.noop); }
132+ var doc = this.findBy("slug", slug);
133+ if (doc.isOutdated(status)) {
134+ doc.install($.noop, $.noop);
135+ }
113136 }
114137 });
115138 }
assets/javascripts/collections/entries.js+1−1
@@ -7,7 +7,7 @@ · 6 unmodified lines
77 */
88 const Cls = (app.collections.Entries = class Entries extends app.Collection {
99 static initClass() {
10− this.model = 'Entry';
10+ this.model = "Entry";
1111 }
1212 });
1313 Cls.initClass();
assets/javascripts/collections/types.js+9−6
@@ -8,14 +8,15 @@ · 7 unmodified lines
88 * DS206: Consider reworking classes to avoid initClass
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
11−(function() {
11+(function () {
1212 let GUIDES_RGX = undefined;
1313 let APPENDIX_RGX = undefined;
1414 const Cls = (app.collections.Types = class Types extends app.Collection {
1515 static initClass() {
16− this.model = 'Type';
17−
18− GUIDES_RGX = /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i;
16+ this.model = "Type";
17+
18+ GUIDES_RGX =
19+ /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i;
1920 APPENDIX_RGX = /appendix/i;
2021 }
2122
@@ -23,9 +24,11 @@ · 1 unmodified line
2324 const result = [];
2425 for (var type of Array.from(this.models)) {
2526 var name;
26− (result[name = this._groupFor(type)] || (result[name] = [])).push(type);
27+ (result[(name = this._groupFor(type))] || (result[name] = [])).push(
28+ type,
29+ );
2730 }
28− return result.filter(e => e.length > 0);
31+ return result.filter((e) => e.length > 0);
2932 }
3033
3134 _groupFor(type) {
assets/javascripts/debug.js+58−31
@@ -9,26 +9,33 @@ · 8 unmodified lines
99 * DS209: Avoid top-level return
1010 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1111 */
12−if (!(typeof console !== 'undefined' && console !== null ? console.time : undefined) || !console.groupCollapsed) { return; }
12+if (
13+ !(typeof console !== "undefined" && console !== null
14+ ? console.time
15+ : undefined) ||
16+ !console.groupCollapsed
17+) {
18+ return;
19+}
1320
1421 //
1522 // App
1623 //
1724
1825 const _init = app.init;
19−app.init = function() {
20− console.time('Init');
26+app.init = function () {
27+ console.time("Init");
2128 _init.call(app);
22− console.timeEnd('Init');
23− return console.time('Load');
29+ console.timeEnd("Init");
30+ return console.time("Load");
2431 };
2532
2633 const _start = app.start;
27−app.start = function() {
28− console.timeEnd('Load');
29− console.time('Start');
34+app.start = function () {
35+ console.timeEnd("Load");
36+ console.time("Start");
3037 _start.call(app, ...arguments);
31− return console.timeEnd('Start');
38+ return console.timeEnd("Start");
3239 };
3340
3441 //
@@ -38,47 +45,50 @@ app.start = function() { · 3 unmodified lines
3845 const _super = app.Searcher;
3946 const _proto = app.Searcher.prototype;
4047
41−app.Searcher = function() {
48+app.Searcher = function () {
4249 _super.apply(this, arguments);
4350
4451 const _setup = this.setup.bind(this);
45− this.setup = function() {
52+ this.setup = function () {
4653 console.groupCollapsed(`Search: ${this.query}`);
47− console.time('Total');
54+ console.time("Total");
4855 return _setup();
4956 };
5057
5158 const _match = this.match.bind(this);
5259 this.match = () => {
53− if (this.matcher) { console.timeEnd(this.matcher.name); }
60+ if (this.matcher) {
61+ console.timeEnd(this.matcher.name);
62+ }
5463 return _match();
5564 };
5665
5766 const _setupMatcher = this.setupMatcher.bind(this);
58− this.setupMatcher = function() {
67+ this.setupMatcher = function () {
5968 console.time(this.matcher.name);
6069 return _setupMatcher();
6170 };
6271
6372 const _end = this.end.bind(this);
64− this.end = function() {
73+ this.end = function () {
6574 console.log(`Results: ${this.totalResults}`);
66− console.timeEnd('Total');
75+ console.timeEnd("Total");
6776 console.groupEnd();
6877 return _end();
6978 };
7079
7180 const _kill = this.kill.bind(this);
72− this.kill = function() {
81+ this.kill = function () {
7382 if (this.timeout) {
74− if (this.matcher) { console.timeEnd(this.matcher.name); }
83+ if (this.matcher) {
84+ console.timeEnd(this.matcher.name);
85+ }
7586 console.groupEnd();
76− console.timeEnd('Total');
77− console.warn('Killed');
87+ console.timeEnd("Total");
88+ console.warn("Killed");
7889 }
7990 return _kill();
8091 };
81−
8292 };
8393
8494 $.extend(app.Searcher, _super);
@@ -89,23 +99,40 @@ app.Searcher.prototype = _proto; · 4 unmodified lines
8999 // View tree
90100 //
91101
92−this.viewTree = function(view, level, visited) {
93− if (view == null) { view = app.document; }
94− if (level == null) { level = 0; }
95− if (visited == null) { visited = []; }
96− if (visited.indexOf(view) >= 0) { return; }
102+this.viewTree = function (view, level, visited) {
103+ if (view == null) {
104+ view = app.document;
105+ }
106+ if (level == null) {
107+ level = 0;
108+ }
109+ if (visited == null) {
110+ visited = [];
111+ }
112+ if (visited.indexOf(view) >= 0) {
113+ return;
114+ }
97115 visited.push(view);
98116
99− console.log(`%c ${Array(level + 1).join(' ')}${view.constructor.name}: ${!!view.activated}`,
100− 'color:' + ((view.activated && 'green') || 'red'));
117+ console.log(
118+ `%c ${Array(level + 1).join(" ")}${
119+ view.constructor.name
120+ }: ${!!view.activated}`,
121+ "color:" + ((view.activated && "green") || "red"),
122+ );
101123
102124 for (var key of Object.keys(view || {})) {
103125 var value = view[key];
104− if ((key !== 'view') && value) {
105− if ((typeof value === 'object') && value.setupElement) {
126+ if (key !== "view" && value) {
127+ if (typeof value === "object" && value.setupElement) {
106128 this.viewTree(value, level + 1, visited);
107129 } else if (value.constructor.toString().match(/Object\(\)/)) {
108− for (var k of Object.keys(value || {})) { var v = value[k]; if (v && (typeof v === 'object') && v.setupElement) { this.viewTree(v, level + 1, visited); } }
130+ for (var k of Object.keys(value || {})) {
131+ var v = value[k];
132+ if (v && typeof v === "object" && v.setupElement) {
133+ this.viewTree(v, level + 1, visited);
134+ }
135+ }
109136 }
110137 }
111138 }
assets/javascripts/lib/ajax.js+73−52
@@ -9,11 +9,11 @@ · 8 unmodified lines
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
1111 const MIME_TYPES = {
12− json: 'application/json',
13− html: 'text/html'
12+ json: "application/json",
13+ html: "text/html",
1414 };
1515
16−this.ajax = function(options) {
16+this.ajax = function (options) {
1717 applyDefaults(options);
1818 serializeData(options);
1919
@@ -26,7 +26,7 @@ this.ajax = function(options) { · 6 unmodified lines
2626 xhr.send(options.data);
2727
2828 if (options.async) {
29− return {abort: abort.bind(undefined, xhr)};
29+ return { abort: abort.bind(undefined, xhr) };
3030 } else {
3131 return parseResponse(xhr, options);
3232 }
@@ -34,51 +34,63 @@ this.ajax = function(options) { · 1 unmodified line
3434
3535 ajax.defaults = {
3636 async: true,
37− dataType: 'json',
37+ dataType: "json",
3838 timeout: 30,
39− type: 'GET'
39+ type: "GET",
4040 };
41− // contentType
42− // context
43− // data
44− // error
45− // headers
46− // progress
47− // success
48− // url
49−
50−var applyDefaults = function(options) {
41+// contentType
42+// context
43+// data
44+// error
45+// headers
46+// progress
47+// success
48+// url
49+
50+var applyDefaults = function (options) {
5151 for (var key in ajax.defaults) {
52− if (options[key] == null) { options[key] = ajax.defaults[key]; }
52+ if (options[key] == null) {
53+ options[key] = ajax.defaults[key];
54+ }
5355 }
5456 };
5557
56−var serializeData = function(options) {
57− if (!options.data) { return; }
58+var serializeData = function (options) {
59+ if (!options.data) {
60+ return;
61+ }
5862
59− if (options.type === 'GET') {
60− options.url += '?' + serializeParams(options.data);
63+ if (options.type === "GET") {
64+ options.url += "?" + serializeParams(options.data);
6165 options.data = null;
6266 } else {
6367 options.data = serializeParams(options.data);
6468 }
6569 };
6670
67−var serializeParams = params => ((() => {
68− const result = [];
69− for (var key in params) {
70− var value = params[key];
71− result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
72− }
73− return result;
74−})()).join('&');
71+var serializeParams = (params) =>
72+ (() => {
73+ const result = [];
74+ for (var key in params) {
75+ var value = params[key];
76+ result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
77+ }
78+ return result;
79+ })().join("&");
7580
76−var applyCallbacks = function(xhr, options) {
77− if (!options.async) { return; }
81+var applyCallbacks = function (xhr, options) {
82+ if (!options.async) {
83+ return;
84+ }
7885
79− xhr.timer = setTimeout(onTimeout.bind(undefined, xhr, options), options.timeout * 1000);
80− if (options.progress) { xhr.onprogress = options.progress; }
81− xhr.onreadystatechange = function() {
86+ xhr.timer = setTimeout(
87+ onTimeout.bind(undefined, xhr, options),
88+ options.timeout * 1000,
89+ );
90+ if (options.progress) {
91+ xhr.onprogress = options.progress;
92+ }
93+ xhr.onreadystatechange = function () {
8294 if (xhr.readyState === 4) {
8395 clearTimeout(xhr.timer);
8496 onComplete(xhr, options);
@@ -86,19 +98,26 @@ var applyCallbacks = function(xhr, options) { · 1 unmodified line
8698 };
8799 };
88100
89−var applyHeaders = function(xhr, options) {
90− if (!options.headers) { options.headers = {}; }
101+var applyHeaders = function (xhr, options) {
102+ if (!options.headers) {
103+ options.headers = {};
104+ }
91105
92106 if (options.contentType) {
93− options.headers['Content-Type'] = options.contentType;
107+ options.headers["Content-Type"] = options.contentType;
94108 }
95109
96− if (!options.headers['Content-Type'] && options.data && (options.type !== 'GET')) {
97− options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
110+ if (
111+ !options.headers["Content-Type"] &&
112+ options.data &&
113+ options.type !== "GET"
114+ ) {
115+ options.headers["Content-Type"] = "application/x-www-form-urlencoded";
98116 }
99117
100118 if (options.dataType) {
101− options.headers['Accept'] = MIME_TYPES[options.dataType] || options.dataType;
119+ options.headers["Accept"] =
120+ MIME_TYPES[options.dataType] || options.dataType;
102121 }
103122
104123 for (var key in options.headers) {
@@ -107,50 +126,52 @@ var applyHeaders = function(xhr, options) { · 2 unmodified lines
107126 }
108127 };
109128
110−var onComplete = function(xhr, options) {
129+var onComplete = function (xhr, options) {
111130 if (200 <= xhr.status && xhr.status < 300) {
112131 let response;
113132 if ((response = parseResponse(xhr, options)) != null) {
114133 onSuccess(response, xhr, options);
115134 } else {
116− onError('invalid', xhr, options);
135+ onError("invalid", xhr, options);
117136 }
118137 } else {
119− onError('error', xhr, options);
138+ onError("error", xhr, options);
120139 }
121140 };
122141
123−var onSuccess = function(response, xhr, options) {
142+var onSuccess = function (response, xhr, options) {
124143 if (options.success != null) {
125144 options.success.call(options.context, response, xhr, options);
126145 }
127146 };
128147
129−var onError = function(type, xhr, options) {
148+var onError = function (type, xhr, options) {
130149 if (options.error != null) {
131150 options.error.call(options.context, type, xhr, options);
132151 }
133152 };
134153
135−var onTimeout = function(xhr, options) {
154+var onTimeout = function (xhr, options) {
136155 xhr.abort();
137− onError('timeout', xhr, options);
156+ onError("timeout", xhr, options);
138157 };
139158
140−var abort = function(xhr) {
159+var abort = function (xhr) {
141160 clearTimeout(xhr.timer);
142161 xhr.onreadystatechange = null;
143162 xhr.abort();
144163 };
145164
146−var parseResponse = function(xhr, options) {
147− if (options.dataType === 'json') {
165+var parseResponse = function (xhr, options) {
166+ if (options.dataType === "json") {
148167 return parseJSON(xhr.responseText);
149168 } else {
150169 return xhr.responseText;
151170 }
152171 };
153172
154−var parseJSON = function(json) {
155− try { return JSON.parse(json); } catch (error) {}
173+var parseJSON = function (json) {
174+ try {
175+ return JSON.parse(json);
176+ } catch (error) {}
156177 };
assets/javascripts/lib/cookies_store.js+21−10
@@ -8,14 +8,14 @@ · 7 unmodified lines
88 * DS207: Consider shorter variations of null checks
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
11−(function() {
11+(function () {
1212 let INT = undefined;
1313 const Cls = (this.CookiesStore = class CookiesStore {
1414 static initClass() {
1515 // Intentionally called CookiesStore instead of CookieStore
1616 // Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome
1717 // Related issue: https://github.com/freeCodeCamp/devdocs/issues/932
18−
18+
1919 INT = /^\d+$/;
2020 }
2121
@@ -23,7 +23,9 @@ · 1 unmodified line
2323
2424 get(key) {
2525 let value = Cookies.get(key);
26− if ((value != null) && INT.test(value)) { value = parseInt(value, 10); }
26+ if (value != null && INT.test(value)) {
27+ value = parseInt(value, 10);
28+ }
2729 return value;
2830 }
2931
@@ -33,10 +35,19 @@ · 3 unmodified lines
3335 return;
3436 }
3537
36− if (value === true) { value = 1; }
37− if (value && (typeof INT.test === 'function' ? INT.test(value) : undefined)) { value = parseInt(value, 10); }
38− Cookies.set(key, '' + value, {path: '/', expires: 1e8});
39− if (this.get(key) !== value) { this.constructor.onBlocked(key, value, this.get(key)); }
38+ if (value === true) {
39+ value = 1;
40+ }
41+ if (
42+ value &&
43+ (typeof INT.test === "function" ? INT.test(value) : undefined)
44+ ) {
45+ value = parseInt(value, 10);
46+ }
47+ Cookies.set(key, "" + value, { path: "/", expires: 1e8 });
48+ if (this.get(key) !== value) {
49+ this.constructor.onBlocked(key, value, this.get(key));
50+ }
4051 }
4152
4253 del(key) {
@@ -46,7 +57,7 @@ · 3 unmodified lines
4657 reset() {
4758 try {
4859 for (var cookie of Array.from(document.cookie.split(/;\s?/))) {
49− Cookies.expire(cookie.split('=')[0]);
60+ Cookies.expire(cookie.split("=")[0]);
5061 }
5162 return;
5263 } catch (error) {}
@@ -55,8 +66,8 @@ · 2 unmodified lines
5566 dump() {
5667 const result = {};
5768 for (var cookie of Array.from(document.cookie.split(/;\s?/))) {
58− if (cookie[0] !== '_') {
59− cookie = cookie.split('=');
69+ if (cookie[0] !== "_") {
70+ cookie = cookie.split("=");
6071 result[cookie[0]] = cookie[1];
6172 }
6273 }
assets/javascripts/lib/events.js+38−14
@@ -11,22 +11,38 @@ · 10 unmodified lines
1111 */
1212 this.Events = {
1313 on(event, callback) {
14− if (event.indexOf(' ') >= 0) {
15− for (var name of Array.from(event.split(' '))) { this.on(name, callback); }
14+ if (event.indexOf(" ") >= 0) {
15+ for (var name of Array.from(event.split(" "))) {
16+ this.on(name, callback);
17+ }
1618 } else {
1719 let base;
18− (((base = this._callbacks != null ? this._callbacks : (this._callbacks = {})))[event] != null ? base[event] : (base[event] = [])).push(callback);
20+ ((base =
21+ this._callbacks != null ? this._callbacks : (this._callbacks = {}))[
22+ event
23+ ] != null
24+ ? base[event]
25+ : (base[event] = [])
26+ ).push(callback);
1927 }
2028 return this;
2129 },
2230
2331 off(event, callback) {
2432 let callbacks, index;
25− if (event.indexOf(' ') >= 0) {
26− for (var name of Array.from(event.split(' '))) { this.off(name, callback); }
27− } else if ((callbacks = this._callbacks != null ? this._callbacks[event] : undefined) && ((index = callbacks.indexOf(callback)) >= 0)) {
33+ if (event.indexOf(" ") >= 0) {
34+ for (var name of Array.from(event.split(" "))) {
35+ this.off(name, callback);
36+ }
37+ } else if (
38+ (callbacks =
39+ this._callbacks != null ? this._callbacks[event] : undefined) &&
40+ (index = callbacks.indexOf(callback)) >= 0
41+ ) {
2842 callbacks.splice(index, 1);
29− if (!callbacks.length) { delete this._callbacks[event]; }
43+ if (!callbacks.length) {
44+ delete this._callbacks[event];
45+ }
3046 }
3147 return this;
3248 },
@@ -34,20 +50,28 @@ this.Events = { · 1 unmodified line
3450 trigger(event, ...args) {
3551 let callbacks;
3652 this.eventInProgress = { name: event, args };
37− if (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) {
38− for (var callback of Array.from(callbacks.slice(0))) { if (typeof callback === 'function') {
39− callback(...Array.from(args || []));
40− } }
53+ if (
54+ (callbacks = this._callbacks != null ? this._callbacks[event] : undefined)
55+ ) {
56+ for (var callback of Array.from(callbacks.slice(0))) {
57+ if (typeof callback === "function") {
58+ callback(...Array.from(args || []));
59+ }
60+ }
4161 }
4262 this.eventInProgress = null;
43− if (event !== 'all') { this.trigger('all', event, ...Array.from(args)); }
63+ if (event !== "all") {
64+ this.trigger("all", event, ...Array.from(args));
65+ }
4466 return this;
4567 },
4668
4769 removeEvent(event) {
4870 if (this._callbacks != null) {
49− for (var name of Array.from(event.split(' '))) { delete this._callbacks[name]; }
71+ for (var name of Array.from(event.split(" "))) {
72+ delete this._callbacks[name];
73+ }
5074 }
5175 return this;
52− }
76+ },
5377 };
assets/javascripts/lib/favicon.js+59−41
@@ -12,22 +12,24 @@ let currentSlug = null; · 11 unmodified lines
1212 const imageCache = {};
1313 const urlCache = {};
1414
15−const withImage = function(url, action) {
15+const withImage = function (url, action) {
1616 if (imageCache[url]) {
1717 return action(imageCache[url]);
1818 } else {
1919 const img = new Image();
20− img.crossOrigin = 'anonymous';
20+ img.crossOrigin = "anonymous";
2121 img.src = url;
22− return img.onload = () => {
22+ return (img.onload = () => {
2323 imageCache[url] = img;
2424 return action(img);
25− };
25+ });
2626 }
2727 };
2828
29−this.setFaviconForDoc = function(doc) {
30− if (currentSlug === doc.slug) { return; }
29+this.setFaviconForDoc = function (doc) {
30+ if (currentSlug === doc.slug) {
31+ return;
32+ }
3133
3234 const favicon = $('link[rel="icon"]');
3335
@@ -41,51 +43,67 @@ this.setFaviconForDoc = function(doc) { · 7 unmodified lines
4143 return;
4244 }
4345
44− const iconEl = $(`._icon-${doc.slug.split('~')[0]}`);
45− if (iconEl === null) { return; }
46+ const iconEl = $(`._icon-${doc.slug.split("~")[0]}`);
47+ if (iconEl === null) {
48+ return;
49+ }
4650
47− const styles = window.getComputedStyle(iconEl, ':before');
51+ const styles = window.getComputedStyle(iconEl, ":before");
4852
49− const backgroundPositionX = styles['background-position-x'];
50− const backgroundPositionY = styles['background-position-y'];
51− if ((backgroundPositionX === undefined) || (backgroundPositionY === undefined)) { return; }
53+ const backgroundPositionX = styles["background-position-x"];
54+ const backgroundPositionY = styles["background-position-y"];
55+ if (backgroundPositionX === undefined || backgroundPositionY === undefined) {
56+ return;
57+ }
5258
5359 const bgUrl = app.config.favicon_spritesheet;
5460 const sourceSize = 16;
5561 const sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2)));
5662 const sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2)));
5763
58− return withImage(bgUrl, docImg => withImage(defaultUrl, function(defaultImg) {
59− const size = defaultImg.width;
60−
61− const canvas = document.createElement('canvas');
62− const ctx = canvas.getContext('2d');
63−
64− canvas.width = size;
65− canvas.height = size;
66− ctx.drawImage(defaultImg, 0, 0);
67−
68− const docIconPercentage = 65;
69− const destinationCoords = (size / 100) * (100 - docIconPercentage);
70− const destinationSize = (size / 100) * docIconPercentage;
71−
72− ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize);
73−
74− try {
75− urlCache[doc.slug] = canvas.toDataURL();
76− favicon.href = urlCache[doc.slug];
77−
78− return currentSlug = doc.slug;
79− } catch (error) {
80− Raven.captureException(error, { level: 'info' });
81− return this.resetFavicon();
82− }
83− }));
64+ return withImage(bgUrl, (docImg) =>
65+ withImage(defaultUrl, function (defaultImg) {
66+ const size = defaultImg.width;
67+
68+ const canvas = document.createElement("canvas");
69+ const ctx = canvas.getContext("2d");
70+
71+ canvas.width = size;
72+ canvas.height = size;
73+ ctx.drawImage(defaultImg, 0, 0);
74+
75+ const docIconPercentage = 65;
76+ const destinationCoords = (size / 100) * (100 - docIconPercentage);
77+ const destinationSize = (size / 100) * docIconPercentage;
78+
79+ ctx.drawImage(
80+ docImg,
81+ sourceX,
82+ sourceY,
83+ sourceSize,
84+ sourceSize,
85+ destinationCoords,
86+ destinationCoords,
87+ destinationSize,
88+ destinationSize,
89+ );
90+
91+ try {
92+ urlCache[doc.slug] = canvas.toDataURL();
93+ favicon.href = urlCache[doc.slug];
94+
95+ return (currentSlug = doc.slug);
96+ } catch (error) {
97+ Raven.captureException(error, { level: "info" });
98+ return this.resetFavicon();
99+ }
100+ }),
101+ );
84102 };
85103
86−this.resetFavicon = function() {
87− if ((defaultUrl !== null) && (currentSlug !== null)) {
104+this.resetFavicon = function () {
105+ if (defaultUrl !== null && currentSlug !== null) {
88106 $('link[rel="icon"]').href = defaultUrl;
89− return currentSlug = null;
107+ return (currentSlug = null);
90108 }
91109 };
assets/javascripts/lib/license.js+1−1
@@ -6,4 +6,4 @@ · 5 unmodified lines
66 * This source code is licensed under the terms of the Mozilla
77 * Public License, v. 2.0, a copy of which may be obtained at:
88 * http://mozilla.org/MPL/2.0/
9−*/
9+ */
assets/javascripts/lib/page.js+153−82
@@ -13,50 +13,54 @@ · 12 unmodified lines
1313 * Based on github.com/visionmedia/page.js
1414 * Licensed under the MIT license
1515 * Copyright 2012 TJ Holowaychuk <tj@vision-media.ca>
16−*/
16+ */
1717
1818 let running = false;
1919 let currentState = null;
2020 const callbacks = [];
2121
22−this.page = function(value, fn) {
23− if (typeof value === 'function') {
24− page('*', value);
25− } else if (typeof fn === 'function') {
22+this.page = function (value, fn) {
23+ if (typeof value === "function") {
24+ page("*", value);
25+ } else if (typeof fn === "function") {
2626 const route = new Route(value);
2727 callbacks.push(route.middleware(fn));
28− } else if (typeof value === 'string') {
28+ } else if (typeof value === "string") {
2929 page.show(value, fn);
3030 } else {
3131 page.start(value);
3232 }
3333 };
3434
35−page.start = function(options) {
36− if (options == null) { options = {}; }
35+page.start = function (options) {
36+ if (options == null) {
37+ options = {};
38+ }
3739 if (!running) {
3840 running = true;
39− addEventListener('popstate', onpopstate);
40− addEventListener('click', onclick);
41+ addEventListener("popstate", onpopstate);
42+ addEventListener("click", onclick);
4143 page.replace(currentPath(), null, null, true);
4244 }
4345 };
4446
45−page.stop = function() {
47+page.stop = function () {
4648 if (running) {
4749 running = false;
48− removeEventListener('click', onclick);
49− removeEventListener('popstate', onpopstate);
50+ removeEventListener("click", onclick);
51+ removeEventListener("popstate", onpopstate);
5052 }
5153 };
5254
53−page.show = function(path, state) {
55+page.show = function (path, state) {
5456 let res;
55− if (path === (currentState != null ? currentState.path : undefined)) { return; }
57+ if (path === (currentState != null ? currentState.path : undefined)) {
58+ return;
59+ }
5660 const context = new Context(path, state);
5761 const previousState = currentState;
5862 currentState = context.state;
59− if (res = page.dispatch(context)) {
63+ if ((res = page.dispatch(context))) {
6064 currentState = previousState;
6165 location.assign(res);
6266 } else {
@@ -67,12 +71,14 @@ page.show = function(path, state) { · 4 unmodified lines
6771 return context;
6872 };
6973
70−page.replace = function(path, state, skipDispatch, init) {
74+page.replace = function (path, state, skipDispatch, init) {
7175 let result;
7276 let context = new Context(path, state || currentState);
7377 context.init = init;
7478 currentState = context.state;
75− if (!skipDispatch) { result = page.dispatch(context); }
79+ if (!skipDispatch) {
80+ result = page.dispatch(context);
81+ }
7682 if (result) {
7783 context = new Context(result);
7884 context.init = init;
@@ -81,15 +87,19 @@ page.replace = function(path, state, skipDispatch, init) { · 2 unmodified lines
8187 }
8288 context.replaceState();
8389 updateCanonicalLink();
84− if (!skipDispatch) { track(); }
90+ if (!skipDispatch) {
91+ track();
92+ }
8593 return context;
8694 };
8795
88−page.dispatch = function(context) {
96+page.dispatch = function (context) {
8997 let i = 0;
90− var next = function() {
98+ var next = function () {
9199 let fn, res;
92− if (fn = callbacks[i++]) { res = fn(context, next); }
100+ if ((fn = callbacks[i++])) {
101+ res = fn(context, next);
102+ }
93103 return res;
94104 };
95105 return next();
@@ -113,11 +123,11 @@ class Context { · 17 unmodified lines
113123 }
114124
115125 static isLastState(state) {
116− return state.id === (this.stateId - 1);
126+ return state.id === this.stateId - 1;
117127 }
118128
119129 static isInitialPopState(state) {
120− return (state.path === this.initialPath) && (this.stateId === 1);
130+ return state.path === this.initialPath && this.stateId === 1;
121131 }
122132
123133 static isSameSession(state) {
@@ -125,27 +135,40 @@ class Context { · 1 unmodified line
125135 }
126136
127137 constructor(path, state) {
128− if (path == null) { path = '/'; }
138+ if (path == null) {
139+ path = "/";
140+ }
129141 this.path = path;
130− if (state == null) { state = {}; }
142+ if (state == null) {
143+ state = {};
144+ }
131145 this.state = state;
132− this.pathname = this.path.replace(/(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) => {
133− this.query = query;
134− this.hash = hash;
135− return '';
136− });
137−
138− if (this.state.id == null) { this.state.id = this.constructor.stateId++; }
139− if (this.state.sessionId == null) { this.state.sessionId = this.constructor.sessionId; }
146+ this.pathname = this.path.replace(
147+ /(?:\?([^#]*))?(?:#(.*))?$/,
148+ (_, query, hash) => {
149+ this.query = query;
150+ this.hash = hash;
151+ return "";
152+ },
153+ );
154+
155+ if (this.state.id == null) {
156+ this.state.id = this.constructor.stateId++;
157+ }
158+ if (this.state.sessionId == null) {
159+ this.state.sessionId = this.constructor.sessionId;
160+ }
140161 this.state.path = this.path;
141162 }
142163
143164 pushState() {
144− history.pushState(this.state, '', this.path);
165+ history.pushState(this.state, "", this.path);
145166 }
146167
147168 replaceState() {
148− try { history.replaceState(this.state, '', this.path); } catch (error) {} // NS_ERROR_FAILURE in Firefox
169+ try {
170+ history.replaceState(this.state, "", this.path);
171+ } catch (error) {} // NS_ERROR_FAILURE in Firefox
149172 }
150173 }
151174 Context.initClass();
@@ -153,7 +176,9 @@ Context.initClass(); · 1 unmodified line
153176 class Route {
154177 constructor(path, options) {
155178 this.path = path;
156− if (options == null) { options = {}; }
179+ if (options == null) {
180+ options = {};
181+ }
157182 this.keys = [];
158183 this.regexp = pathtoRegexp(this.path, this.keys);
159184 }
@@ -172,13 +197,17 @@ class Route { · 12 unmodified lines
172197
173198 match(path, params) {
174199 let matchData;
175− if (!(matchData = this.regexp.exec(path))) { return; }
200+ if (!(matchData = this.regexp.exec(path))) {
201+ return;
202+ }
176203
177204 const iterable = matchData.slice(1);
178205 for (let i = 0; i < iterable.length; i++) {
179206 var key;
180207 var value = iterable[i];
181− if (typeof value === 'string') { value = decodeURIComponent(value); }
208+ if (typeof value === "string") {
209+ value = decodeURIComponent(value);
210+ }
182211 if ((key = this.keys[i])) {
183212 params[key.name] = value;
184213 } else {
@@ -189,32 +218,50 @@ class Route { · 4 unmodified lines
189218 }
190219 }
191220
192−var pathtoRegexp = function(path, keys) {
193− if (path instanceof RegExp) { return path; }
221+var pathtoRegexp = function (path, keys) {
222+ if (path instanceof RegExp) {
223+ return path;
224+ }
194225
195− if (path instanceof Array) { path = `(${path.join('|')})`; }
226+ if (path instanceof Array) {
227+ path = `(${path.join("|")})`;
228+ }
196229 path = path
197− .replace(/\/\(/g, '(?:/')
198− .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) {
199− if (slash == null) { slash = ''; }
200− if (format == null) { format = ''; }
201− keys.push({name: key, optional: !!optional});
202− let str = optional ? '' : slash;
203− str += '(?:';
204− if (optional) { str += slash; }
205− str += format;
206− str += capture || (format ? '([^/.]+?)' : '([^/]+?)');
207− str += ')';
208− if (optional) { str += optional; }
209− return str;
210− }).replace(/([\/.])/g, '\\$1')
211− .replace(/\*/g, '(.*)');
230+ .replace(/\/\(/g, "(?:/")
231+ .replace(
232+ /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g,
233+ function (_, slash, format, key, capture, optional) {
234+ if (slash == null) {
235+ slash = "";
236+ }
237+ if (format == null) {
238+ format = "";
239+ }
240+ keys.push({ name: key, optional: !!optional });
241+ let str = optional ? "" : slash;
242+ str += "(?:";
243+ if (optional) {
244+ str += slash;
245+ }
246+ str += format;
247+ str += capture || (format ? "([^/.]+?)" : "([^/]+?)");
248+ str += ")";
249+ if (optional) {
250+ str += optional;
251+ }
252+ return str;
253+ },
254+ )
255+ .replace(/([\/.])/g, "\\$1")
256+ .replace(/\*/g, "(.*)");
212257
213258 return new RegExp(`^${path}$`);
214259 };
215260
216−var onpopstate = function(event) {
217− if (!event.state || Context.isInitialPopState(event.state)) { return; }
261+var onpopstate = function (event) {
262+ if (!event.state || Context.isInitialPopState(event.state)) {
263+ return;
264+ }
218265
219266 if (Context.isSameSession(event.state)) {
220267 page.replace(event.state.path, event.state);
@@ -223,59 +270,83 @@ var onpopstate = function(event) { · 2 unmodified lines
223270 }
224271 };
225272
226−var onclick = function(event) {
273+var onclick = function (event) {
227274 try {
228− if ((event.which !== 1) || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { return; }
275+ if (
276+ event.which !== 1 ||
277+ event.metaKey ||
278+ event.ctrlKey ||
279+ event.shiftKey ||
280+ event.defaultPrevented
281+ ) {
282+ return;
283+ }
229284 } catch (error) {
230285 return;
231286 }
232287
233288 let link = $.eventTarget(event);
234− while (link && (link.tagName !== 'A')) { link = link.parentNode; }
289+ while (link && link.tagName !== "A") {
290+ link = link.parentNode;
291+ }
235292
236293 if (link && !link.target && isSameOrigin(link.href)) {
237294 event.preventDefault();
238295 let path = link.pathname + link.search + link.hash;
239− path = path.replace(/^\/\/+/, '/'); // IE11 bug
296+ path = path.replace(/^\/\/+/, "/"); // IE11 bug
240297 page.show(path);
241298 }
242299 };
243300
244−var isSameOrigin = url => url.indexOf(`${location.protocol}//${location.hostname}`) === 0;
301+var isSameOrigin = (url) =>
302+ url.indexOf(`${location.protocol}//${location.hostname}`) === 0;
245303
246−var updateCanonicalLink = function() {
247− if (!this.canonicalLink) { this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); }
248− return this.canonicalLink.setAttribute('href', `https://${location.host}${location.pathname}`);
304+var updateCanonicalLink = function () {
305+ if (!this.canonicalLink) {
306+ this.canonicalLink = document.head.querySelector('link[rel="canonical"]');
307+ }
308+ return this.canonicalLink.setAttribute(
309+ "href",
310+ `https://${location.host}${location.pathname}`,
311+ );
249312 };
250313
251314 const trackers = [];
252315
253−page.track = function(fn) {
316+page.track = function (fn) {
254317 trackers.push(fn);
255318 };
256319
257−var track = function() {
258− if (app.config.env !== 'production') { return; }
259− if (navigator.doNotTrack === '1') { return; }
260− if (navigator.globalPrivacyControl) { return; }
320+var track = function () {
321+ if (app.config.env !== "production") {
322+ return;
323+ }
324+ if (navigator.doNotTrack === "1") {
325+ return;
326+ }
327+ if (navigator.globalPrivacyControl) {
328+ return;
329+ }
261330
262− const consentGiven = Cookies.get('analyticsConsent');
263− const consentAsked = Cookies.get('analyticsConsentAsked');
331+ const consentGiven = Cookies.get("analyticsConsent");
332+ const consentAsked = Cookies.get("analyticsConsentAsked");
264333
265− if (consentGiven === '1') {
266− for (var tracker of Array.from(trackers)) { tracker.call(); }
267− } else if ((consentGiven === undefined) && (consentAsked === undefined)) {
334+ if (consentGiven === "1") {
335+ for (var tracker of Array.from(trackers)) {
336+ tracker.call();
337+ }
338+ } else if (consentGiven === undefined && consentAsked === undefined) {
268339 // Only ask for consent once per browser session
269− Cookies.set('analyticsConsentAsked', '1');
340+ Cookies.set("analyticsConsentAsked", "1");
270341
271− new app.views.Notif('AnalyticsConsent', {autoHide: null});
342+ new app.views.Notif("AnalyticsConsent", { autoHide: null });
272343 }
273344 };
274345
275−this.resetAnalytics = function() {
346+this.resetAnalytics = function () {
276347 for (var cookie of Array.from(document.cookie.split(/;\s?/))) {
277− var name = cookie.split('=')[0];
278− if ((name[0] === '_') && (name[1] !== '_')) {
348+ var name = cookie.split("=")[0];
349+ if (name[0] === "_" && name[1] !== "_") {
279350 Cookies.expire(name);
280351 }
281352 }
assets/javascripts/lib/util.js+308−157
@@ -16,32 +16,52 @@ · 15 unmodified lines
1616 //
1717
1818 let smoothDistance, smoothDuration, smoothEnd, smoothStart;
19−this.$ = function(selector, el) {
20− if (el == null) { el = document; }
21− try { return el.querySelector(selector); } catch (error) {}
19+this.$ = function (selector, el) {
20+ if (el == null) {
21+ el = document;
22+ }
23+ try {
24+ return el.querySelector(selector);
25+ } catch (error) {}
2226 };
2327
24−this.$$ = function(selector, el) {
25− if (el == null) { el = document; }
26− try { return el.querySelectorAll(selector); } catch (error) {}
28+this.$$ = function (selector, el) {
29+ if (el == null) {
30+ el = document;
31+ }
32+ try {
33+ return el.querySelectorAll(selector);
34+ } catch (error) {}
2735 };
2836
29−$.id = id => document.getElementById(id);
37+$.id = (id) => document.getElementById(id);
3038
31−$.hasChild = function(parent, el) {
32− if (!parent) { return; }
39+$.hasChild = function (parent, el) {
40+ if (!parent) {
41+ return;
42+ }
3343 while (el) {
34− if (el === parent) { return true; }
35− if (el === document.body) { return; }
44+ if (el === parent) {
45+ return true;
46+ }
47+ if (el === document.body) {
48+ return;
49+ }
3650 el = el.parentNode;
3751 }
3852 };
3953
40−$.closestLink = function(el, parent) {
41− if (parent == null) { parent = document.body; }
54+$.closestLink = function (el, parent) {
55+ if (parent == null) {
56+ parent = document.body;
57+ }
4258 while (el) {
43− if (el.tagName === 'A') { return el; }
44− if (el === parent) { return; }
59+ if (el.tagName === "A") {
60+ return el;
61+ }
62+ if (el === parent) {
63+ return;
64+ }
4565 el = el.parentNode;
4666 }
4767 };
@@ -50,55 +70,85 @@ $.closestLink = function(el, parent) { · 2 unmodified lines
5070 // Events
5171 //
5272
53−$.on = function(el, event, callback, useCapture) {
54− if (useCapture == null) { useCapture = false; }
55− if (event.indexOf(' ') >= 0) {
56− for (var name of Array.from(event.split(' '))) { $.on(el, name, callback); }
73+$.on = function (el, event, callback, useCapture) {
74+ if (useCapture == null) {
75+ useCapture = false;
76+ }
77+ if (event.indexOf(" ") >= 0) {
78+ for (var name of Array.from(event.split(" "))) {
79+ $.on(el, name, callback);
80+ }
5781 } else {
5882 el.addEventListener(event, callback, useCapture);
5983 }
6084 };
6185
62−$.off = function(el, event, callback, useCapture) {
63− if (useCapture == null) { useCapture = false; }
64− if (event.indexOf(' ') >= 0) {
65− for (var name of Array.from(event.split(' '))) { $.off(el, name, callback); }
86+$.off = function (el, event, callback, useCapture) {
87+ if (useCapture == null) {
88+ useCapture = false;
89+ }
90+ if (event.indexOf(" ") >= 0) {
91+ for (var name of Array.from(event.split(" "))) {
92+ $.off(el, name, callback);
93+ }
6694 } else {
6795 el.removeEventListener(event, callback, useCapture);
6896 }
6997 };
7098
71−$.trigger = function(el, type, canBubble, cancelable) {
72− if (canBubble == null) { canBubble = true; }
73− if (cancelable == null) { cancelable = true; }
74− const event = document.createEvent('Event');
99+$.trigger = function (el, type, canBubble, cancelable) {
100+ if (canBubble == null) {
101+ canBubble = true;
102+ }
103+ if (cancelable == null) {
104+ cancelable = true;
105+ }
106+ const event = document.createEvent("Event");
75107 event.initEvent(type, canBubble, cancelable);
76108 el.dispatchEvent(event);
77109 };
78110
79−$.click = function(el) {
80− const event = document.createEvent('MouseEvent');
81− event.initMouseEvent('click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null);
111+$.click = function (el) {
112+ const event = document.createEvent("MouseEvent");
113+ event.initMouseEvent(
114+ "click",
115+ true,
116+ true,
117+ window,
118+ null,
119+ 0,
120+ 0,
121+ 0,
122+ 0,
123+ false,
124+ false,
125+ false,
126+ false,
127+ 0,
128+ null,
129+ );
82130 el.dispatchEvent(event);
83131 };
84132
85−$.stopEvent = function(event) {
133+$.stopEvent = function (event) {
86134 event.preventDefault();
87135 event.stopPropagation();
88136 event.stopImmediatePropagation();
89137 };
90138
91−$.eventTarget = event => event.target.correspondingUseElement || event.target;
139+$.eventTarget = (event) => event.target.correspondingUseElement || event.target;
92140
93141 //
94142 // Manipulation
95143 //
96144
97−const buildFragment = function(value) {
145+const buildFragment = function (value) {
98146 const fragment = document.createDocumentFragment();
99147
100148 if ($.isCollection(value)) {
101− for (var child of Array.from($.makeArray(value))) { fragment.appendChild(child); }
149+ for (var child of Array.from($.makeArray(value))) {
150+ fragment.appendChild(child);
151+ }
102152 } else {
103153 fragment.innerHTML = value;
104154 }
@@ -106,36 +156,40 @@ const buildFragment = function(value) { · 1 unmodified line
106156 return fragment;
107157 };
108158
109−$.append = function(el, value) {
110− if (typeof value === 'string') {
111− el.insertAdjacentHTML('beforeend', value);
159+$.append = function (el, value) {
160+ if (typeof value === "string") {
161+ el.insertAdjacentHTML("beforeend", value);
112162 } else {
113− if ($.isCollection(value)) { value = buildFragment(value); }
163+ if ($.isCollection(value)) {
164+ value = buildFragment(value);
165+ }
114166 el.appendChild(value);
115167 }
116168 };
117169
118−$.prepend = function(el, value) {
170+$.prepend = function (el, value) {
119171 if (!el.firstChild) {
120172 $.append(value);
121− } else if (typeof value === 'string') {
122− el.insertAdjacentHTML('afterbegin', value);
173+ } else if (typeof value === "string") {
174+ el.insertAdjacentHTML("afterbegin", value);
123175 } else {
124− if ($.isCollection(value)) { value = buildFragment(value); }
176+ if ($.isCollection(value)) {
177+ value = buildFragment(value);
178+ }
125179 el.insertBefore(value, el.firstChild);
126180 }
127181 };
128182
129−$.before = function(el, value) {
130− if ((typeof value === 'string') || $.isCollection(value)) {
183+$.before = function (el, value) {
184+ if (typeof value === "string" || $.isCollection(value)) {
131185 value = buildFragment(value);
132186 }
133187
134188 el.parentNode.insertBefore(value, el);
135189 };
136190
137−$.after = function(el, value) {
138− if ((typeof value === 'string') || $.isCollection(value)) {
191+$.after = function (el, value) {
192+ if (typeof value === "string" || $.isCollection(value)) {
139193 value = buildFragment(value);
140194 }
141195
@@ -146,11 +200,13 @@ $.after = function(el, value) { · 4 unmodified lines
146200 }
147201 };
148202
149−$.remove = function(value) {
203+$.remove = function (value) {
150204 if ($.isCollection(value)) {
151− for (var el of Array.from($.makeArray(value))) { if (el.parentNode != null) {
152− el.parentNode.removeChild(el);
153− } }
205+ for (var el of Array.from($.makeArray(value))) {
206+ if (el.parentNode != null) {
207+ el.parentNode.removeChild(el);
208+ }
209+ }
154210 } else {
155211 if (value.parentNode != null) {
156212 value.parentNode.removeChild(value);
@@ -158,13 +214,15 @@ $.remove = function(value) { · 1 unmodified line
158214 }
159215 };
160216
161−$.empty = function(el) {
162− while (el.firstChild) { el.removeChild(el.firstChild); }
217+$.empty = function (el) {
218+ while (el.firstChild) {
219+ el.removeChild(el.firstChild);
220+ }
163221 };
164222
165223 // Calls the function while the element is off the DOM to avoid triggering
166224 // unnecessary reflows and repaints.
167−$.batchUpdate = function(el, fn) {
225+$.batchUpdate = function (el, fn) {
168226 const parent = el.parentNode;
169227 const sibling = el.nextSibling;
170228 parent.removeChild(el);
@@ -182,14 +240,16 @@ $.batchUpdate = function(el, fn) { · 11 unmodified lines
182240 // Offset
183241 //
184242
185−$.rect = el => el.getBoundingClientRect();
243+$.rect = (el) => el.getBoundingClientRect();
186244
187−$.offset = function(el, container) {
188− if (container == null) { container = document.body; }
245+$.offset = function (el, container) {
246+ if (container == null) {
247+ container = document.body;
248+ }
189249 let top = 0;
190250 let left = 0;
191251
192− while (el && (el !== container)) {
252+ while (el && el !== container) {
193253 top += el.offsetTop;
194254 left += el.offsetLeft;
195255 el = el.offsetParent;
@@ -197,102 +257,131 @@ $.offset = function(el, container) { · 1 unmodified line
197257
198258 return {
199259 top,
200− left
260+ left,
201261 };
202262 };
203263
204−$.scrollParent = function(el) {
205− while ((el = el.parentNode) && (el.nodeType === 1)) {
264+$.scrollParent = function (el) {
265+ while ((el = el.parentNode) && el.nodeType === 1) {
206266 var needle;
207− if (el.scrollTop > 0) { break; }
208− if ((needle = __guard__(getComputedStyle(el), x => x.overflowY), ['auto', 'scroll'].includes(needle))) { break; }
267+ if (el.scrollTop > 0) {
268+ break;
269+ }
270+ if (
271+ ((needle = __guard__(getComputedStyle(el), (x) => x.overflowY)),
272+ ["auto", "scroll"].includes(needle))
273+ ) {
274+ break;
275+ }
209276 }
210277 return el;
211278 };
212279
213−$.scrollTo = function(el, parent, position, options) {
214− if (position == null) { position = 'center'; }
215− if (options == null) { options = {}; }
216− if (!el) { return; }
280+$.scrollTo = function (el, parent, position, options) {
281+ if (position == null) {
282+ position = "center";
283+ }
284+ if (options == null) {
285+ options = {};
286+ }
287+ if (!el) {
288+ return;
289+ }
217290
218− if (parent == null) { parent = $.scrollParent(el); }
219− if (!parent) { return; }
291+ if (parent == null) {
292+ parent = $.scrollParent(el);
293+ }
294+ if (!parent) {
295+ return;
296+ }
220297
221298 const parentHeight = parent.clientHeight;
222299 const parentScrollHeight = parent.scrollHeight;
223− if (!(parentScrollHeight > parentHeight)) { return; }
300+ if (!(parentScrollHeight > parentHeight)) {
301+ return;
302+ }
224303
225− const {
226− top
227− } = $.offset(el, parent);
228− const {
229− offsetTop
230− } = parent.firstElementChild;
304+ const { top } = $.offset(el, parent);
305+ const { offsetTop } = parent.firstElementChild;
231306
232307 switch (position) {
233− case 'top':
234− parent.scrollTop = top - offsetTop - ((options.margin != null) ? options.margin : 0);
308+ case "top":
309+ parent.scrollTop =
310+ top - offsetTop - (options.margin != null ? options.margin : 0);
235311 break;
236− case 'center':
237− parent.scrollTop = top - Math.round((parentHeight / 2) - (el.offsetHeight / 2));
312+ case "center":
313+ parent.scrollTop =
314+ top - Math.round(parentHeight / 2 - el.offsetHeight / 2);
238315 break;
239− case 'continuous':
240− var {
241− scrollTop
242− } = parent;
316+ case "continuous":
317+ var { scrollTop } = parent;
243318 var height = el.offsetHeight;
244319
245− var lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight;
246− var offsetBottom = lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0;
320+ var lastElementOffset =
321+ parent.lastElementChild.offsetTop +
322+ parent.lastElementChild.offsetHeight;
323+ var offsetBottom =
324+ lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0;
247325
248326 // If the target element is above the visible portion of its scrollable
249327 // ancestor, move it near the top with a gap = options.topGap * target's height.
250− if ((top - offsetTop) <= (scrollTop + (height * (options.topGap || 1)))) {
251− parent.scrollTop = top - offsetTop - (height * (options.topGap || 1));
252− // If the target element is below the visible portion of its scrollable
253− // ancestor, move it near the bottom with a gap = options.bottomGap * target's height.
254− } else if ((top + offsetBottom) >= ((scrollTop + parentHeight) - (height * ((options.bottomGap || 1) + 1)))) {
255− parent.scrollTop = ((top + offsetBottom) - parentHeight) + (height * ((options.bottomGap || 1) + 1));
328+ if (top - offsetTop <= scrollTop + height * (options.topGap || 1)) {
329+ parent.scrollTop = top - offsetTop - height * (options.topGap || 1);
330+ // If the target element is below the visible portion of its scrollable
331+ // ancestor, move it near the bottom with a gap = options.bottomGap * target's height.
332+ } else if (
333+ top + offsetBottom >=
334+ scrollTop + parentHeight - height * ((options.bottomGap || 1) + 1)
335+ ) {
336+ parent.scrollTop =
337+ top +
338+ offsetBottom -
339+ parentHeight +
340+ height * ((options.bottomGap || 1) + 1);
256341 }
257342 break;
258343 }
259344 };
260345
261−$.scrollToWithImageLock = function(el, parent, ...args) {
262− if (parent == null) { parent = $.scrollParent(el); }
263− if (!parent) { return; }
346+$.scrollToWithImageLock = function (el, parent, ...args) {
347+ if (parent == null) {
348+ parent = $.scrollParent(el);
349+ }
350+ if (!parent) {
351+ return;
352+ }
264353
265354 $.scrollTo(el, parent, ...Array.from(args));
266355
267356 // Lock the scroll position on the target element for up to 3 seconds while
268357 // nearby images are loaded and rendered.
269− for (var image of Array.from(parent.getElementsByTagName('img'))) {
358+ for (var image of Array.from(parent.getElementsByTagName("img"))) {
270359 if (!image.complete) {
271− (function() {
360+ (function () {
272361 let timeout;
273− const onLoad = function(event) {
362+ const onLoad = function (event) {
274363 clearTimeout(timeout);
275364 unbind(event.target);
276365 return $.scrollTo(el, parent, ...Array.from(args));
277366 };
278367
279− var unbind = target => $.off(target, 'load', onLoad);
368+ var unbind = (target) => $.off(target, "load", onLoad);
280369
281− $.on(image, 'load', onLoad);
282− return timeout = setTimeout(unbind.bind(null, image), 3000);
370+ $.on(image, "load", onLoad);
371+ return (timeout = setTimeout(unbind.bind(null, image), 3000));
283372 })();
284373 }
285374 }
286375 };
287376
288377 // Calls the function while locking the element's position relative to the window.
289−$.lockScroll = function(el, fn) {
378+$.lockScroll = function (el, fn) {
290379 let parent;
291− if (parent = $.scrollParent(el)) {
292− let {
293− top
294− } = $.rect(el);
295− if (![document.body, document.documentElement].includes(parent)) { top -= $.rect(parent).top; }
380+ if ((parent = $.scrollParent(el))) {
381+ let { top } = $.rect(el);
382+ if (![document.body, document.documentElement].includes(parent)) {
383+ top -= $.rect(parent).top;
384+ }
296385 fn();
297386 parent.scrollTop = $.offset(el, parent).top - top;
298387 } else {
@@ -300,9 +389,14 @@ $.lockScroll = function(el, fn) { · 1 unmodified line
300389 }
301390 };
302391
303−let smoothScroll = (smoothStart = (smoothEnd = (smoothDistance = (smoothDuration = null))));
392+let smoothScroll =
393+ (smoothStart =
394+ smoothEnd =
395+ smoothDistance =
396+ smoothDuration =
397+ null);
304398
305−$.smoothScroll = function(el, end) {
399+$.smoothScroll = function (el, end) {
306400 if (!window.requestAnimationFrame) {
307401 el.scrollTop = end;
308402 return;
@@ -322,12 +416,18 @@ $.smoothScroll = function(el, end) { · 13 unmodified lines
322416 smoothDuration = Math.min(300, Math.abs(smoothDistance));
323417 const startTime = Date.now();
324418
325− smoothScroll = function() {
419+ smoothScroll = function () {
326420 const p = Math.min(1, (Date.now() - startTime) / smoothDuration);
327− const y = Math.max(0, Math.floor(smoothStart + (smoothDistance * (p < 0.5 ? 2 * p * p : (p * (4 - (p * 2))) - 1))));
421+ const y = Math.max(
422+ 0,
423+ Math.floor(
424+ smoothStart +
425+ smoothDistance * (p < 0.5 ? 2 * p * p : p * (4 - p * 2) - 1),
426+ ),
427+ );
328428 el.scrollTop = y;
329429 if (p === 1) {
330− return smoothScroll = null;
430+ return (smoothScroll = null);
331431 } else {
332432 return requestAnimationFrame(smoothScroll);
333433 }
@@ -339,7 +439,7 @@ $.smoothScroll = function(el, end) { · 5 unmodified lines
339439 // Utilities
340440 //
341441
342−$.extend = function(target, ...objects) {
442+$.extend = function (target, ...objects) {
343443 for (var object of Array.from(objects)) {
344444 if (object) {
345445 for (var key in object) {
@@ -351,7 +451,7 @@ $.extend = function(target, ...objects) { · 5 unmodified lines
351451 return target;
352452 };
353453
354−$.makeArray = function(object) {
454+$.makeArray = function (object) {
355455 if (Array.isArray(object)) {
356456 return object;
357457 } else {
@@ -359,7 +459,7 @@ $.makeArray = function(object) { · 1 unmodified line
359459 }
360460 };
361461
362−$.arrayDelete = function(array, object) {
462+$.arrayDelete = function (array, object) {
363463 const index = array.indexOf(object);
364464 if (index >= 0) {
365465 array.splice(index, 1);
@@ -370,45 +470,49 @@ $.arrayDelete = function(array, object) { · 4 unmodified lines
370470 };
371471
372472 // Returns true if the object is an array or a collection of DOM elements.
373−$.isCollection = object => Array.isArray(object) || (typeof (object != null ? object.item : undefined) === 'function');
473+$.isCollection = (object) =>
474+ Array.isArray(object) ||
475+ typeof (object != null ? object.item : undefined) === "function";
374476
375477 const ESCAPE_HTML_MAP = {
376− '&': '&amp;',
377− '<': '&lt;',
378− '>': '&gt;',
379− '"': '&quot;',
380− "'": '&#x27;',
381− '/': '&#x2F;'
478+ "&": "&amp;",
479+ "<": "&lt;",
480+ ">": "&gt;",
481+ '"': "&quot;",
482+ "'": "&#x27;",
483+ "/": "&#x2F;",
382484 };
383485
384486 const ESCAPE_HTML_REGEXP = /[&<>"'\/]/g;
385487
386−$.escape = string => string.replace(ESCAPE_HTML_REGEXP, match => ESCAPE_HTML_MAP[match]);
488+$.escape = (string) =>
489+ string.replace(ESCAPE_HTML_REGEXP, (match) => ESCAPE_HTML_MAP[match]);
387490
388491 const ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g;
389492
390−$.escapeRegexp = string => string.replace(ESCAPE_REGEXP, "\\$1");
493+$.escapeRegexp = (string) => string.replace(ESCAPE_REGEXP, "\\$1");
391494
392−$.urlDecode = string => decodeURIComponent(string.replace(/\+/g, '%20'));
495+$.urlDecode = (string) => decodeURIComponent(string.replace(/\+/g, "%20"));
393496
394−$.classify = function(string) {
395− string = string.split('_');
497+$.classify = function (string) {
498+ string = string.split("_");
396499 for (let i = 0; i < string.length; i++) {
397500 var substr = string[i];
398501 string[i] = substr[0].toUpperCase() + substr.slice(1);
399502 }
400− return string.join('');
503+ return string.join("");
401504 };
402505
403−$.framify = function(fn, obj) {
506+$.framify = function (fn, obj) {
404507 if (window.requestAnimationFrame) {
405− return (...args) => requestAnimationFrame(fn.bind(obj, ...Array.from(args)));
508+ return (...args) =>
509+ requestAnimationFrame(fn.bind(obj, ...Array.from(args)));
406510 } else {
407511 return fn;
408512 }
409513 };
410514
411−$.requestAnimationFrame = function(fn) {
515+$.requestAnimationFrame = function (fn) {
412516 if (window.requestAnimationFrame) {
413517 requestAnimationFrame(fn);
414518 } else {
@@ -420,37 +524,81 @@ $.requestAnimationFrame = function(fn) { · 5 unmodified lines
420524 // Miscellaneous
421525 //
422526
423−$.noop = function() {};
527+$.noop = function () {};
424528
425−$.popup = function(value) {
529+$.popup = function (value) {
426530 try {
427531 const win = window.open();
428− if (win.opener) { win.opener = null; }
532+ if (win.opener) {
533+ win.opener = null;
534+ }
429535 win.location = value.href || value;
430536 } catch (error) {
431− window.open(value.href || value, '_blank');
537+ window.open(value.href || value, "_blank");
432538 }
433539 };
434540
435541 let isMac = null;
436−$.isMac = () => isMac != null ? isMac : (isMac = (navigator.userAgent != null ? navigator.userAgent.indexOf('Mac') : undefined) >= 0);
542+$.isMac = () =>
543+ isMac != null
544+ ? isMac
545+ : (isMac =
546+ (navigator.userAgent != null
547+ ? navigator.userAgent.indexOf("Mac")
548+ : undefined) >= 0);
437549
438550 let isIE = null;
439−$.isIE = () => isIE != null ? isIE : (isIE = ((navigator.userAgent != null ? navigator.userAgent.indexOf('MSIE') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('rv:11.0') : undefined) >= 0));
551+$.isIE = () =>
552+ isIE != null
553+ ? isIE
554+ : (isIE =
555+ (navigator.userAgent != null
556+ ? navigator.userAgent.indexOf("MSIE")
557+ : undefined) >= 0 ||
558+ (navigator.userAgent != null
559+ ? navigator.userAgent.indexOf("rv:11.0")
560+ : undefined) >= 0);
440561
441562 let isChromeForAndroid = null;
442−$.isChromeForAndroid = () => isChromeForAndroid != null ? isChromeForAndroid : (isChromeForAndroid = ((navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0) && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent));
563+$.isChromeForAndroid = () =>
564+ isChromeForAndroid != null
565+ ? isChromeForAndroid
566+ : (isChromeForAndroid =
567+ (navigator.userAgent != null
568+ ? navigator.userAgent.indexOf("Android")
569+ : undefined) >= 0 &&
570+ /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent));
443571
444572 let isAndroid = null;
445−$.isAndroid = () => isAndroid != null ? isAndroid : (isAndroid = (navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0);
573+$.isAndroid = () =>
574+ isAndroid != null
575+ ? isAndroid
576+ : (isAndroid =
577+ (navigator.userAgent != null
578+ ? navigator.userAgent.indexOf("Android")
579+ : undefined) >= 0);
446580
447581 let isIOS = null;
448−$.isIOS = () => isIOS != null ? isIOS : (isIOS = ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPhone') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPad') : undefined) >= 0));
449−
450−$.overlayScrollbarsEnabled = function() {
451− if (!$.isMac()) { return false; }
452− const div = document.createElement('div');
453− div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute');
582+$.isIOS = () =>
583+ isIOS != null
584+ ? isIOS
585+ : (isIOS =
586+ (navigator.userAgent != null
587+ ? navigator.userAgent.indexOf("iPhone")
588+ : undefined) >= 0 ||
589+ (navigator.userAgent != null
590+ ? navigator.userAgent.indexOf("iPad")
591+ : undefined) >= 0);
592+
593+$.overlayScrollbarsEnabled = function () {
594+ if (!$.isMac()) {
595+ return false;
596+ }
597+ const div = document.createElement("div");
598+ div.setAttribute(
599+ "style",
600+ "width: 100px; height: 100px; overflow: scroll; position: absolute",
601+ );
454602 document.body.appendChild(div);
455603 const result = div.offsetWidth === div.clientWidth;
456604 document.body.removeChild(div);
@@ -458,36 +606,39 @@ $.overlayScrollbarsEnabled = function() { · 1 unmodified line
458606 };
459607
460608 const HIGHLIGHT_DEFAULTS = {
461− className: 'highlight',
462− delay: 1000
609+ className: "highlight",
610+ delay: 1000,
463611 };
464612
465−$.highlight = function(el, options) {
466− if (options == null) { options = {}; }
613+$.highlight = function (el, options) {
614+ if (options == null) {
615+ options = {};
616+ }
467617 options = $.extend({}, HIGHLIGHT_DEFAULTS, options);
468618 el.classList.add(options.className);
469− setTimeout((() => el.classList.remove(options.className)), options.delay);
619+ setTimeout(() => el.classList.remove(options.className), options.delay);
470620 };
471621
472−$.copyToClipboard = function(string) {
622+$.copyToClipboard = function (string) {
473623 let result;
474− const textarea = document.createElement('textarea');
475− textarea.style.position = 'fixed';
624+ const textarea = document.createElement("textarea");
625+ textarea.style.position = "fixed";
476626 textarea.style.opacity = 0;
477627 textarea.value = string;
478628 document.body.appendChild(textarea);
479629 try {
480630 textarea.select();
481− result = !!document.execCommand('copy');
631+ result = !!document.execCommand("copy");
482632 } catch (error) {
483633 result = false;
484− }
485− finally {
634+ } finally {
486635 document.body.removeChild(textarea);
487636 }
488637 return result;
489638 };
490639
491640 function __guard__(value, transform) {
492− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
493−}
641+ return typeof value !== "undefined" && value !== null
642+ ? transform(value)
643+ : undefined;
644+}
assets/javascripts/models/doc.js+63−29
@@ -12,10 +12,12 @@ app.models.Doc = class Doc extends app.Model { · 11 unmodified lines
1212 constructor() {
1313 super(...arguments);
1414 this.reset(this);
15− this.slug_without_version = this.slug.split('~')[0];
16− this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : '');
15+ this.slug_without_version = this.slug.split("~")[0];
16+ this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : "");
1717 this.icon = this.slug_without_version;
18− if (this.version) { this.short_version = this.version.split(' ')[0]; }
18+ if (this.version) {
19+ this.short_version = this.version.split(" ")[0];
20+ }
1921 this.text = this.toEntry().text;
2022 }
2123
@@ -26,17 +28,25 @@ app.models.Doc = class Doc extends app.Model { · 4 unmodified lines
2628
2729 resetEntries(entries) {
2830 this.entries = new app.collections.Entries(entries);
29− this.entries.each(entry => { return entry.doc = this; });
31+ this.entries.each((entry) => {
32+ return (entry.doc = this);
33+ });
3034 }
3135
3236 resetTypes(types) {
3337 this.types = new app.collections.Types(types);
34− this.types.each(type => { return type.doc = this; });
38+ this.types.each((type) => {
39+ return (type.doc = this);
40+ });
3541 }
3642
3743 fullPath(path) {
38− if (path == null) { path = ''; }
39− if (path[0] !== '/') { path = `/${path}`; }
44+ if (path == null) {
45+ path = "";
46+ }
47+ if (path[0] !== "/") {
48+ path = `/${path}`;
49+ }
4050 return `/${this.slug}${path}`;
4151 }
4252
@@ -49,45 +59,57 @@ app.models.Doc = class Doc extends app.Model { · 6 unmodified lines
4959 }
5060
5161 indexUrl() {
52− return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${this.mtime}`;
62+ return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${
63+ this.mtime
64+ }`;
5365 }
5466
5567 toEntry() {
56− if (this.entry) { return this.entry; }
68+ if (this.entry) {
69+ return this.entry;
70+ }
5771 this.entry = new app.models.Entry({
5872 doc: this,
5973 name: this.fullName,
60− path: 'index'
74+ path: "index",
6175 });
62− if (this.version) { this.entry.addAlias(this.name); }
76+ if (this.version) {
77+ this.entry.addAlias(this.name);
78+ }
6379 return this.entry;
6480 }
6581
6682 findEntryByPathAndHash(path, hash) {
6783 let entry;
68− if (hash && (entry = this.entries.findBy('path', `${path}#${hash}`))) {
84+ if (hash && (entry = this.entries.findBy("path", `${path}#${hash}`))) {
6985 return entry;
70− } else if (path === 'index') {
86+ } else if (path === "index") {
7187 return this.toEntry();
7288 } else {
73− return this.entries.findBy('path', path);
89+ return this.entries.findBy("path", path);
7490 }
7591 }
7692
7793 load(onSuccess, onError, options) {
78− if (options == null) { options = {}; }
79− if (options.readCache && this._loadFromCache(onSuccess)) { return; }
94+ if (options == null) {
95+ options = {};
96+ }
97+ if (options.readCache && this._loadFromCache(onSuccess)) {
98+ return;
99+ }
80100
81− const callback = data => {
101+ const callback = (data) => {
82102 this.reset(data);
83103 onSuccess();
84− if (options.writeCache) { this._setCache(data); }
104+ if (options.writeCache) {
105+ this._setCache(data);
106+ }
85107 };
86108
87109 return ajax({
88110 url: this.indexUrl(),
89111 success: callback,
90− error: onError
112+ error: onError,
91113 });
92114 }
93115
@@ -97,7 +119,9 @@ app.models.Doc = class Doc extends app.Model { · 3 unmodified lines
97119
98120 _loadFromCache(onSuccess) {
99121 let data;
100− if (!(data = this._getCache())) { return; }
122+ if (!(data = this._getCache())) {
123+ return;
124+ }
101125
102126 const callback = () => {
103127 this.reset(data);
@@ -110,7 +134,9 @@ app.models.Doc = class Doc extends app.Model { · 6 unmodified lines
110134
111135 _getCache() {
112136 let data;
113− if (!(data = app.localStorage.get(this.slug))) { return; }
137+ if (!(data = app.localStorage.get(this.slug))) {
138+ return;
139+ }
114140
115141 if (data[0] === this.mtime) {
116142 return data[1];
@@ -125,7 +151,9 @@ app.models.Doc = class Doc extends app.Model { · 8 unmodified lines
125151 }
126152
127153 install(onSuccess, onError, onProgress) {
128− if (this.installing) { return; }
154+ if (this.installing) {
155+ return;
156+ }
129157 this.installing = true;
130158
131159 const error = () => {
@@ -133,7 +161,7 @@ app.models.Doc = class Doc extends app.Model { · 1 unmodified line
133161 onError();
134162 };
135163
136− const success = data => {
164+ const success = (data) => {
137165 this.installing = null;
138166 app.db.store(this, data, onSuccess, error);
139167 };
@@ -143,12 +171,14 @@ app.models.Doc = class Doc extends app.Model { · 3 unmodified lines
143171 success,
144172 error,
145173 progress: onProgress,
146− timeout: 3600
174+ timeout: 3600,
147175 });
148176 }
149177
150178 uninstall(onSuccess, onError) {
151− if (this.installing) { return; }
179+ if (this.installing) {
180+ return;
181+ }
152182 this.installing = true;
153183
154184 const success = () => {
@@ -165,12 +195,16 @@ app.models.Doc = class Doc extends app.Model { · 10 unmodified lines
165195 }
166196
167197 getInstallStatus(callback) {
168− app.db.version(this, value => callback({installed: !!value, mtime: value}));
198+ app.db.version(this, (value) =>
199+ callback({ installed: !!value, mtime: value }),
200+ );
169201 }
170202
171203 isOutdated(status) {
172− if (!status) { return false; }
173− const isInstalled = status.installed || app.settings.get('autoInstall');
174− return isInstalled && (this.mtime !== status.mtime);
204+ if (!status) {
205+ return false;
206+ }
207+ const isInstalled = status.installed || app.settings.get("autoInstall");
208+ return isInstalled && this.mtime !== status.mtime;
175209 }
176210 };
assets/javascripts/models/entry.js+51−48
@@ -8,63 +8,62 @@ · 7 unmodified lines
88 */
99 //= require app/searcher
1010
11−(function() {
11+(function () {
1212 let applyAliases = undefined;
1313 const Cls = (app.models.Entry = class Entry extends app.Model {
1414 static initClass() {
15−
1615 let ALIASES;
17− applyAliases = function(string) {
16+ applyAliases = function (string) {
1817 if (ALIASES.hasOwnProperty(string)) {
1918 return [string, ALIASES[string]];
2019 } else {
21− const words = string.split('.');
20+ const words = string.split(".");
2221 for (let i = 0; i < words.length; i++) {
2322 var word = words[i];
2423 if (ALIASES.hasOwnProperty(word)) {
2524 words[i] = ALIASES[word];
26− return [string, words.join('.')];
25+ return [string, words.join(".")];
2726 }
2827 }
2928 }
3029 return string;
3130 };
32−
33− this.ALIASES = (ALIASES = {
34− 'angular': 'ng',
35− 'angular.js': 'ng',
36− 'backbone.js': 'bb',
37− 'c++': 'cpp',
38− 'coffeescript': 'cs',
39− 'crystal': 'cr',
40− 'elixir': 'ex',
41− 'javascript': 'js',
42− 'julia': 'jl',
43− 'jquery': '$',
44− 'knockout.js': 'ko',
45− 'kubernetes': 'k8s',
46− 'less': 'ls',
47− 'lodash': '_',
48− 'löve': 'love',
49− 'marionette': 'mn',
50− 'markdown': 'md',
51− 'matplotlib': 'mpl',
52− 'modernizr': 'mdr',
53− 'moment.js': 'mt',
54− 'openjdk': 'java',
55− 'nginx': 'ngx',
56− 'numpy': 'np',
57− 'pandas': 'pd',
58− 'postgresql': 'pg',
59− 'python': 'py',
60− 'ruby.on.rails': 'ror',
61− 'ruby': 'rb',
62− 'rust': 'rs',
63− 'sass': 'scss',
64− 'tensorflow': 'tf',
65− 'typescript': 'ts',
66− 'underscore.js': '_'
67− });
31+
32+ this.ALIASES = ALIASES = {
33+ angular: "ng",
34+ "angular.js": "ng",
35+ "backbone.js": "bb",
36+ "c++": "cpp",
37+ coffeescript: "cs",
38+ crystal: "cr",
39+ elixir: "ex",
40+ javascript: "js",
41+ julia: "jl",
42+ jquery: "$",
43+ "knockout.js": "ko",
44+ kubernetes: "k8s",
45+ less: "ls",
46+ lodash: "_",
47+ löve: "love",
48+ marionette: "mn",
49+ markdown: "md",
50+ matplotlib: "mpl",
51+ modernizr: "mdr",
52+ "moment.js": "mt",
53+ openjdk: "java",
54+ nginx: "ngx",
55+ numpy: "np",
56+ pandas: "pd",
57+ postgresql: "pg",
58+ python: "py",
59+ "ruby.on.rails": "ror",
60+ ruby: "rb",
61+ rust: "rs",
62+ sass: "scss",
63+ tensorflow: "tf",
64+ typescript: "ts",
65+ "underscore.js": "_",
66+ };
6867 }
6968 // Attributes: name, type, path
7069
@@ -75,16 +74,18 @@ · 4 unmodified lines
7574
7675 addAlias(name) {
7776 const text = applyAliases(app.Searcher.normalizeString(name));
78− if (!Array.isArray(this.text)) { this.text = [this.text]; }
77+ if (!Array.isArray(this.text)) {
78+ this.text = [this.text];
79+ }
7980 this.text.push(Array.isArray(text) ? text[1] : text);
8081 }
8182
8283 fullPath() {
83− return this.doc.fullPath(this.isIndex() ? '' : this.path);
84+ return this.doc.fullPath(this.isIndex() ? "" : this.path);
8485 }
8586
8687 dbPath() {
87− return this.path.replace(/#.*/, '');
88+ return this.path.replace(/#.*/, "");
8889 }
8990
9091 filePath() {
@@ -96,17 +97,19 @@ · 5 unmodified lines
9697 }
9798
9899 _filePath() {
99− let result = this.path.replace(/#.*/, '');
100− if (result.slice(-5) !== '.html') { result += '.html'; }
100+ let result = this.path.replace(/#.*/, "");
101+ if (result.slice(-5) !== ".html") {
102+ result += ".html";
103+ }
101104 return result;
102105 }
103106
104107 isIndex() {
105− return this.path === 'index';
108+ return this.path === "index";
106109 }
107110
108111 getType() {
109− return this.doc.types.findBy('name', this.type);
112+ return this.doc.types.findBy("name", this.type);
110113 }
111114
112115 loadFile(onSuccess, onError) {
assets/javascripts/models/model.js+4−1
@@ -2,6 +2,9 @@ · 1 unmodified line
22 // Sanity-check the conversion and remove this comment.
33 app.Model = class Model {
44 constructor(attributes) {
5− for (var key in attributes) { var value = attributes[key]; this[key] = value; }
5+ for (var key in attributes) {
6+ var value = attributes[key];
7+ this[key] = value;
8+ }
69 }
710 };
assets/javascripts/models/type.js+2−2
@@ -13,14 +13,14 @@ app.models.Type = class Type extends app.Model { · 12 unmodified lines
1313 }
1414
1515 entries() {
16− return this.doc.entries.findAllBy('type', this.name);
16+ return this.doc.entries.findAllBy("type", this.name);
1717 }
1818
1919 toEntry() {
2020 return new app.models.Entry({
2121 doc: this.doc,
2222 name: `${this.doc.name} / ${this.name}`,
23− path: '..' + this.fullPath()
23+ path: ".." + this.fullPath(),
2424 });
2525 }
2626 };
assets/javascripts/templates/base.js+6−4
@@ -6,14 +6,16 @@ · 5 unmodified lines
66 * DS102: Remove unnecessary code created because of implicit returns
77 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
88 */
9−app.templates.render = function(name, value, ...args) {
9+app.templates.render = function (name, value, ...args) {
1010 const template = app.templates[name];
1111
1212 if (Array.isArray(value)) {
13− let result = '';
14− for (var val of Array.from(value)) { result += template(val, ...Array.from(args)); }
13+ let result = "";
14+ for (var val of Array.from(value)) {
15+ result += template(val, ...Array.from(args));
16+ }
1517 return result;
16− } else if (typeof template === 'function') {
18+ } else if (typeof template === "function") {
1719 return template(value, ...Array.from(args));
1820 } else {
1921 return template;
assets/javascripts/templates/error_tmpl.js+53−36
@@ -7,62 +7,79 @@ · 6 unmodified lines
77 * DS207: Consider shorter variations of null checks
88 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
99 */
10−const error = function(title, text, links) {
11− if (text == null) { text = ''; }
12− if (links == null) { links = ''; }
13− if (text) { text = `<p class="_error-text">${text}</p>`; }
14− if (links) { links = `<p class="_error-links">${links}</p>`; }
10+const error = function (title, text, links) {
11+ if (text == null) {
12+ text = "";
13+ }
14+ if (links == null) {
15+ links = "";
16+ }
17+ if (text) {
18+ text = `<p class="_error-text">${text}</p>`;
19+ }
20+ if (links) {
21+ links = `<p class="_error-links">${links}</p>`;
22+ }
1523 return `<div class="_error"><h1 class="_error-title">${title}</h1>${text}${links}</div>`;
1624 };
1725
1826 const back = '<a href="#" data-behavior="back" class="_error-link">Go back</a>';
1927
20−app.templates.notFoundPage = () => error(" Page not found. ",
21− " It may be missing from the source documentation or this could be a bug. ",
22− back);
28+app.templates.notFoundPage = () =>
29+ error(
30+ " Page not found. ",
31+ " It may be missing from the source documentation or this could be a bug. ",
32+ back,
33+ );
2334
24−app.templates.pageLoadError = () => error(" The page failed to load. ",
25− ` It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br>
35+app.templates.pageLoadError = () =>
36+ error(
37+ " The page failed to load. ",
38+ ` It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br>
2639 If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `,
27− ` ${back} &middot; <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a>
28−&middot; <a href="#" class="_error-link" data-retry>Retry</a> `
29−);
40+ ` ${back} &middot; <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a>
41+&middot; <a href="#" class="_error-link" data-retry>Retry</a> `,
42+ );
3043
31−app.templates.bootError = () => error(" The app failed to load. ",
32− ` Check your Internet connection and try <a href="#" data-behavior="reload">reloading</a>.<br>
33−If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `
34−);
44+app.templates.bootError = () =>
45+ error(
46+ " The app failed to load. ",
47+ ` Check your Internet connection and try <a href="#" data-behavior="reload">reloading</a>.<br>
48+If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `,
49+ );
3550
36−app.templates.offlineError = function(reason, exception) {
37− if (reason === 'cookie_blocked') {
51+app.templates.offlineError = function (reason, exception) {
52+ if (reason === "cookie_blocked") {
3853 return error(" Cookies must be enabled to use offline mode. ");
3954 }
4055
41− reason = (() => { switch (reason) {
42− case 'not_supported':
43− return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
56+ reason = (() => {
57+ switch (reason) {
58+ case "not_supported":
59+ return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
4460 Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. `;
45− case 'buggy':
46− return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
61+ case "buggy":
62+ return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
4763 Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. `;
48− case 'private_mode':
49− return ` Your browser appears to be running in private mode.<br>
64+ case "private_mode":
65+ return ` Your browser appears to be running in private mode.<br>
5066 This prevents DevDocs from caching documentations for offline access.`;
51− case 'exception':
52− return ` An error occurred when trying to open the IndexedDB database:<br>
67+ case "exception":
68+ return ` An error occurred when trying to open the IndexedDB database:<br>
5369 <code class="_label">${exception.name}: ${exception.message}</code> `;
54− case 'cant_open':
55− return ` An error occurred when trying to open the IndexedDB database:<br>
70+ case "cant_open":
71+ return ` An error occurred when trying to open the IndexedDB database:<br>
5672 <code class="_label">${exception.name}: ${exception.message}</code><br>
5773 This could be because you're browsing in private mode or have disallowed offline storage on the domain. `;
58− case 'version':
59− return ` The IndexedDB database was modified with a newer version of the app.<br>
74+ case "version":
75+ return ` The IndexedDB database was modified with a newer version of the app.<br>
6076 <a href="#" data-behavior="reload">Reload the page</a> to use offline mode. `;
61− case 'empty':
62− return " The IndexedDB database appears to be corrupted. Try <a href=\"#\" data-behavior=\"reset\">resetting the app</a>. ";
63− } })();
77+ case "empty":
78+ return ' The IndexedDB database appears to be corrupted. Try <a href="#" data-behavior="reset">resetting the app</a>. ';
79+ }
80+ })();
6481
65− return error('Offline mode is unavailable.', reason);
82+ return error("Offline mode is unavailable.", reason);
6683 };
6784
6885 app.templates.unsupportedBrowser = `\
assets/javascripts/templates/notice_tmpl.js+7−7
@@ -5,12 +5,12 @@ · 4 unmodified lines
55 * DS102: Remove unnecessary code created because of implicit returns
66 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
77 */
8−const notice = text => `<p class="_notice-text">${text}</p>`;
8+const notice = (text) => `<p class="_notice-text">${text}</p>`;
99
10−app.templates.singleDocNotice = doc => notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to
11−<a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). `
12−);
10+app.templates.singleDocNotice = (doc) =>
11+ notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to
12+<a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). `);
1313
14−app.templates.disabledDocNotice = () => notice(` <strong>This documentation is disabled.</strong>
15−To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. `
16−);
14+app.templates.disabledDocNotice = () =>
15+ notice(` <strong>This documentation is disabled.</strong>
16+To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. `);
assets/javascripts/templates/notif_tmpl.js+69−34
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 * DS102: Remove unnecessary code created because of implicit returns
77 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
88 */
9−const notif = function(title, html) {
9+const notif = function (title, html) {
1010 html = html.replace(/<a /g, '<a class="_notif-link" ');
1111 return ` <h5 class="_notif-title">${title}</h5>
1212 ${html}
@@ -14,32 +14,56 @@ ${html} · 1 unmodified line
1414 `;
1515 };
1616
17−const textNotif = (title, message) => notif(title, `<p class="_notif-text">${message}`);
17+const textNotif = (title, message) =>
18+ notif(title, `<p class="_notif-text">${message}`);
1819
19−app.templates.notifUpdateReady = () => textNotif("<span data-behavior=\"reboot\">DevDocs has been updated.</span>",
20− "<span data-behavior=\"reboot\"><a href=\"#\" data-behavior=\"reboot\">Reload the page</a> to use the new version.</span>");
20+app.templates.notifUpdateReady = () =>
21+ textNotif(
22+ '<span data-behavior="reboot">DevDocs has been updated.</span>',
23+ '<span data-behavior="reboot"><a href="#" data-behavior="reboot">Reload the page</a> to use the new version.</span>',
24+ );
2125
22−app.templates.notifError = () => textNotif(" Oops, an error occurred. ",
23− ` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists,
26+app.templates.notifError = () =>
27+ textNotif(
28+ " Oops, an error occurred. ",
29+ ` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists,
2430 <a href="#" data-behavior="reset">resetting the app</a>.<br>
25−You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. `
26−);
31+You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. `,
32+ );
2733
28−app.templates.notifQuotaExceeded = () => textNotif(" The offline database has exceeded its size limitation. ",
29− " Unfortunately this quota can't be detected programmatically, and the database can't be opened while over the quota, so it had to be reset. ");
34+app.templates.notifQuotaExceeded = () =>
35+ textNotif(
36+ " The offline database has exceeded its size limitation. ",
37+ " Unfortunately this quota can't be detected programmatically, and the database can't be opened while over the quota, so it had to be reset. ",
38+ );
3039
31−app.templates.notifCookieBlocked = () => textNotif(" Please enable cookies. ",
32− " DevDocs will not work properly if cookies are disabled. ");
40+app.templates.notifCookieBlocked = () =>
41+ textNotif(
42+ " Please enable cookies. ",
43+ " DevDocs will not work properly if cookies are disabled. ",
44+ );
3345
34−app.templates.notifInvalidLocation = () => textNotif(` DevDocs must be loaded from ${app.config.production_host} `,
35− " Otherwise things are likely to break. ");
46+app.templates.notifInvalidLocation = () =>
47+ textNotif(
48+ ` DevDocs must be loaded from ${app.config.production_host} `,
49+ " Otherwise things are likely to break. ",
50+ );
3651
37−app.templates.notifImportInvalid = () => textNotif(" Oops, an error occurred. ",
38− " The file you selected is invalid. ");
52+app.templates.notifImportInvalid = () =>
53+ textNotif(
54+ " Oops, an error occurred. ",
55+ " The file you selected is invalid. ",
56+ );
3957
40−app.templates.notifNews = news => notif('Changelog', `<div class="_notif-content _notif-news">${app.templates.newsList(news, {years: false})}</div>`);
58+app.templates.notifNews = (news) =>
59+ notif(
60+ "Changelog",
61+ `<div class="_notif-content _notif-news">${app.templates.newsList(news, {
62+ years: false,
63+ })}</div>`,
64+ );
4165
42−app.templates.notifUpdates = function(docs, disabledDocs) {
66+app.templates.notifUpdates = function (docs, disabledDocs) {
4367 let doc;
4468 let html = '<div class="_notif-content _notif-news">';
4569
@@ -48,9 +72,11 @@ app.templates.notifUpdates = function(docs, disabledDocs) { · 2 unmodified lines
4872 html += '<ul class="_notif-list">';
4973 for (doc of Array.from(docs)) {
5074 html += `<li>${doc.name}`;
51− if (doc.release) { html += ` <code>&rarr;</code> ${doc.release}`; }
75+ if (doc.release) {
76+ html += ` <code>&rarr;</code> ${doc.release}`;
77+ }
5278 }
53− html += '</ul></div>';
79+ html += "</ul></div>";
5480 }
5581
5682 if (disabledDocs.length > 0) {
@@ -58,26 +84,35 @@ app.templates.notifUpdates = function(docs, disabledDocs) { · 1 unmodified line
5884 html += '<ul class="_notif-list">';
5985 for (doc of Array.from(disabledDocs)) {
6086 html += `<li>${doc.name}`;
61− if (doc.release) { html += ` <code>&rarr;</code> ${doc.release}`; }
62− html += "<span class=\"_notif-info\"><a href=\"/settings\">Enable</a></span>";
87+ if (doc.release) {
88+ html += ` <code>&rarr;</code> ${doc.release}`;
89+ }
90+ html += '<span class="_notif-info"><a href="/settings">Enable</a></span>';
6391 }
64− html += '</ul></div>';
92+ html += "</ul></div>";
6593 }
6694
67− return notif('Updates', `${html}</div>`);
95+ return notif("Updates", `${html}</div>`);
6896 };
6997
70−app.templates.notifShare = () => textNotif(" Hi there! ",
71− ` Like DevDocs? Help us reach more developers by sharing the link with your friends on
98+app.templates.notifShare = () =>
99+ textNotif(
100+ " Hi there! ",
101+ ` Like DevDocs? Help us reach more developers by sharing the link with your friends on
72102 <a href="https://out.devdocs.io/s/tw" target="_blank" rel="noopener">Twitter</a>, <a href="https://out.devdocs.io/s/fb" target="_blank" rel="noopener">Facebook</a>,
73−<a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) `
74−);
103+<a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) `,
104+ );
75105
76−app.templates.notifUpdateDocs = () => textNotif(" Documentation updates available. ",
77− " <a href=\"/offline\">Install them</a> as soon as possible to avoid broken pages. ");
106+app.templates.notifUpdateDocs = () =>
107+ textNotif(
108+ " Documentation updates available. ",
109+ ' <a href="/offline">Install them</a> as soon as possible to avoid broken pages. ',
110+ );
78111
79−app.templates.notifAnalyticsConsent = () => textNotif(" Tracking cookies ",
80− ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information.
112+app.templates.notifAnalyticsConsent = () =>
113+ textNotif(
114+ " Tracking cookies ",
115+ ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information.
81116 Please confirm if you accept our tracking cookies. You can always change your decision in the settings.
82−<br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> `
83−);
117+<br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> `,
118+ );
assets/javascripts/templates/pages/about_tmpl.js+24−13
@@ -8,12 +8,18 @@ · 7 unmodified lines
88 * DS207: Consider shorter variations of null checks
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
11−app.templates.aboutPage = function() {
11+app.templates.aboutPage = function () {
1212 let doc;
13− const all_docs = app.docs.all().concat(...Array.from(app.disabledDocs.all() || []));
13+ const all_docs = app.docs
14+ .all()
15+ .concat(...Array.from(app.disabledDocs.all() || []));
1416 // de-duplicate docs by doc.name
1517 const docs = [];
16− for (doc of Array.from(all_docs)) { if (!(docs.find(d => d.name === doc.name))) { docs.push(doc); } }
18+ for (doc of Array.from(all_docs)) {
19+ if (!docs.find((d) => d.name === doc.name)) {
20+ docs.push(doc);
21+ }
22+ }
1723 return `\
1824 <nav class="_toc" role="directory">
1925 <h3 class="_toc-title">Table of Contents</h3>
@@ -79,18 +85,23 @@ app.templates.aboutPage = function() { · 59 unmodified lines
7985 <th>Documentation
8086 <th>Copyright/License
8187 <th>Source code
82− ${((() => {
83− const result = [];
84−
85− for (doc of Array.from(docs)) { result.push(`<tr> \
86−<td><a href=\"${(doc.links != null ? doc.links.home : undefined)}\">${doc.name}</a></td> \
88+ ${(() => {
89+ const result = [];
90+
91+ for (doc of Array.from(docs)) {
92+ result.push(`<tr> \
93+<td><a href=\"${doc.links != null ? doc.links.home : undefined}\">${
94+ doc.name
95+ }</a></td> \
8796 <td>${doc.attribution}</td> \
88−<td><a href=\"${(doc.links != null ? doc.links.code : undefined)}\">Source code</a></td> \
97+<td><a href=\"${
98+ doc.links != null ? doc.links.code : undefined
99+ }\">Source code</a></td> \
89100 </tr>`);
90− }
91−
92− return result;
93− })()).join('')}
101+ }
102+
103+ return result;
104+ })().join("")}
94105 </table>
95106 </div>
96107
assets/javascripts/templates/pages/help_tmpl.js+35−29
@@ -6,11 +6,11 @@ · 5 unmodified lines
66 * DS205: Consider reworking code to avoid use of IIFEs
77 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
88 */
9−app.templates.helpPage = function() {
9+app.templates.helpPage = function () {
1010 let key, value;
11− const ctrlKey = $.isMac() ? 'cmd' : 'ctrl';
12− const navKey = $.isMac() ? 'cmd' : 'alt';
13− const arrowScroll = app.settings.get('arrowScroll');
11+ const ctrlKey = $.isMac() ? "cmd" : "ctrl";
12+ const navKey = $.isMac() ? "cmd" : "alt";
13+ const arrowScroll = app.settings.get("arrowScroll");
1414
1515 const aliases_one = {};
1616 const aliases_two = {};
@@ -18,7 +18,8 @@ app.templates.helpPage = function() { · 1 unmodified line
1818 const middle = Math.ceil(keys.length / 2) - 1;
1919 for (let i = 0; i < keys.length; i++) {
2020 key = keys[i];
21− (i > middle ? aliases_two : aliases_one)[key] = app.models.Entry.ALIASES[key];
21+ (i > middle ? aliases_two : aliases_one)[key] =
22+ app.models.Entry.ALIASES[key];
2223 }
2324
2425 return `\
@@ -79,12 +80,12 @@ app.templates.helpPage = function() { · 54 unmodified lines
7980 <h3 class="_shortcuts-title">Sidebar</h3>
8081 <dl class="_shortcuts-dl">
8182 <dt class="_shortcuts-dt">
82− ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''}
83+ ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""}
8384 <code class="_shortcut-code">&darr;</code>
8485 <code class="_shortcut-code">&uarr;</code>
8586 <dd class="_shortcuts-dd">Move selection
8687 <dt class="_shortcuts-dt">
87− ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''}
88+ ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""}
8889 <code class="_shortcut-code">&rarr;</code>
8990 <code class="_shortcut-code">&larr;</code>
9091 <dd class="_shortcuts-dd">Show/hide sub-list
@@ -105,15 +106,16 @@ app.templates.helpPage = function() { · 14 unmodified lines
105106 <code class="_shortcut-code">${navKey} + &rarr;</code>
106107 <dd class="_shortcuts-dd">Go back/forward
107108 <dt class="_shortcuts-dt">
108− ${arrowScroll ?
109− '<code class="_shortcut-code">&darr;</code> ' +
109+ ${
110+ arrowScroll
111+ ? '<code class="_shortcut-code">&darr;</code> ' +
110112 '<code class="_shortcut-code">&uarr;</code>'
111− :
112− '<code class="_shortcut-code">alt + &darr;</code> ' +
113+ : '<code class="_shortcut-code">alt + &darr;</code> ' +
113114 '<code class="_shortcut-code">alt + &uarr;</code>' +
114− '<br>' +
115+ "<br>" +
115116 '<code class="_shortcut-code">shift + &darr;</code> ' +
116− '<code class="_shortcut-code">shift + &uarr;</code>'}
117+ '<code class="_shortcut-code">shift + &uarr;</code>'
118+ }
117119 <dd class="_shortcuts-dd">Scroll step by step<br><br>
118120 <dt class="_shortcuts-dt">
119121 <code class="_shortcut-code">space</code>
@@ -167,27 +169,31 @@ app.templates.helpPage = function() { · 47 unmodified lines
167169 <tr>
168170 <th>Word
169171 <th>Alias
170− ${((() => {
171− const result = [];
172− for (key in aliases_one) {
173− value = aliases_one[key];
174− result.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`);
175− }
176− return result;
177− })()).join('')}
172+ ${(() => {
173+ const result = [];
174+ for (key in aliases_one) {
175+ value = aliases_one[key];
176+ result.push(
177+ `<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`,
178+ );
179+ }
180+ return result;
181+ })().join("")}
178182 </table>
179183 <table>
180184 <tr>
181185 <th>Word
182186 <th>Alias
183− ${((() => {
184− const result1 = [];
185− for (key in aliases_two) {
186− value = aliases_two[key];
187− result1.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`);
188− }
189− return result1;
190− })()).join('')}
187+ ${(() => {
188+ const result1 = [];
189+ for (key in aliases_two) {
190+ value = aliases_two[key];
191+ result1.push(
192+ `<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`,
193+ );
194+ }
195+ return result1;
196+ })().join("")}
191197 </table>
192198 </div>
193199 <p>Feel free to suggest new aliases on <a href="https://github.com/freeCodeCamp/devdocs/issues/new">GitHub</a>.\
assets/javascripts/templates/pages/offline_tmpl.js+20−16
@@ -5,12 +5,14 @@ · 4 unmodified lines
55 * DS102: Remove unnecessary code created because of implicit returns
66 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
77 */
8−app.templates.offlinePage = docs => `\
8+app.templates.offlinePage = (docs) => `\
99 <h1 class="_lined-heading">Offline Documentation</h1>
1010
1111 <div class="_docs-tools">
1212 <label>
13− <input type="checkbox" name="autoUpdate" value="1" ${app.settings.get('manualUpdate') ? '' : 'checked'}>Install updates automatically
13+ <input type="checkbox" name="autoUpdate" value="1" ${
14+ app.settings.get("manualUpdate") ? "" : "checked"
15+ }>Install updates automatically
1416 </label>
1517 <div class="_docs-links">
1618 <button type="button" class="_btn-link" data-action-all="install">Install all</button><button type="button" class="_btn-link" data-action-all="update"><strong>Update all</strong></button><button type="button" class="_btn-link" data-action-all="uninstall">Uninstall all</button>
@@ -47,14 +49,15 @@ app.templates.offlinePage = docs => `\ · 30 unmodified lines
4749 </dl>\
4850 `;
4951
50−var canICloseTheTab = function() {
52+var canICloseTheTab = function () {
5153 if (app.ServiceWorker.isEnabled()) {
52− return " Yes! Even offline, you can open a new tab, go to <a href=\"//devdocs.io\">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). ";
54+ return ' Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). ';
5355 } else {
5456 let reason = "aren't available in your browser (or are disabled)";
5557
56− if (app.config.env !== 'production') {
57− reason = "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)";
58+ if (app.config.env !== "production") {
59+ reason =
60+ "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)";
5861 }
5962
6063 return ` No. Service Workers ${reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br>
@@ -62,30 +65,31 @@ The current tab will continue to function even when you go offline (provided you · 1 unmodified line
6265 }
6366 };
6467
65−app.templates.offlineDoc = function(doc, status) {
68+app.templates.offlineDoc = function (doc, status) {
6669 const outdated = doc.isOutdated(status);
6770
6871 let html = `\
69−<tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ''}>
72+<tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ""}>
7073 <td class="_docs-name _icon-${doc.icon}">${doc.fullName}</td>
71− <td class="_docs-size">${Math.ceil(doc.db_size / 100000) / 10}&nbsp;<small>MB</small></td>\
74+ <td class="_docs-size">${
75+ Math.ceil(doc.db_size / 100000) / 10
76+ }&nbsp;<small>MB</small></td>\
7277 `;
7378
74− html += !(status && status.installed) ?
75− `\
79+ html += !(status && status.installed)
80+ ? `\
7681 <td>-</td>
7782 <td><button type="button" class="_btn-link" data-action="install">Install</button></td>\
7883 `
79− : outdated ?
80− `\
84+ : outdated
85+ ? `\
8186 <td><strong>Outdated</strong></td>
8287 <td><button type="button" class="_btn-link _bold" data-action="update">Update</button> - <button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\
8388 `
84− :
85− `\
89+ : `\
8690 <td>Up&#8209;to&#8209;date</td>
8791 <td><button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\
8892 `;
8993
90− return html + '</tr>';
94+ return html + "</tr>";
9195 };
assets/javascripts/templates/pages/settings_tmpl.js+50−19
@@ -7,23 +7,32 @@ · 6 unmodified lines
77 */
88 const themeOption = ({ label, value }, settings) => `\
99 <label class="_settings-label _theme-label">
10− <input type="radio" name="theme" value="${value}"${settings.theme === value ? ' checked' : ''}>
10+ <input type="radio" name="theme" value="${value}"${
11+ settings.theme === value ? " checked" : ""
12+ }>
1113 ${label}
1214 </label>\
1315 `;
1416
15−app.templates.settingsPage = settings => `\
17+app.templates.settingsPage = (settings) => `\
1618 <h1 class="_lined-heading">Preferences</h1>
1719
1820 <div class="_settings-fieldset">
1921 <h2 class="_settings-legend">Theme:</h2>
2022 <div class="_settings-inputs">
21− ${settings.autoSupported ?
22− themeOption({label: "Automatic <small>Matches system setting</small>", value: "auto"}, settings)
23− :
24− ""}
25− ${themeOption({label: "Light", value: "default"}, settings)}
26− ${themeOption({label: "Dark", value: "dark"}, settings)}
23+ ${
24+ settings.autoSupported
25+ ? themeOption(
26+ {
27+ label: "Automatic <small>Matches system setting</small>",
28+ value: "auto",
29+ },
30+ settings,
31+ )
32+ : ""
33+ }
34+ ${themeOption({ label: "Light", value: "default" }, settings)}
35+ ${themeOption({ label: "Dark", value: "dark" }, settings)}
2736 </div>
2837 </div>
2938
@@ -32,24 +41,36 @@ app.templates.settingsPage = settings => `\ · 2 unmodified lines
3241
3342 <div class="_settings-inputs">
3443 <label class="_settings-label _setting-max-width">
35− <input type="checkbox" form="settings" name="layout" value="_max-width"${settings['_max-width'] ? ' checked' : ''}>Enable fixed-width layout
44+ <input type="checkbox" form="settings" name="layout" value="_max-width"${
45+ settings["_max-width"] ? " checked" : ""
46+ }>Enable fixed-width layout
3647 </label>
3748 <label class="_settings-label _setting-text-justify-hyphenate">
38− <input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${settings['_text-justify-hyphenate'] ? ' checked' : ''}>Enable justified layout and automatic hyphenation
49+ <input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${
50+ settings["_text-justify-hyphenate"] ? " checked" : ""
51+ }>Enable justified layout and automatic hyphenation
3952 </label>
4053 <label class="_settings-label _hide-on-mobile">
41− <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${settings['_sidebar-hidden'] ? ' checked' : ''}>Automatically hide and show the sidebar
54+ <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${
55+ settings["_sidebar-hidden"] ? " checked" : ""
56+ }>Automatically hide and show the sidebar
4257 <small>Tip: drag the edge of the sidebar to resize it.</small>
4358 </label>
4459 <label class="_settings-label _hide-on-mobile">
45− <input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${settings.noAutofocus ? ' checked' : ''}>Disable autofocus of search input
60+ <input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${
61+ settings.noAutofocus ? " checked" : ""
62+ }>Disable autofocus of search input
4663 </label>
4764 <label class="_settings-label">
48− <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${settings.autoInstall ? ' checked' : ''}>Automatically download documentation for offline use
65+ <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${
66+ settings.autoInstall ? " checked" : ""
67+ }>Automatically download documentation for offline use
4968 <small>Only enable this when bandwidth isn't a concern to you.</small>
5069 </label>
5170 <label class="_settings-label _hide-in-development">
52− <input type="checkbox" form="settings" name="analyticsConsent"${settings.analyticsConsent ? ' checked' : ''}>Enable tracking cookies
71+ <input type="checkbox" form="settings" name="analyticsConsent"${
72+ settings.analyticsConsent ? " checked" : ""
73+ }>Enable tracking cookies
5374 <small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small>
5475 </label>
5576 </div>
@@ -60,20 +81,30 @@ app.templates.settingsPage = settings => `\ · 4 unmodified lines
6081
6182 <div class="_settings-inputs">
6283 <label class="_settings-label">
63− <input type="checkbox" form="settings" name="smoothScroll" value="1"${settings.smoothScroll ? ' checked' : ''}>Use smooth scrolling
84+ <input type="checkbox" form="settings" name="smoothScroll" value="1"${
85+ settings.smoothScroll ? " checked" : ""
86+ }>Use smooth scrolling
6487 </label>
6588 <label class="_settings-label _setting-native-scrollbar">
66− <input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${settings['_native-scrollbars'] ? ' checked' : ''}>Use native scrollbars
89+ <input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${
90+ settings["_native-scrollbars"] ? " checked" : ""
91+ }>Use native scrollbars
6792 </label>
6893 <label class="_settings-label">
69− <input type="checkbox" form="settings" name="arrowScroll" value="1"${settings.arrowScroll ? ' checked' : ''}>Use arrow keys to scroll the main content area
94+ <input type="checkbox" form="settings" name="arrowScroll" value="1"${
95+ settings.arrowScroll ? " checked" : ""
96+ }>Use arrow keys to scroll the main content area
7097 <small>With this checked, use <code class="_label">shift</code> + <code class="_label">&uarr;</code><code class="_label">&darr;</code><code class="_label">&larr;</code><code class="_label">&rarr;</code> to navigate the sidebar.</small>
7198 </label>
7299 <label class="_settings-label">
73− <input type="checkbox" form="settings" name="spaceScroll" value="1"${settings.spaceScroll ? ' checked' : ''}>Use spacebar to scroll during search
100+ <input type="checkbox" form="settings" name="spaceScroll" value="1"${
101+ settings.spaceScroll ? " checked" : ""
102+ }>Use spacebar to scroll during search
74103 </label>
75104 <label class="_settings-label">
76− <input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${settings.spaceTimeout}"> Delay until you can scroll by pressing space
105+ <input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${
106+ settings.spaceTimeout
107+ }"> Delay until you can scroll by pressing space
77108 <small>Time in seconds</small>
78109 </label>
79110 </div>
assets/javascripts/templates/pages/type_tmpl.js+9−3
@@ -5,7 +5,13 @@ · 4 unmodified lines
55 * DS102: Remove unnecessary code created because of implicit returns
66 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
77 */
8−app.templates.typePage = type => ` <h1>${type.doc.fullName} / ${type.name}</h1>
9−<ul class="_entry-list">${app.templates.render('typePageEntry', type.entries())}</ul> `;
8+app.templates.typePage = (type) => ` <h1>${type.doc.fullName} / ${
9+ type.name
10+}</h1>
11+<ul class="_entry-list">${app.templates.render(
12+ "typePageEntry",
13+ type.entries(),
14+)}</ul> `;
1015
11−app.templates.typePageEntry = entry => `<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`;
16+app.templates.typePageEntry = (entry) =>
17+ `<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`;
assets/javascripts/templates/path_tmpl.js+13−5
@@ -5,11 +5,19 @@ · 4 unmodified lines
55 * DS102: Remove unnecessary code created because of implicit returns
66 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
77 */
8−const arrow = "<svg class=\"_path-arrow\"><use xlink:href=\"#icon-dir\"/></svg>";
8+const arrow = '<svg class="_path-arrow"><use xlink:href="#icon-dir"/></svg>';
99
10−app.templates.path = function(doc, type, entry) {
11− let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${doc.icon}">${doc.fullName}</a>`;
12− if (type) { html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${type.name}</a>`; }
13− if (entry) { html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`; }
10+app.templates.path = function (doc, type, entry) {
11+ let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${
12+ doc.icon
13+ }">${doc.fullName}</a>`;
14+ if (type) {
15+ html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${
16+ type.name
17+ }</a>`;
18+ }
19+ if (entry) {
20+ html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`;
21+ }
1422 return html;
1523 };
assets/javascripts/templates/sidebar_tmpl.js+75−37
@@ -6,74 +6,112 @@ · 5 unmodified lines
66 * DS207: Consider shorter variations of null checks
77 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
88 */
9−const {
10− templates
11−} = app;
9+const { templates } = app;
1210
13−const arrow = "<svg class=\"_list-arrow\"><use xlink:href=\"#icon-dir\"/></svg>";
11+const arrow = '<svg class="_list-arrow"><use xlink:href="#icon-dir"/></svg>';
1412
15−templates.sidebarDoc = function(doc, options) {
16− if (options == null) { options = {}; }
17− let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `;
18− link += options.disabled ? '_list-disabled' : '_list-dir';
13+templates.sidebarDoc = function (doc, options) {
14+ if (options == null) {
15+ options = {};
16+ }
17+ let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `;
18+ link += options.disabled ? "_list-disabled" : "_list-dir";
1919 link += `" data-slug="${doc.slug}" title="${doc.fullName}" tabindex="-1">`;
2020 if (options.disabled) {
2121 link += `<span class="_list-enable" data-enable="${doc.slug}">Enable</span>`;
2222 } else {
2323 link += arrow;
2424 }
25− if (doc.release) { link += `<span class="_list-count">${doc.release}</span>`; }
25+ if (doc.release) {
26+ link += `<span class="_list-count">${doc.release}</span>`;
27+ }
2628 link += `<span class="_list-text">${doc.name}`;
27− if (options.fullName || (options.disabled && doc.version)) { link += ` ${doc.version}`; }
29+ if (options.fullName || (options.disabled && doc.version)) {
30+ link += ` ${doc.version}`;
31+ }
2832 return link + "</span></a>";
2933 };
3034
31−templates.sidebarType = type => `<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${type.slug}" tabindex="-1">${arrow}<span class="_list-count">${type.count}</span><span class="_list-text">${$.escape(type.name)}</span></a>`;
35+templates.sidebarType = (type) =>
36+ `<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${
37+ type.slug
38+ }" tabindex="-1">${arrow}<span class="_list-count">${
39+ type.count
40+ }</span><span class="_list-text">${$.escape(type.name)}</span></a>`;
3241
33−templates.sidebarEntry = entry => `<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape(entry.name)}</a>`;
42+templates.sidebarEntry = (entry) =>
43+ `<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape(
44+ entry.name,
45+ )}</a>`;
3446
35−templates.sidebarResult = function(entry) {
36− let addons = entry.isIndex() && app.disabledDocs.contains(entry.doc) ?
37− `<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>`
38− :
39− "<span class=\"_list-reveal\" data-reset-list title=\"Reveal in list\"></span>";
40− if (entry.doc.version && !entry.isIndex()) { addons += `<span class="_list-count">${entry.doc.short_version}</span>`; }
41− return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${entry.doc.icon}" tabindex="-1">${addons}<span class="_list-text">${$.escape(entry.name)}</span></a>`;
47+templates.sidebarResult = function (entry) {
48+ let addons =
49+ entry.isIndex() && app.disabledDocs.contains(entry.doc)
50+ ? `<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>`
51+ : '<span class="_list-reveal" data-reset-list title="Reveal in list"></span>';
52+ if (entry.doc.version && !entry.isIndex()) {
53+ addons += `<span class="_list-count">${entry.doc.short_version}</span>`;
54+ }
55+ return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${
56+ entry.doc.icon
57+ }" tabindex="-1">${addons}<span class="_list-text">${$.escape(
58+ entry.name,
59+ )}</span></a>`;
4260 };
4361
44−templates.sidebarNoResults = function() {
45− let html = " <div class=\"_list-note\">No results.</div> ";
46− if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) { html += `\
62+templates.sidebarNoResults = function () {
63+ let html = ' <div class="_list-note">No results.</div> ';
64+ if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) {
65+ html += `\
4766 <div class="_list-note">Note: documentations must be <a href="/settings" class="_list-note-link">enabled</a> to appear in the search.</div>\
48−`; }
67+`;
68+ }
4969 return html;
5070 };
5171
52−templates.sidebarPageLink = count => `<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`;
72+templates.sidebarPageLink = (count) =>
73+ `<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`;
5374
54−templates.sidebarLabel = function(doc, options) {
55− if (options == null) { options = {}; }
56− let label = "<label class=\"_list-item";
57− if (!doc.version) { label += ` _icon-${doc.icon}`; }
75+templates.sidebarLabel = function (doc, options) {
76+ if (options == null) {
77+ options = {};
78+ }
79+ let label = '<label class="_list-item';
80+ if (!doc.version) {
81+ label += ` _icon-${doc.icon}`;
82+ }
5883 label += `"><input type="checkbox" name="${doc.slug}" class="_list-checkbox" `;
59− if (options.checked) { label += "checked"; }
84+ if (options.checked) {
85+ label += "checked";
86+ }
6087 return label + `><span class="_list-text">${doc.fullName}</span></label>`;
6188 };
6289
63−templates.sidebarVersionedDoc = function(doc, versions, options) {
64− if (options == null) { options = {}; }
90+templates.sidebarVersionedDoc = function (doc, versions, options) {
91+ if (options == null) {
92+ options = {};
93+ }
6594 let html = `<div class="_list-item _list-dir _list-rdir _icon-${doc.icon}`;
66− if (options.open) { html += " open"; }
67− return html + `" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>`;
95+ if (options.open) {
96+ html += " open";
97+ }
98+ return (
99+ html +
100+ `" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>`
101+ );
68102 };
69103
70−templates.sidebarDisabled = options => `<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`;
104+templates.sidebarDisabled = (options) =>
105+ `<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`;
71106
72−templates.sidebarDisabledList = html => `<div class="_disabled-list">${html}</div>`;
107+templates.sidebarDisabledList = (html) =>
108+ `<div class="_disabled-list">${html}</div>`;
73109
74−templates.sidebarDisabledVersionedDoc = (doc, versions) => `<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`;
110+templates.sidebarDisabledVersionedDoc = (doc, versions) =>
111+ `<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`;
75112
76−templates.docPickerHeader = "<div class=\"_list-picker-head\"><span>Documentation</span> <span>Enable</span></div>";
113+templates.docPickerHeader =
114+ '<div class="_list-picker-head"><span>Documentation</span> <span>Enable</span></div>';
77115
78116 templates.docPickerNote = `\
79117 <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>
assets/javascripts/templates/tip_tmpl.js+8−2
@@ -10,8 +10,14 @@ app.templates.tipKeyNav = () => `\ · 9 unmodified lines
1010 <strong>ProTip</strong>
1111 <span class="_notif-info">(click to dismiss)</span>
1212 <p class="_notif-text">
13− Hit ${app.settings.get('arrowScroll') ? '<code class="_label">shift</code> +' : ''} <code class="_label">&darr;</code> <code class="_label">&uarr;</code> <code class="_label">&larr;</code> <code class="_label">&rarr;</code> to navigate the sidebar.<br>
14− Hit <code class="_label">space / shift space</code>${app.settings.get('arrowScroll') ? ' or <code class="_label">&darr;/&uarr;</code>' : ', <code class="_label">alt &darr;/&uarr;</code> or <code class="_label">shift &darr;/&uarr;</code>'} to scroll the page.
13+ Hit ${
14+ app.settings.get("arrowScroll") ? '<code class="_label">shift</code> +' : ""
15+ } <code class="_label">&darr;</code> <code class="_label">&uarr;</code> <code class="_label">&larr;</code> <code class="_label">&rarr;</code> to navigate the sidebar.<br>
16+ Hit <code class="_label">space / shift space</code>${
17+ app.settings.get("arrowScroll")
18+ ? ' or <code class="_label">&darr;/&uarr;</code>'
19+ : ', <code class="_label">alt &darr;/&uarr;</code> or <code class="_label">shift &darr;/&uarr;</code>'
20+ } to scroll the page.
1521 <p class="_notif-text">
1622 <a href="/help#shortcuts" class="_notif-link">See all keyboard shortcuts</a>\
1723 `;
assets/javascripts/tracking.js+43−20
@@ -1,32 +1,55 @@
11 try {
2− if (app.config.env === 'production') {
3− if (Cookies.get('analyticsConsent') === '1') {
4− (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
5− (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
6− m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
7− })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
8− ga('create', 'UA-5544833-12', 'devdocs.io');
9− page.track(function() {
10− ga('send', 'pageview', {
2+ if (app.config.env === "production") {
3+ if (Cookies.get("analyticsConsent") === "1") {
4+ (function (i, s, o, g, r, a, m) {
5+ i["GoogleAnalyticsObject"] = r;
6+ (i[r] =
7+ i[r] ||
8+ function () {
9+ (i[r].q = i[r].q || []).push(arguments);
10+ }),
11+ (i[r].l = 1 * new Date());
12+ (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
13+ a.async = 1;
14+ a.src = g;
15+ m.parentNode.insertBefore(a, m);
16+ })(
17+ window,
18+ document,
19+ "script",
20+ "https://www.google-analytics.com/analytics.js",
21+ "ga",
22+ );
23+ ga("create", "UA-5544833-12", "devdocs.io");
24+ page.track(function () {
25+ ga("send", "pageview", {
1126 page: location.pathname + location.search + location.hash,
12− dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
27+ dimension1:
28+ app.router.context &&
29+ app.router.context.doc &&
30+ app.router.context.doc.slug_without_version,
1331 });
1432 });
1533
16− page.track(function() {
17− if (window._gauges)
18− _gauges.push(['track']);
34+ page.track(function () {
35+ if (window._gauges) _gauges.push(["track"]);
1936 else
20− (function() {
21− var _gauges=_gauges||[];!function(){var a=document.createElement("script");
22− a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
23− a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
24− a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
25− b.parentNode.insertBefore(a,b)}();
37+ (function () {
38+ var _gauges = _gauges || [];
39+ !(function () {
40+ var a = document.createElement("script");
41+ (a.type = "text/javascript"),
42+ (a.async = !0),
43+ (a.id = "gauges-tracker"),
44+ a.setAttribute("data-site-id", "51c15f82613f5d7819000067"),
45+ (a.src = "https://secure.gaug.es/track.js");
46+ var b = document.getElementsByTagName("script")[0];
47+ b.parentNode.insertBefore(a, b);
48+ })();
2649 })();
2750 });
2851 } else {
2952 resetAnalytics();
3053 }
3154 }
32−} catch(e) { }
55+} catch (e) {}
assets/javascripts/vendor/cookies.js+191−158
@@ -5,171 +5,204 @@ · 4 unmodified lines
55 * This is free and unencumbered software released into the public domain.
66 */
77 (function (global, undefined) {
8− 'use strict';
8+ "use strict";
99
10− var factory = function (window) {
11− if (typeof window.document !== 'object') {
12− throw new Error('Cookies.js requires a `window` with a `document` object');
10+ var factory = function (window) {
11+ if (typeof window.document !== "object") {
12+ throw new Error(
13+ "Cookies.js requires a `window` with a `document` object",
14+ );
15+ }
16+
17+ var Cookies = function (key, value, options) {
18+ return arguments.length === 1
19+ ? Cookies.get(key)
20+ : Cookies.set(key, value, options);
21+ };
22+
23+ // Allows for setter injection in unit tests
24+ Cookies._document = window.document;
25+
26+ // Used to ensure cookie keys do not collide with
27+ // built-in `Object` properties
28+ Cookies._cacheKeyPrefix = "cookey."; // Hurr hurr, :)
29+
30+ Cookies._maxExpireDate = new Date("Fri, 31 Dec 9999 23:59:59 UTC");
31+
32+ Cookies.defaults = {
33+ path: "/",
34+ SameSite: "Strict",
35+ secure: true,
36+ };
37+
38+ Cookies.get = function (key) {
39+ if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
40+ Cookies._renewCache();
41+ }
42+
43+ var value = Cookies._cache[Cookies._cacheKeyPrefix + key];
44+
45+ return value === undefined ? undefined : decodeURIComponent(value);
46+ };
47+
48+ Cookies.set = function (key, value, options) {
49+ options = Cookies._getExtendedOptions(options);
50+ options.expires = Cookies._getExpiresDate(
51+ value === undefined ? -1 : options.expires,
52+ );
53+
54+ Cookies._document.cookie = Cookies._generateCookieString(
55+ key,
56+ value,
57+ options,
58+ );
59+
60+ return Cookies;
61+ };
62+
63+ Cookies.expire = function (key, options) {
64+ return Cookies.set(key, undefined, options);
65+ };
66+
67+ Cookies._getExtendedOptions = function (options) {
68+ return {
69+ path: (options && options.path) || Cookies.defaults.path,
70+ domain: (options && options.domain) || Cookies.defaults.domain,
71+ SameSite: (options && options.SameSite) || Cookies.defaults.SameSite,
72+ expires: (options && options.expires) || Cookies.defaults.expires,
73+ secure:
74+ options && options.secure !== undefined
75+ ? options.secure
76+ : Cookies.defaults.secure,
77+ };
78+ };
79+
80+ Cookies._isValidDate = function (date) {
81+ return (
82+ Object.prototype.toString.call(date) === "[object Date]" &&
83+ !isNaN(date.getTime())
84+ );
85+ };
86+
87+ Cookies._getExpiresDate = function (expires, now) {
88+ now = now || new Date();
89+
90+ if (typeof expires === "number") {
91+ expires =
92+ expires === Infinity
93+ ? Cookies._maxExpireDate
94+ : new Date(now.getTime() + expires * 1000);
95+ } else if (typeof expires === "string") {
96+ expires = new Date(expires);
97+ }
98+
99+ if (expires && !Cookies._isValidDate(expires)) {
100+ throw new Error(
101+ "`expires` parameter cannot be converted to a valid Date instance",
102+ );
103+ }
104+
105+ return expires;
106+ };
107+
108+ Cookies._generateCookieString = function (key, value, options) {
109+ key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
110+ key = key.replace(/\(/g, "%28").replace(/\)/g, "%29");
111+ value = (value + "").replace(
112+ /[^!#$&-+\--:<-\[\]-~]/g,
113+ encodeURIComponent,
114+ );
115+ options = options || {};
116+
117+ var cookieString = key + "=" + value;
118+ cookieString += options.path ? ";path=" + options.path : "";
119+ cookieString += options.domain ? ";domain=" + options.domain : "";
120+ cookieString += options.SameSite ? ";SameSite=" + options.SameSite : "";
121+ cookieString += options.expires
122+ ? ";expires=" + options.expires.toUTCString()
123+ : "";
124+ cookieString += options.secure ? ";secure" : "";
125+
126+ return cookieString;
127+ };
128+
129+ Cookies._getCacheFromString = function (documentCookie) {
130+ var cookieCache = {};
131+ var cookiesArray = documentCookie ? documentCookie.split("; ") : [];
132+
133+ for (var i = 0; i < cookiesArray.length; i++) {
134+ var cookieKvp = Cookies._getKeyValuePairFromCookieString(
135+ cookiesArray[i],
136+ );
137+
138+ if (
139+ cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined
140+ ) {
141+ cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] =
142+ cookieKvp.value;
13143 }
144+ }
14145
15− var Cookies = function (key, value, options) {
16− return arguments.length === 1 ?
17− Cookies.get(key) : Cookies.set(key, value, options);
18− };
146+ return cookieCache;
147+ };
19148
20− // Allows for setter injection in unit tests
21− Cookies._document = window.document;
22−
23− // Used to ensure cookie keys do not collide with
24− // built-in `Object` properties
25− Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :)
149+ Cookies._getKeyValuePairFromCookieString = function (cookieString) {
150+ // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
151+ var separatorIndex = cookieString.indexOf("=");
152+
153+ // IE omits the "=" when the cookie value is an empty string
154+ separatorIndex =
155+ separatorIndex < 0 ? cookieString.length : separatorIndex;
156+
157+ var key = cookieString.substr(0, separatorIndex);
158+ var decodedKey;
159+ try {
160+ decodedKey = decodeURIComponent(key);
161+ } catch (e) {
162+ if (console && typeof console.error === "function") {
163+ console.error('Could not decode cookie with key "' + key + '"', e);
164+ }
165+ }
26166
27− Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC');
167+ return {
168+ key: decodedKey,
169+ value: cookieString.substr(separatorIndex + 1), // Defer decoding value until accessed
170+ };
171+ };
28172
29− Cookies.defaults = {
30− path: '/',
31− SameSite: 'Strict',
32− secure: true
33− };
34−
35− Cookies.get = function (key) {
36− if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
37− Cookies._renewCache();
38− }
39−
40− var value = Cookies._cache[Cookies._cacheKeyPrefix + key];
41−
42− return value === undefined ? undefined : decodeURIComponent(value);
43− };
44−
45− Cookies.set = function (key, value, options) {
46− options = Cookies._getExtendedOptions(options);
47− options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);
48−
49− Cookies._document.cookie = Cookies._generateCookieString(key, value, options);
50−
51− return Cookies;
52− };
53−
54− Cookies.expire = function (key, options) {
55− return Cookies.set(key, undefined, options);
56− };
57−
58− Cookies._getExtendedOptions = function (options) {
59− return {
60− path: options && options.path || Cookies.defaults.path,
61− domain: options && options.domain || Cookies.defaults.domain,
62− SameSite: options && options.SameSite || Cookies.defaults.SameSite,
63− expires: options && options.expires || Cookies.defaults.expires,
64− secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure
65− };
66− };
67−
68− Cookies._isValidDate = function (date) {
69− return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
70− };
71−
72− Cookies._getExpiresDate = function (expires, now) {
73− now = now || new Date();
74−
75− if (typeof expires === 'number') {
76− expires = expires === Infinity ?
77− Cookies._maxExpireDate : new Date(now.getTime() + expires * 1000);
78− } else if (typeof expires === 'string') {
79− expires = new Date(expires);
80− }
81−
82− if (expires && !Cookies._isValidDate(expires)) {
83− throw new Error('`expires` parameter cannot be converted to a valid Date instance');
84− }
85−
86− return expires;
87− };
88−
89− Cookies._generateCookieString = function (key, value, options) {
90− key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
91− key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
92− value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
93− options = options || {};
94−
95− var cookieString = key + '=' + value;
96− cookieString += options.path ? ';path=' + options.path : '';
97− cookieString += options.domain ? ';domain=' + options.domain : '';
98− cookieString += options.SameSite ? ';SameSite=' + options.SameSite : '';
99− cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
100− cookieString += options.secure ? ';secure' : '';
101−
102− return cookieString;
103− };
104−
105− Cookies._getCacheFromString = function (documentCookie) {
106− var cookieCache = {};
107− var cookiesArray = documentCookie ? documentCookie.split('; ') : [];
108−
109− for (var i = 0; i < cookiesArray.length; i++) {
110− var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);
111−
112− if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) {
113− cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value;
114− }
115− }
116−
117− return cookieCache;
118− };
119−
120− Cookies._getKeyValuePairFromCookieString = function (cookieString) {
121− // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
122− var separatorIndex = cookieString.indexOf('=');
123−
124− // IE omits the "=" when the cookie value is an empty string
125− separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;
126−
127− var key = cookieString.substr(0, separatorIndex);
128− var decodedKey;
129− try {
130− decodedKey = decodeURIComponent(key);
131− } catch (e) {
132− if (console && typeof console.error === 'function') {
133− console.error('Could not decode cookie with key "' + key + '"', e);
134− }
135− }
136−
137− return {
138− key: decodedKey,
139− value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed
140− };
141− };
142−
143− Cookies._renewCache = function () {
144− Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
145− Cookies._cachedDocumentCookie = Cookies._document.cookie;
146− };
147−
148− Cookies._areEnabled = function () {
149− var testKey = 'cookies.js';
150− var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
151− Cookies.expire(testKey);
152− return areEnabled;
153− };
154−
155− Cookies.enabled = Cookies._areEnabled();
156−
157− return Cookies;
173+ Cookies._renewCache = function () {
174+ Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
175+ Cookies._cachedDocumentCookie = Cookies._document.cookie;
158176 };
159− var cookiesExport = (global && typeof global.document === 'object') ? factory(global) : factory;
160177
161− // AMD support
162− if (typeof define === 'function' && define.amd) {
163− define(function () { return cookiesExport; });
178+ Cookies._areEnabled = function () {
179+ var testKey = "cookies.js";
180+ var areEnabled = Cookies.set(testKey, 1).get(testKey) === "1";
181+ Cookies.expire(testKey);
182+ return areEnabled;
183+ };
184+
185+ Cookies.enabled = Cookies._areEnabled();
186+
187+ return Cookies;
188+ };
189+ var cookiesExport =
190+ global && typeof global.document === "object" ? factory(global) : factory;
191+
192+ // AMD support
193+ if (typeof define === "function" && define.amd) {
194+ define(function () {
195+ return cookiesExport;
196+ });
164197 // CommonJS/Node.js support
165− } else if (typeof exports === 'object') {
166− // Support Node.js specific `module.exports` (which can be a function)
167− if (typeof module === 'object' && typeof module.exports === 'object') {
168− exports = module.exports = cookiesExport;
169− }
170− // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
171− exports.Cookies = cookiesExport;
172− } else {
173− global.Cookies = cookiesExport;
198+ } else if (typeof exports === "object") {
199+ // Support Node.js specific `module.exports` (which can be a function)
200+ if (typeof module === "object" && typeof module.exports === "object") {
201+ exports = module.exports = cookiesExport;
174202 }
175−})(typeof window === 'undefined' ? this : window);
203+ // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
204+ exports.Cookies = cookiesExport;
205+ } else {
206+ global.Cookies = cookiesExport;
207+ }
208+})(typeof window === "undefined" ? this : window);
assets/javascripts/vendor/mathml.js+9−4
@@ -4,17 +4,22 @@ · 3 unmodified lines
44 * Adapted from: https://github.com/fred-wang/mathml.css */
55
66 (function () {
7− window.addEventListener("load", function() {
7+ window.addEventListener("load", function () {
88 var box, div, link, namespaceURI;
99 // First check whether the page contains any <math> element.
1010 namespaceURI = "http://www.w3.org/1998/Math/MathML";
1111 // Create a div to test mspace, using Kuma's "offscreen" CSS
12− document.body.insertAdjacentHTML("afterbegin", "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" + namespaceURI + "'><mspace height='23px' width='77px'></mspace></math></div>");
12+ document.body.insertAdjacentHTML(
13+ "afterbegin",
14+ "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" +
15+ namespaceURI +
16+ "'><mspace height='23px' width='77px'></mspace></math></div>",
17+ );
1318 div = document.body.firstChild;
1419 box = div.firstChild.firstChild.getBoundingClientRect();
1520 document.body.removeChild(div);
16− if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) {
21+ if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) {
1722 window.supportsMathML = false;
1823 }
1924 });
20−}());
25+})();
assets/javascripts/vendor/prism.js+5777−5324
@@ -2,13 +2,13 @@ · 1 unmodified line
22 https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+cmake+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markdown+markup-templating+matlab+nginx+nim+nix+ocaml+perl+php+python+qml+r+jsx+ruby+rust+scss+scala+shell-session+sql+typescript+yaml+zig */
33 /// <reference lib="WebWorker"/>
44
5−var _self = (typeof window !== 'undefined')
6− ? window // if in browser
7− : (
8− (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
9− ? self // if in worker
10− : {} // if in node js
11− );
5+var _self =
6+ typeof window !== "undefined"
7+ ? window // if in browser
8+ : typeof WorkerGlobalScope !== "undefined" &&
9+ self instanceof WorkerGlobalScope
10+ ? self // if in worker
11+ : {}; // if in node js
1212
1313 /**
1414 * Prism: Lightweight, robust, elegant syntax highlighting
@@ -19,1200 +19,1260 @@ var _self = (typeof window !== 'undefined') · 4 unmodified lines
1919 * @public
2020 */
2121 var Prism = (function (_self) {
22−
23− // Private helper vars
24− var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i;
25− var uniqueId = 0;
26−
27− // The grammar object for plaintext
28− var plainTextGrammar = {};
29−
30−
31− var _ = {
32− /**
33− * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the
34− * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load
35− * additional languages or plugins yourself.
36− *
37− * By setting this value to `true`, Prism will not automatically highlight all code elements on the page.
38− *
39− * You obviously have to change this value before the automatic highlighting started. To do this, you can add an
40− * empty Prism object into the global scope before loading the Prism script like this:
41− *
42− * ```js
43− * window.Prism = window.Prism || {};
44− * Prism.manual = true;
45− * // add a new <script> to load Prism's script
46− * ```
47− *
48− * @default false
49− * @type {boolean}
50− * @memberof Prism
51− * @public
52− */
53− manual: _self.Prism && _self.Prism.manual,
54− /**
55− * By default, if Prism is in a web worker, it assumes that it is in a worker it created itself, so it uses
56− * `addEventListener` to communicate with its parent instance. However, if you're using Prism manually in your
57− * own worker, you don't want it to do this.
58− *
59− * By setting this value to `true`, Prism will not add its own listeners to the worker.
60− *
61− * You obviously have to change this value before Prism executes. To do this, you can add an
62− * empty Prism object into the global scope before loading the Prism script like this:
63− *
64− * ```js
65− * window.Prism = window.Prism || {};
66− * Prism.disableWorkerMessageHandler = true;
67− * // Load Prism's script
68− * ```
69− *
70− * @default false
71− * @type {boolean}
72− * @memberof Prism
73− * @public
74− */
75− disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
76−
77− /**
78− * A namespace for utility methods.
79− *
80− * All function in this namespace that are not explicitly marked as _public_ are for __internal use only__ and may
81− * change or disappear at any time.
82− *
83− * @namespace
84− * @memberof Prism
85− */
86− util: {
87− encode: function encode(tokens) {
88− if (tokens instanceof Token) {
89− return new Token(tokens.type, encode(tokens.content), tokens.alias);
90− } else if (Array.isArray(tokens)) {
91− return tokens.map(encode);
92− } else {
93− return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');
94− }
95− },
96−
97− /**
98− * Returns the name of the type of the given value.
99− *
100− * @param {any} o
101− * @returns {string}
102− * @example
103− * type(null) === 'Null'
104− * type(undefined) === 'Undefined'
105− * type(123) === 'Number'
106− * type('foo') === 'String'
107− * type(true) === 'Boolean'
108− * type([1, 2]) === 'Array'
109− * type({}) === 'Object'
110− * type(String) === 'Function'
111− * type(/abc+/) === 'RegExp'
112− */
113− type: function (o) {
114− return Object.prototype.toString.call(o).slice(8, -1);
115− },
116−
117− /**
118− * Returns a unique number for the given object. Later calls will still return the same number.
119− *
120− * @param {Object} obj
121− * @returns {number}
122− */
123− objId: function (obj) {
124− if (!obj['__id']) {
125− Object.defineProperty(obj, '__id', { value: ++uniqueId });
126− }
127− return obj['__id'];
128− },
129−
130− /**
131− * Creates a deep clone of the given object.
132− *
133− * The main intended use of this function is to clone language definitions.
134− *
135− * @param {T} o
136− * @param {Record<number, any>} [visited]
137− * @returns {T}
138− * @template T
139− */
140− clone: function deepClone(o, visited) {
141− visited = visited || {};
142−
143− var clone; var id;
144− switch (_.util.type(o)) {
145− case 'Object':
146− id = _.util.objId(o);
147− if (visited[id]) {
148− return visited[id];
149− }
150− clone = /** @type {Record<string, any>} */ ({});
151− visited[id] = clone;
152−
153− for (var key in o) {
154− if (o.hasOwnProperty(key)) {
155− clone[key] = deepClone(o[key], visited);
156− }
157− }
158−
159− return /** @type {any} */ (clone);
160−
161− case 'Array':
162− id = _.util.objId(o);
163− if (visited[id]) {
164− return visited[id];
165− }
166− clone = [];
167− visited[id] = clone;
168−
169− (/** @type {Array} */(/** @type {any} */(o))).forEach(function (v, i) {
170− clone[i] = deepClone(v, visited);
171− });
172−
173− return /** @type {any} */ (clone);
174−
175− default:
176− return o;
177− }
178− },
179−
180− /**
181− * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class.
182− *
183− * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned.
184− *
185− * @param {Element} element
186− * @returns {string}
187− */
188− getLanguage: function (element) {
189− while (element) {
190− var m = lang.exec(element.className);
191− if (m) {
192− return m[1].toLowerCase();
193− }
194− element = element.parentElement;
195− }
196− return 'none';
197− },
198−
199− /**
200− * Sets the Prism `language-xxxx` class of the given element.
201− *
202− * @param {Element} element
203− * @param {string} language
204− * @returns {void}
205− */
206− setLanguage: function (element, language) {
207− // remove all `language-xxxx` classes
208− // (this might leave behind a leading space)
209− element.className = element.className.replace(RegExp(lang, 'gi'), '');
210−
211− // add the new `language-xxxx` class
212− // (using `classList` will automatically clean up spaces for us)
213− element.classList.add('language-' + language);
214− },
215−
216− /**
217− * Returns the script element that is currently executing.
218− *
219− * This does __not__ work for line script element.
220− *
221− * @returns {HTMLScriptElement | null}
222− */
223− currentScript: function () {
224− if (typeof document === 'undefined') {
225− return null;
226− }
227− if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis */) {
228− return /** @type {any} */ (document.currentScript);
229− }
230−
231− // IE11 workaround
232− // we'll get the src of the current script by parsing IE11's error stack trace
233− // this will not work for inline scripts
234−
235− try {
236− throw new Error();
237− } catch (err) {
238− // Get file src url from stack. Specifically works with the format of stack traces in IE.
239− // A stack will look like this:
240− //
241− // Error
242− // at _.util.currentScript (http://localhost/components/prism-core.js:119:5)
243− // at Global code (http://localhost/components/prism-core.js:606:1)
244−
245− var src = (/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(err.stack) || [])[1];
246− if (src) {
247− var scripts = document.getElementsByTagName('script');
248− for (var i in scripts) {
249− if (scripts[i].src == src) {
250− return scripts[i];
251− }
252− }
253− }
254− return null;
255− }
256− },
257−
258− /**
259− * Returns whether a given class is active for `element`.
260− *
261− * The class can be activated if `element` or one of its ancestors has the given class and it can be deactivated
262− * if `element` or one of its ancestors has the negated version of the given class. The _negated version_ of the
263− * given class is just the given class with a `no-` prefix.
264− *
265− * Whether the class is active is determined by the closest ancestor of `element` (where `element` itself is
266− * closest ancestor) that has the given class or the negated version of it. If neither `element` nor any of its
267− * ancestors have the given class or the negated version of it, then the default activation will be returned.
268− *
269− * In the paradoxical situation where the closest ancestor contains __both__ the given class and the negated
270− * version of it, the class is considered active.
271− *
272− * @param {Element} element
273− * @param {string} className
274− * @param {boolean} [defaultActivation=false]
275− * @returns {boolean}
276− */
277− isActive: function (element, className, defaultActivation) {
278− var no = 'no-' + className;
279−
280− while (element) {
281− var classList = element.classList;
282− if (classList.contains(className)) {
283− return true;
284− }
285− if (classList.contains(no)) {
286− return false;
287− }
288− element = element.parentElement;
289− }
290− return !!defaultActivation;
291− }
292− },
293−
294− /**
295− * This namespace contains all currently loaded languages and the some helper functions to create and modify languages.
296− *
297− * @namespace
298− * @memberof Prism
299− * @public
300− */
301− languages: {
302− /**
303− * The grammar for plain, unformatted text.
304− */
305− plain: plainTextGrammar,
306− plaintext: plainTextGrammar,
307− text: plainTextGrammar,
308− txt: plainTextGrammar,
309−
310− /**
311− * Creates a deep copy of the language with the given id and appends the given tokens.
312− *
313− * If a token in `redef` also appears in the copied language, then the existing token in the copied language
314− * will be overwritten at its original position.
315− *
316− * ## Best practices
317− *
318− * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the copied language)
319− * doesn't matter, they can technically be in any order. However, this can be confusing to others that trying to
320− * understand the language definition because, normally, the order of tokens matters in Prism grammars.
321− *
322− * Therefore, it is encouraged to order overwriting tokens according to the positions of the overwritten tokens.
323− * Furthermore, all non-overwriting tokens should be placed after the overwriting ones.
324− *
325− * @param {string} id The id of the language to extend. This has to be a key in `Prism.languages`.
326− * @param {Grammar} redef The new tokens to append.
327− * @returns {Grammar} The new language created.
328− * @public
329− * @example
330− * Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
331− * // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token
332− * // at its original position
333− * 'comment': { ... },
334− * // CSS doesn't have a 'color' token, so this token will be appended
335− * 'color': /\b(?:red|green|blue)\b/
336− * });
337− */
338− extend: function (id, redef) {
339− var lang = _.util.clone(_.languages[id]);
340−
341− for (var key in redef) {
342− lang[key] = redef[key];
343− }
344−
345− return lang;
346− },
347−
348− /**
349− * Inserts tokens _before_ another token in a language definition or any other grammar.
350− *
351− * ## Usage
352− *
353− * This helper method makes it easy to modify existing languages. For example, the CSS language definition
354− * not only defines CSS highlighting for CSS documents, but also needs to define highlighting for CSS embedded
355− * in HTML through `<style>` elements. To do this, it needs to modify `Prism.languages.markup` and add the
356− * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object literal, so if you do
357− * this:
358− *
359− * ```js
360− * Prism.languages.markup.style = {
361− * // token
362− * };
363− * ```
364− *
365− * then the `style` token will be added (and processed) at the end. `insertBefore` allows you to insert tokens
366− * before existing tokens. For the CSS example above, you would use it like this:
367− *
368− * ```js
369− * Prism.languages.insertBefore('markup', 'cdata', {
370− * 'style': {
371− * // token
372− * }
373− * });
374− * ```
375− *
376− * ## Special cases
377− *
378− * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in `inside`'s grammar
379− * will be ignored.
380− *
381− * This behavior can be used to insert tokens after `before`:
382− *
383− * ```js
384− * Prism.languages.insertBefore('markup', 'comment', {
385− * 'comment': Prism.languages.markup.comment,
386− * // tokens after 'comment'
387− * });
388− * ```
389− *
390− * ## Limitations
391− *
392− * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the iteration order for object
393− * properties is guaranteed to be the insertion order (except for integer keys) but some browsers behave
394− * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented by temporarily
395− * deleting properties which is necessary to insert at arbitrary positions.
396− *
397− * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the target object.
398− * Instead, it will create a new object and replace all references to the target object with the new one. This
399− * can be done without temporarily deleting properties, so the iteration order is well-defined.
400− *
401− * However, only references that can be reached from `Prism.languages` or `insert` will be replaced. I.e. if
402− * you hold the target object in a variable, then the value of the variable will not change.
403− *
404− * ```js
405− * var oldMarkup = Prism.languages.markup;
406− * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... });
407− *
408− * assert(oldMarkup !== Prism.languages.markup);
409− * assert(newMarkup === Prism.languages.markup);
410− * ```
411− *
412− * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) that contains the
413− * object to be modified.
414− * @param {string} before The key to insert before.
415− * @param {Grammar} insert An object containing the key-value pairs to be inserted.
416− * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that contains the
417− * object to be modified.
418− *
419− * Defaults to `Prism.languages`.
420− * @returns {Grammar} The new grammar object.
421− * @public
422− */
423− insertBefore: function (inside, before, insert, root) {
424− root = root || /** @type {any} */ (_.languages);
425− var grammar = root[inside];
426− /** @type {Grammar} */
427− var ret = {};
428−
429− for (var token in grammar) {
430− if (grammar.hasOwnProperty(token)) {
431−
432− if (token == before) {
433− for (var newToken in insert) {
434− if (insert.hasOwnProperty(newToken)) {
435− ret[newToken] = insert[newToken];
436− }
437− }
438− }
439−
440− // Do not insert token which also occur in insert. See #1525
441− if (!insert.hasOwnProperty(token)) {
442− ret[token] = grammar[token];
443− }
444− }
445− }
446−
447− var old = root[inside];
448− root[inside] = ret;
449−
450− // Update references in other language definitions
451− _.languages.DFS(_.languages, function (key, value) {
452− if (value === old && key != inside) {
453− this[key] = ret;
454− }
455− });
456−
457− return ret;
458− },
459−
460− // Traverse a language definition with Depth First Search
461− DFS: function DFS(o, callback, type, visited) {
462− visited = visited || {};
463−
464− var objId = _.util.objId;
465−
466− for (var i in o) {
467− if (o.hasOwnProperty(i)) {
468− callback.call(o, i, o[i], type || i);
469−
470− var property = o[i];
471− var propertyType = _.util.type(property);
472−
473− if (propertyType === 'Object' && !visited[objId(property)]) {
474− visited[objId(property)] = true;
475− DFS(property, callback, null, visited);
476− } else if (propertyType === 'Array' && !visited[objId(property)]) {
477− visited[objId(property)] = true;
478− DFS(property, callback, i, visited);
479− }
480− }
481− }
482− }
483− },
484−
485− plugins: {},
486−
487− /**
488− * This is the most high-level function in Prism’s API.
489− * It fetches all the elements that have a `.language-xxxx` class and then calls {@link Prism.highlightElement} on
490− * each one of them.
491− *
492− * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`.
493− *
494− * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}.
495− * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}.
496− * @memberof Prism
497− * @public
498− */
499− highlightAll: function (async, callback) {
500− _.highlightAllUnder(document, async, callback);
501− },
502−
503− /**
504− * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls
505− * {@link Prism.highlightElement} on each one of them.
506− *
507− * The following hooks will be run:
508− * 1. `before-highlightall`
509− * 2. `before-all-elements-highlight`
510− * 3. All hooks of {@link Prism.highlightElement} for each element.
511− *
512− * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` class will be highlighted.
513− * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web Workers.
514− * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its highlighting is done.
515− * @memberof Prism
516− * @public
517− */
518− highlightAllUnder: function (container, async, callback) {
519− var env = {
520− callback: callback,
521− container: container,
522− selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
523− };
524−
525− _.hooks.run('before-highlightall', env);
526−
527− env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));
528−
529− _.hooks.run('before-all-elements-highlight', env);
530−
531− for (var i = 0, element; (element = env.elements[i++]);) {
532− _.highlightElement(element, async === true, env.callback);
533− }
534− },
535−
536− /**
537− * Highlights the code inside a single element.
538− *
539− * The following hooks will be run:
540− * 1. `before-sanity-check`
541− * 2. `before-highlight`
542− * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if `async` is `true`.
543− * 4. `before-insert`
544− * 5. `after-highlight`
545− * 6. `complete`
546− *
547− * Some the above hooks will be skipped if the element doesn't contain any text or there is no grammar loaded for
548− * the element's language.
549− *
550− * @param {Element} element The element containing the code.
551− * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language identifier.
552− * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web Workers
553− * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This option is
554− * [disabled by default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default).
555− *
556− * Note: All language definitions required to highlight the code must be included in the main `prism.js` file for
557− * asynchronous highlighting to work. You can build your own bundle on the
558− * [Download page](https://prismjs.com/download.html).
559− * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is done.
560− * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously.
561− * @memberof Prism
562− * @public
563− */
564− highlightElement: function (element, async, callback) {
565− // Find language
566− var language = _.util.getLanguage(element);
567− var grammar = _.languages[language];
568−
569− // Set language on the element, if not present
570− _.util.setLanguage(element, language);
571−
572− // Set language on the parent, for styling
573− var parent = element.parentElement;
574− if (parent && parent.nodeName.toLowerCase() === 'pre') {
575− _.util.setLanguage(parent, language);
576− }
577−
578− var code = element.textContent;
579−
580− var env = {
581− element: element,
582− language: language,
583− grammar: grammar,
584− code: code
585− };
586−
587− function insertHighlightedCode(highlightedCode) {
588− env.highlightedCode = highlightedCode;
589−
590− _.hooks.run('before-insert', env);
591−
592− env.element.innerHTML = env.highlightedCode;
593−
594− _.hooks.run('after-highlight', env);
595− _.hooks.run('complete', env);
596− callback && callback.call(env.element);
597− }
598−
599− _.hooks.run('before-sanity-check', env);
600−
601− // plugins may change/add the parent/element
602− parent = env.element.parentElement;
603− if (parent && parent.nodeName.toLowerCase() === 'pre' && !parent.hasAttribute('tabindex')) {
604− parent.setAttribute('tabindex', '0');
605− }
606−
607− if (!env.code) {
608− _.hooks.run('complete', env);
609− callback && callback.call(env.element);
610− return;
611− }
612−
613− _.hooks.run('before-highlight', env);
614−
615− if (!env.grammar) {
616− insertHighlightedCode(_.util.encode(env.code));
617− return;
618− }
619−
620− if (async && _self.Worker) {
621− var worker = new Worker(_.filename);
622−
623− worker.onmessage = function (evt) {
624− insertHighlightedCode(evt.data);
625− };
626−
627− worker.postMessage(JSON.stringify({
628− language: env.language,
629− code: env.code,
630− immediateClose: true
631− }));
632− } else {
633− insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
634− }
635− },
636−
637− /**
638− * Low-level function, only use if you know what you’re doing. It accepts a string of text as input
639− * and the language definitions to use, and returns a string with the HTML produced.
640− *
641− * The following hooks will be run:
642− * 1. `before-tokenize`
643− * 2. `after-tokenize`
644− * 3. `wrap`: On each {@link Token}.
645− *
646− * @param {string} text A string with the code to be highlighted.
647− * @param {Grammar} grammar An object containing the tokens to use.
648− *
649− * Usually a language definition like `Prism.languages.markup`.
650− * @param {string} language The name of the language definition passed to `grammar`.
651− * @returns {string} The highlighted HTML.
652− * @memberof Prism
653− * @public
654− * @example
655− * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
656− */
657− highlight: function (text, grammar, language) {
658− var env = {
659− code: text,
660− grammar: grammar,
661− language: language
662− };
663− _.hooks.run('before-tokenize', env);
664− if (!env.grammar) {
665− throw new Error('The language "' + env.language + '" has no grammar.');
666− }
667− env.tokens = _.tokenize(env.code, env.grammar);
668− _.hooks.run('after-tokenize', env);
669− return Token.stringify(_.util.encode(env.tokens), env.language);
670− },
671−
672− /**
673− * This is the heart of Prism, and the most low-level function you can use. It accepts a string of text as input
674− * and the language definitions to use, and returns an array with the tokenized code.
675− *
676− * When the language definition includes nested tokens, the function is called recursively on each of these tokens.
677− *
678− * This method could be useful in other contexts as well, as a very crude parser.
679− *
680− * @param {string} text A string with the code to be highlighted.
681− * @param {Grammar} grammar An object containing the tokens to use.
682− *
683− * Usually a language definition like `Prism.languages.markup`.
684− * @returns {TokenStream} An array of strings and tokens, a token stream.
685− * @memberof Prism
686− * @public
687− * @example
688− * let code = `var foo = 0;`;
689− * let tokens = Prism.tokenize(code, Prism.languages.javascript);
690− * tokens.forEach(token => {
691− * if (token instanceof Prism.Token && token.type === 'number') {
692− * console.log(`Found numeric literal: ${token.content}`);
693− * }
694− * });
695− */
696− tokenize: function (text, grammar) {
697− var rest = grammar.rest;
698− if (rest) {
699− for (var token in rest) {
700− grammar[token] = rest[token];
701− }
702−
703− delete grammar.rest;
704− }
705−
706− var tokenList = new LinkedList();
707− addAfter(tokenList, tokenList.head, text);
708−
709− matchGrammar(text, tokenList, grammar, tokenList.head, 0);
710−
711− return toArray(tokenList);
712− },
713−
714− /**
715− * @namespace
716− * @memberof Prism
717− * @public
718− */
719− hooks: {
720− all: {},
721−
722− /**
723− * Adds the given callback to the list of callbacks for the given hook.
724− *
725− * The callback will be invoked when the hook it is registered for is run.
726− * Hooks are usually directly run by a highlight function but you can also run hooks yourself.
727− *
728− * One callback function can be registered to multiple hooks and the same hook multiple times.
729− *
730− * @param {string} name The name of the hook.
731− * @param {HookCallback} callback The callback function which is given environment variables.
732− * @public
733− */
734− add: function (name, callback) {
735− var hooks = _.hooks.all;
736−
737− hooks[name] = hooks[name] || [];
738−
739− hooks[name].push(callback);
740− },
741−
742− /**
743− * Runs a hook invoking all registered callbacks with the given environment variables.
744− *
745− * Callbacks will be invoked synchronously and in the order in which they were registered.
746− *
747− * @param {string} name The name of the hook.
748− * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered.
749− * @public
750− */
751− run: function (name, env) {
752− var callbacks = _.hooks.all[name];
753−
754− if (!callbacks || !callbacks.length) {
755− return;
756− }
757−
758− for (var i = 0, callback; (callback = callbacks[i++]);) {
759− callback(env);
760− }
761− }
762− },
763−
764− Token: Token
765− };
766− _self.Prism = _;
767−
768−
769− // Typescript note:
770− // The following can be used to import the Token type in JSDoc:
771− //
772− // @typedef {InstanceType<import("./prism-core")["Token"]>} Token
773−
774− /**
775− * Creates a new token.
776− *
777− * @param {string} type See {@link Token#type type}
778− * @param {string | TokenStream} content See {@link Token#content content}
779− * @param {string|string[]} [alias] The alias(es) of the token.
780− * @param {string} [matchedStr=""] A copy of the full string this token was created from.
781− * @class
782− * @global
783− * @public
784− */
785− function Token(type, content, alias, matchedStr) {
786− /**
787− * The type of the token.
788− *
789− * This is usually the key of a pattern in a {@link Grammar}.
790− *
791− * @type {string}
792− * @see GrammarToken
793− * @public
794− */
795− this.type = type;
796− /**
797− * The strings or tokens contained by this token.
798− *
799− * This will be a token stream if the pattern matched also defined an `inside` grammar.
800− *
801− * @type {string | TokenStream}
802− * @public
803− */
804− this.content = content;
805− /**
806− * The alias(es) of the token.
807− *
808− * @type {string|string[]}
809− * @see GrammarToken
810− * @public
811− */
812− this.alias = alias;
813− // Copy of the full string this token was created from
814− this.length = (matchedStr || '').length | 0;
815− }
816−
817− /**
818− * A token stream is an array of strings and {@link Token Token} objects.
819− *
820− * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) that process
821− * them.
822− *
823− * 1. No adjacent strings.
824− * 2. No empty strings.
825− *
826− * The only exception here is the token stream that only contains the empty string and nothing else.
827− *
828− * @typedef {Array<string | Token>} TokenStream
829− * @global
830− * @public
831− */
832−
833− /**
834− * Converts the given token or token stream to an HTML representation.
835− *
836− * The following hooks will be run:
837− * 1. `wrap`: On each {@link Token}.
838− *
839− * @param {string | Token | TokenStream} o The token or token stream to be converted.
840− * @param {string} language The name of current language.
841− * @returns {string} The HTML representation of the token or token stream.
842− * @memberof Token
843− * @static
844− */
845− Token.stringify = function stringify(o, language) {
846− if (typeof o == 'string') {
847− return o;
848− }
849− if (Array.isArray(o)) {
850− var s = '';
851− o.forEach(function (e) {
852− s += stringify(e, language);
853− });
854− return s;
855− }
856−
857− var env = {
858− type: o.type,
859− content: stringify(o.content, language),
860− tag: 'span',
861− classes: ['token', o.type],
862− attributes: {},
863− language: language
864− };
865−
866− var aliases = o.alias;
867− if (aliases) {
868− if (Array.isArray(aliases)) {
869− Array.prototype.push.apply(env.classes, aliases);
870− } else {
871− env.classes.push(aliases);
872− }
873− }
874−
875− _.hooks.run('wrap', env);
876−
877− var attributes = '';
878− for (var name in env.attributes) {
879− attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
880− }
881−
882− return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + env.content + '</' + env.tag + '>';
883− };
884−
885− /**
886− * @param {RegExp} pattern
887− * @param {number} pos
888− * @param {string} text
889− * @param {boolean} lookbehind
890− * @returns {RegExpExecArray | null}
891− */
892− function matchPattern(pattern, pos, text, lookbehind) {
893− pattern.lastIndex = pos;
894− var match = pattern.exec(text);
895− if (match && lookbehind && match[1]) {
896− // change the match to remove the text matched by the Prism lookbehind group
897− var lookbehindLength = match[1].length;
898− match.index += lookbehindLength;
899− match[0] = match[0].slice(lookbehindLength);
900− }
901− return match;
902− }
903−
904− /**
905− * @param {string} text
906− * @param {LinkedList<string | Token>} tokenList
907− * @param {any} grammar
908− * @param {LinkedListNode<string | Token>} startNode
909− * @param {number} startPos
910− * @param {RematchOptions} [rematch]
911− * @returns {void}
912− * @private
913− *
914− * @typedef RematchOptions
915− * @property {string} cause
916− * @property {number} reach
917− */
918− function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) {
919− for (var token in grammar) {
920− if (!grammar.hasOwnProperty(token) || !grammar[token]) {
921− continue;
922− }
923−
924− var patterns = grammar[token];
925− patterns = Array.isArray(patterns) ? patterns : [patterns];
926−
927− for (var j = 0; j < patterns.length; ++j) {
928− if (rematch && rematch.cause == token + ',' + j) {
929− return;
930− }
931−
932− var patternObj = patterns[j];
933− var inside = patternObj.inside;
934− var lookbehind = !!patternObj.lookbehind;
935− var greedy = !!patternObj.greedy;
936− var alias = patternObj.alias;
937−
938− if (greedy && !patternObj.pattern.global) {
939− // Without the global flag, lastIndex won't work
940− var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];
941− patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g');
942− }
943−
944− /** @type {RegExp} */
945− var pattern = patternObj.pattern || patternObj;
946−
947− for ( // iterate the token list and keep track of the current token/string position
948− var currentNode = startNode.next, pos = startPos;
949− currentNode !== tokenList.tail;
950− pos += currentNode.value.length, currentNode = currentNode.next
951− ) {
952−
953− if (rematch && pos >= rematch.reach) {
954− break;
955− }
956−
957− var str = currentNode.value;
958−
959− if (tokenList.length > text.length) {
960− // Something went terribly wrong, ABORT, ABORT!
961− return;
962− }
963−
964− if (str instanceof Token) {
965− continue;
966− }
967−
968− var removeCount = 1; // this is the to parameter of removeBetween
969− var match;
970−
971− if (greedy) {
972− match = matchPattern(pattern, pos, text, lookbehind);
973− if (!match || match.index >= text.length) {
974− break;
975− }
976−
977− var from = match.index;
978− var to = match.index + match[0].length;
979− var p = pos;
980−
981− // find the node that contains the match
982− p += currentNode.value.length;
983− while (from >= p) {
984− currentNode = currentNode.next;
985− p += currentNode.value.length;
986− }
987− // adjust pos (and p)
988− p -= currentNode.value.length;
989− pos = p;
990−
991− // the current node is a Token, then the match starts inside another Token, which is invalid
992− if (currentNode.value instanceof Token) {
993− continue;
994− }
995−
996− // find the last node which is affected by this match
997− for (
998− var k = currentNode;
999− k !== tokenList.tail && (p < to || typeof k.value === 'string');
1000− k = k.next
1001− ) {
1002− removeCount++;
1003− p += k.value.length;
1004− }
1005− removeCount--;
1006−
1007− // replace with the new match
1008− str = text.slice(pos, p);
1009− match.index -= pos;
1010− } else {
1011− match = matchPattern(pattern, 0, str, lookbehind);
1012− if (!match) {
1013− continue;
1014− }
1015− }
1016−
1017− // eslint-disable-next-line no-redeclare
1018− var from = match.index;
1019− var matchStr = match[0];
1020− var before = str.slice(0, from);
1021− var after = str.slice(from + matchStr.length);
1022−
1023− var reach = pos + str.length;
1024− if (rematch && reach > rematch.reach) {
1025− rematch.reach = reach;
1026− }
1027−
1028− var removeFrom = currentNode.prev;
1029−
1030− if (before) {
1031− removeFrom = addAfter(tokenList, removeFrom, before);
1032− pos += before.length;
1033− }
1034−
1035− removeRange(tokenList, removeFrom, removeCount);
1036−
1037− var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) : matchStr, alias, matchStr);
1038− currentNode = addAfter(tokenList, removeFrom, wrapped);
1039−
1040− if (after) {
1041− addAfter(tokenList, currentNode, after);
1042− }
1043−
1044− if (removeCount > 1) {
1045− // at least one Token object was removed, so we have to do some rematching
1046− // this can only happen if the current pattern is greedy
1047−
1048− /** @type {RematchOptions} */
1049− var nestedRematch = {
1050− cause: token + ',' + j,
1051− reach: reach
1052− };
1053− matchGrammar(text, tokenList, grammar, currentNode.prev, pos, nestedRematch);
1054−
1055− // the reach might have been extended because of the rematching
1056− if (rematch && nestedRematch.reach > rematch.reach) {
1057− rematch.reach = nestedRematch.reach;
1058− }
1059− }
1060− }
1061− }
1062− }
1063− }
1064−
1065− /**
1066− * @typedef LinkedListNode
1067− * @property {T} value
1068− * @property {LinkedListNode<T> | null} prev The previous node.
1069− * @property {LinkedListNode<T> | null} next The next node.
1070− * @template T
1071− * @private
1072− */
1073−
1074− /**
1075− * @template T
1076− * @private
1077− */
1078− function LinkedList() {
1079− /** @type {LinkedListNode<T>} */
1080− var head = { value: null, prev: null, next: null };
1081− /** @type {LinkedListNode<T>} */
1082− var tail = { value: null, prev: head, next: null };
1083− head.next = tail;
1084−
1085− /** @type {LinkedListNode<T>} */
1086− this.head = head;
1087− /** @type {LinkedListNode<T>} */
1088− this.tail = tail;
1089− this.length = 0;
1090− }
1091−
1092− /**
1093− * Adds a new node with the given value to the list.
1094− *
1095− * @param {LinkedList<T>} list
1096− * @param {LinkedListNode<T>} node
1097− * @param {T} value
1098− * @returns {LinkedListNode<T>} The added node.
1099− * @template T
1100− */
1101− function addAfter(list, node, value) {
1102− // assumes that node != list.tail && values.length >= 0
1103− var next = node.next;
1104−
1105− var newNode = { value: value, prev: node, next: next };
1106− node.next = newNode;
1107− next.prev = newNode;
1108− list.length++;
1109−
1110− return newNode;
1111− }
1112− /**
1113− * Removes `count` nodes after the given node. The given node will not be removed.
1114− *
1115− * @param {LinkedList<T>} list
1116− * @param {LinkedListNode<T>} node
1117− * @param {number} count
1118− * @template T
1119− */
1120− function removeRange(list, node, count) {
1121− var next = node.next;
1122− for (var i = 0; i < count && next !== list.tail; i++) {
1123− next = next.next;
1124− }
1125− node.next = next;
1126− next.prev = node;
1127− list.length -= i;
1128− }
1129− /**
1130− * @param {LinkedList<T>} list
1131− * @returns {T[]}
1132− * @template T
1133− */
1134− function toArray(list) {
1135− var array = [];
1136− var node = list.head.next;
1137− while (node !== list.tail) {
1138− array.push(node.value);
1139− node = node.next;
1140− }
1141− return array;
1142− }
1143−
1144−
1145− if (!_self.document) {
1146− if (!_self.addEventListener) {
1147− // in Node.js
1148− return _;
1149− }
1150−
1151− if (!_.disableWorkerMessageHandler) {
1152− // In worker
1153− _self.addEventListener('message', function (evt) {
1154− var message = JSON.parse(evt.data);
1155− var lang = message.language;
1156− var code = message.code;
1157− var immediateClose = message.immediateClose;
1158−
1159− _self.postMessage(_.highlight(code, _.languages[lang], lang));
1160− if (immediateClose) {
1161− _self.close();
1162− }
1163− }, false);
1164− }
1165−
1166− return _;
1167− }
1168−
1169− // Get current script and highlight
1170− var script = _.util.currentScript();
1171−
1172− if (script) {
1173− _.filename = script.src;
1174−
1175− if (script.hasAttribute('data-manual')) {
1176− _.manual = true;
1177− }
1178− }
1179−
1180− function highlightAutomaticallyCallback() {
1181− if (!_.manual) {
1182− _.highlightAll();
1183− }
1184− }
1185−
1186− if (!_.manual) {
1187− // If the document state is "loading", then we'll use DOMContentLoaded.
1188− // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the
1189− // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they
1190− // might take longer one animation frame to execute which can create a race condition where only some plugins have
1191− // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded.
1192− // See https://github.com/PrismJS/prism/issues/2102
1193− var readyState = document.readyState;
1194− if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {
1195− document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);
1196− } else {
1197− if (window.requestAnimationFrame) {
1198− window.requestAnimationFrame(highlightAutomaticallyCallback);
1199− } else {
1200− window.setTimeout(highlightAutomaticallyCallback, 16);
1201− }
1202− }
1203− }
1204−
1205− return _;
1206−
1207−}(_self));
1208−
1209−if (typeof module !== 'undefined' && module.exports) {
1210− module.exports = Prism;
22+ // Private helper vars
23+ var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i;
24+ var uniqueId = 0;
25+
26+ // The grammar object for plaintext
27+ var plainTextGrammar = {};
28+
29+ var _ = {
30+ /**
31+ * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the
32+ * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load
33+ * additional languages or plugins yourself.
34+ *
35+ * By setting this value to `true`, Prism will not automatically highlight all code elements on the page.
36+ *
37+ * You obviously have to change this value before the automatic highlighting started. To do this, you can add an
38+ * empty Prism object into the global scope before loading the Prism script like this:
39+ *
40+ * ```js
41+ * window.Prism = window.Prism || {};
42+ * Prism.manual = true;
43+ * // add a new <script> to load Prism's script
44+ * ```
45+ *
46+ * @default false
47+ * @type {boolean}
48+ * @memberof Prism
49+ * @public
50+ */
51+ manual: _self.Prism && _self.Prism.manual,
52+ /**
53+ * By default, if Prism is in a web worker, it assumes that it is in a worker it created itself, so it uses
54+ * `addEventListener` to communicate with its parent instance. However, if you're using Prism manually in your
55+ * own worker, you don't want it to do this.
56+ *
57+ * By setting this value to `true`, Prism will not add its own listeners to the worker.
58+ *
59+ * You obviously have to change this value before Prism executes. To do this, you can add an
60+ * empty Prism object into the global scope before loading the Prism script like this:
61+ *
62+ * ```js
63+ * window.Prism = window.Prism || {};
64+ * Prism.disableWorkerMessageHandler = true;
65+ * // Load Prism's script
66+ * ```
67+ *
68+ * @default false
69+ * @type {boolean}
70+ * @memberof Prism
71+ * @public
72+ */
73+ disableWorkerMessageHandler:
74+ _self.Prism && _self.Prism.disableWorkerMessageHandler,
75+
76+ /**
77+ * A namespace for utility methods.
78+ *
79+ * All function in this namespace that are not explicitly marked as _public_ are for __internal use only__ and may
80+ * change or disappear at any time.
81+ *
82+ * @namespace
83+ * @memberof Prism
84+ */
85+ util: {
86+ encode: function encode(tokens) {
87+ if (tokens instanceof Token) {
88+ return new Token(tokens.type, encode(tokens.content), tokens.alias);
89+ } else if (Array.isArray(tokens)) {
90+ return tokens.map(encode);
91+ } else {
92+ return tokens
93+ .replace(/&/g, "&amp;")
94+ .replace(/</g, "&lt;")
95+ .replace(/\u00a0/g, " ");
96+ }
97+ },
98+
99+ /**
100+ * Returns the name of the type of the given value.
101+ *
102+ * @param {any} o
103+ * @returns {string}
104+ * @example
105+ * type(null) === 'Null'
106+ * type(undefined) === 'Undefined'
107+ * type(123) === 'Number'
108+ * type('foo') === 'String'
109+ * type(true) === 'Boolean'
110+ * type([1, 2]) === 'Array'
111+ * type({}) === 'Object'
112+ * type(String) === 'Function'
113+ * type(/abc+/) === 'RegExp'
114+ */
115+ type: function (o) {
116+ return Object.prototype.toString.call(o).slice(8, -1);
117+ },
118+
119+ /**
120+ * Returns a unique number for the given object. Later calls will still return the same number.
121+ *
122+ * @param {Object} obj
123+ * @returns {number}
124+ */
125+ objId: function (obj) {
126+ if (!obj["__id"]) {
127+ Object.defineProperty(obj, "__id", { value: ++uniqueId });
128+ }
129+ return obj["__id"];
130+ },
131+
132+ /**
133+ * Creates a deep clone of the given object.
134+ *
135+ * The main intended use of this function is to clone language definitions.
136+ *
137+ * @param {T} o
138+ * @param {Record<number, any>} [visited]
139+ * @returns {T}
140+ * @template T
141+ */
142+ clone: function deepClone(o, visited) {
143+ visited = visited || {};
144+
145+ var clone;
146+ var id;
147+ switch (_.util.type(o)) {
148+ case "Object":
149+ id = _.util.objId(o);
150+ if (visited[id]) {
151+ return visited[id];
152+ }
153+ clone = /** @type {Record<string, any>} */ ({});
154+ visited[id] = clone;
155+
156+ for (var key in o) {
157+ if (o.hasOwnProperty(key)) {
158+ clone[key] = deepClone(o[key], visited);
159+ }
160+ }
161+
162+ return /** @type {any} */ (clone);
163+
164+ case "Array":
165+ id = _.util.objId(o);
166+ if (visited[id]) {
167+ return visited[id];
168+ }
169+ clone = [];
170+ visited[id] = clone;
171+
172+ /** @type {Array} */ (/** @type {any} */ (o)).forEach(
173+ function (v, i) {
174+ clone[i] = deepClone(v, visited);
175+ },
176+ );
177+
178+ return /** @type {any} */ (clone);
179+
180+ default:
181+ return o;
182+ }
183+ },
184+
185+ /**
186+ * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class.
187+ *
188+ * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned.
189+ *
190+ * @param {Element} element
191+ * @returns {string}
192+ */
193+ getLanguage: function (element) {
194+ while (element) {
195+ var m = lang.exec(element.className);
196+ if (m) {
197+ return m[1].toLowerCase();
198+ }
199+ element = element.parentElement;
200+ }
201+ return "none";
202+ },
203+
204+ /**
205+ * Sets the Prism `language-xxxx` class of the given element.
206+ *
207+ * @param {Element} element
208+ * @param {string} language
209+ * @returns {void}
210+ */
211+ setLanguage: function (element, language) {
212+ // remove all `language-xxxx` classes
213+ // (this might leave behind a leading space)
214+ element.className = element.className.replace(RegExp(lang, "gi"), "");
215+
216+ // add the new `language-xxxx` class
217+ // (using `classList` will automatically clean up spaces for us)
218+ element.classList.add("language-" + language);
219+ },
220+
221+ /**
222+ * Returns the script element that is currently executing.
223+ *
224+ * This does __not__ work for line script element.
225+ *
226+ * @returns {HTMLScriptElement | null}
227+ */
228+ currentScript: function () {
229+ if (typeof document === "undefined") {
230+ return null;
231+ }
232+ if (
233+ "currentScript" in document &&
234+ 1 < 2 /* hack to trip TS' flow analysis */
235+ ) {
236+ return /** @type {any} */ (document.currentScript);
237+ }
238+
239+ // IE11 workaround
240+ // we'll get the src of the current script by parsing IE11's error stack trace
241+ // this will not work for inline scripts
242+
243+ try {
244+ throw new Error();
245+ } catch (err) {
246+ // Get file src url from stack. Specifically works with the format of stack traces in IE.
247+ // A stack will look like this:
248+ //
249+ // Error
250+ // at _.util.currentScript (http://localhost/components/prism-core.js:119:5)
251+ // at Global code (http://localhost/components/prism-core.js:606:1)
252+
253+ var src = (/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(err.stack) ||
254+ [])[1];
255+ if (src) {
256+ var scripts = document.getElementsByTagName("script");
257+ for (var i in scripts) {
258+ if (scripts[i].src == src) {
259+ return scripts[i];
260+ }
261+ }
262+ }
263+ return null;
264+ }
265+ },
266+
267+ /**
268+ * Returns whether a given class is active for `element`.
269+ *
270+ * The class can be activated if `element` or one of its ancestors has the given class and it can be deactivated
271+ * if `element` or one of its ancestors has the negated version of the given class. The _negated version_ of the
272+ * given class is just the given class with a `no-` prefix.
273+ *
274+ * Whether the class is active is determined by the closest ancestor of `element` (where `element` itself is
275+ * closest ancestor) that has the given class or the negated version of it. If neither `element` nor any of its
276+ * ancestors have the given class or the negated version of it, then the default activation will be returned.
277+ *
278+ * In the paradoxical situation where the closest ancestor contains __both__ the given class and the negated
279+ * version of it, the class is considered active.
280+ *
281+ * @param {Element} element
282+ * @param {string} className
283+ * @param {boolean} [defaultActivation=false]
284+ * @returns {boolean}
285+ */
286+ isActive: function (element, className, defaultActivation) {
287+ var no = "no-" + className;
288+
289+ while (element) {
290+ var classList = element.classList;
291+ if (classList.contains(className)) {
292+ return true;
293+ }
294+ if (classList.contains(no)) {
295+ return false;
296+ }
297+ element = element.parentElement;
298+ }
299+ return !!defaultActivation;
300+ },
301+ },
302+
303+ /**
304+ * This namespace contains all currently loaded languages and the some helper functions to create and modify languages.
305+ *
306+ * @namespace
307+ * @memberof Prism
308+ * @public
309+ */
310+ languages: {
311+ /**
312+ * The grammar for plain, unformatted text.
313+ */
314+ plain: plainTextGrammar,
315+ plaintext: plainTextGrammar,
316+ text: plainTextGrammar,
317+ txt: plainTextGrammar,
318+
319+ /**
320+ * Creates a deep copy of the language with the given id and appends the given tokens.
321+ *
322+ * If a token in `redef` also appears in the copied language, then the existing token in the copied language
323+ * will be overwritten at its original position.
324+ *
325+ * ## Best practices
326+ *
327+ * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the copied language)
328+ * doesn't matter, they can technically be in any order. However, this can be confusing to others that trying to
329+ * understand the language definition because, normally, the order of tokens matters in Prism grammars.
330+ *
331+ * Therefore, it is encouraged to order overwriting tokens according to the positions of the overwritten tokens.
332+ * Furthermore, all non-overwriting tokens should be placed after the overwriting ones.
333+ *
334+ * @param {string} id The id of the language to extend. This has to be a key in `Prism.languages`.
335+ * @param {Grammar} redef The new tokens to append.
336+ * @returns {Grammar} The new language created.
337+ * @public
338+ * @example
339+ * Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
340+ * // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token
341+ * // at its original position
342+ * 'comment': { ... },
343+ * // CSS doesn't have a 'color' token, so this token will be appended
344+ * 'color': /\b(?:red|green|blue)\b/
345+ * });
346+ */
347+ extend: function (id, redef) {
348+ var lang = _.util.clone(_.languages[id]);
349+
350+ for (var key in redef) {
351+ lang[key] = redef[key];
352+ }
353+
354+ return lang;
355+ },
356+
357+ /**
358+ * Inserts tokens _before_ another token in a language definition or any other grammar.
359+ *
360+ * ## Usage
361+ *
362+ * This helper method makes it easy to modify existing languages. For example, the CSS language definition
363+ * not only defines CSS highlighting for CSS documents, but also needs to define highlighting for CSS embedded
364+ * in HTML through `<style>` elements. To do this, it needs to modify `Prism.languages.markup` and add the
365+ * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object literal, so if you do
366+ * this:
367+ *
368+ * ```js
369+ * Prism.languages.markup.style = {
370+ * // token
371+ * };
372+ * ```
373+ *
374+ * then the `style` token will be added (and processed) at the end. `insertBefore` allows you to insert tokens
375+ * before existing tokens. For the CSS example above, you would use it like this:
376+ *
377+ * ```js
378+ * Prism.languages.insertBefore('markup', 'cdata', {
379+ * 'style': {
380+ * // token
381+ * }
382+ * });
383+ * ```
384+ *
385+ * ## Special cases
386+ *
387+ * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in `inside`'s grammar
388+ * will be ignored.
389+ *
390+ * This behavior can be used to insert tokens after `before`:
391+ *
392+ * ```js
393+ * Prism.languages.insertBefore('markup', 'comment', {
394+ * 'comment': Prism.languages.markup.comment,
395+ * // tokens after 'comment'
396+ * });
397+ * ```
398+ *
399+ * ## Limitations
400+ *
401+ * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the iteration order for object
402+ * properties is guaranteed to be the insertion order (except for integer keys) but some browsers behave
403+ * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented by temporarily
404+ * deleting properties which is necessary to insert at arbitrary positions.
405+ *
406+ * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the target object.
407+ * Instead, it will create a new object and replace all references to the target object with the new one. This
408+ * can be done without temporarily deleting properties, so the iteration order is well-defined.
409+ *
410+ * However, only references that can be reached from `Prism.languages` or `insert` will be replaced. I.e. if
411+ * you hold the target object in a variable, then the value of the variable will not change.
412+ *
413+ * ```js
414+ * var oldMarkup = Prism.languages.markup;
415+ * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... });
416+ *
417+ * assert(oldMarkup !== Prism.languages.markup);
418+ * assert(newMarkup === Prism.languages.markup);
419+ * ```
420+ *
421+ * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) that contains the
422+ * object to be modified.
423+ * @param {string} before The key to insert before.
424+ * @param {Grammar} insert An object containing the key-value pairs to be inserted.
425+ * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that contains the
426+ * object to be modified.
427+ *
428+ * Defaults to `Prism.languages`.
429+ * @returns {Grammar} The new grammar object.
430+ * @public
431+ */
432+ insertBefore: function (inside, before, insert, root) {
433+ root = root || /** @type {any} */ (_.languages);
434+ var grammar = root[inside];
435+ /** @type {Grammar} */
436+ var ret = {};
437+
438+ for (var token in grammar) {
439+ if (grammar.hasOwnProperty(token)) {
440+ if (token == before) {
441+ for (var newToken in insert) {
442+ if (insert.hasOwnProperty(newToken)) {
443+ ret[newToken] = insert[newToken];
444+ }
445+ }
446+ }
447+
448+ // Do not insert token which also occur in insert. See #1525
449+ if (!insert.hasOwnProperty(token)) {
450+ ret[token] = grammar[token];
451+ }
452+ }
453+ }
454+
455+ var old = root[inside];
456+ root[inside] = ret;
457+
458+ // Update references in other language definitions
459+ _.languages.DFS(_.languages, function (key, value) {
460+ if (value === old && key != inside) {
461+ this[key] = ret;
462+ }
463+ });
464+
465+ return ret;
466+ },
467+
468+ // Traverse a language definition with Depth First Search
469+ DFS: function DFS(o, callback, type, visited) {
470+ visited = visited || {};
471+
472+ var objId = _.util.objId;
473+
474+ for (var i in o) {
475+ if (o.hasOwnProperty(i)) {
476+ callback.call(o, i, o[i], type || i);
477+
478+ var property = o[i];
479+ var propertyType = _.util.type(property);
480+
481+ if (propertyType === "Object" && !visited[objId(property)]) {
482+ visited[objId(property)] = true;
483+ DFS(property, callback, null, visited);
484+ } else if (propertyType === "Array" && !visited[objId(property)]) {
485+ visited[objId(property)] = true;
486+ DFS(property, callback, i, visited);
487+ }
488+ }
489+ }
490+ },
491+ },
492+
493+ plugins: {},
494+
495+ /**
496+ * This is the most high-level function in Prism’s API.
497+ * It fetches all the elements that have a `.language-xxxx` class and then calls {@link Prism.highlightElement} on
498+ * each one of them.
499+ *
500+ * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`.
501+ *
502+ * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}.
503+ * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}.
504+ * @memberof Prism
505+ * @public
506+ */
507+ highlightAll: function (async, callback) {
508+ _.highlightAllUnder(document, async, callback);
509+ },
510+
511+ /**
512+ * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls
513+ * {@link Prism.highlightElement} on each one of them.
514+ *
515+ * The following hooks will be run:
516+ * 1. `before-highlightall`
517+ * 2. `before-all-elements-highlight`
518+ * 3. All hooks of {@link Prism.highlightElement} for each element.
519+ *
520+ * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` class will be highlighted.
521+ * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web Workers.
522+ * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its highlighting is done.
523+ * @memberof Prism
524+ * @public
525+ */
526+ highlightAllUnder: function (container, async, callback) {
527+ var env = {
528+ callback: callback,
529+ container: container,
530+ selector:
531+ 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code',
532+ };
533+
534+ _.hooks.run("before-highlightall", env);
535+
536+ env.elements = Array.prototype.slice.apply(
537+ env.container.querySelectorAll(env.selector),
538+ );
539+
540+ _.hooks.run("before-all-elements-highlight", env);
541+
542+ for (var i = 0, element; (element = env.elements[i++]); ) {
543+ _.highlightElement(element, async === true, env.callback);
544+ }
545+ },
546+
547+ /**
548+ * Highlights the code inside a single element.
549+ *
550+ * The following hooks will be run:
551+ * 1. `before-sanity-check`
552+ * 2. `before-highlight`
553+ * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if `async` is `true`.
554+ * 4. `before-insert`
555+ * 5. `after-highlight`
556+ * 6. `complete`
557+ *
558+ * Some the above hooks will be skipped if the element doesn't contain any text or there is no grammar loaded for
559+ * the element's language.
560+ *
561+ * @param {Element} element The element containing the code.
562+ * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language identifier.
563+ * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web Workers
564+ * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This option is
565+ * [disabled by default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default).
566+ *
567+ * Note: All language definitions required to highlight the code must be included in the main `prism.js` file for
568+ * asynchronous highlighting to work. You can build your own bundle on the
569+ * [Download page](https://prismjs.com/download.html).
570+ * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is done.
571+ * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously.
572+ * @memberof Prism
573+ * @public
574+ */
575+ highlightElement: function (element, async, callback) {
576+ // Find language
577+ var language = _.util.getLanguage(element);
578+ var grammar = _.languages[language];
579+
580+ // Set language on the element, if not present
581+ _.util.setLanguage(element, language);
582+
583+ // Set language on the parent, for styling
584+ var parent = element.parentElement;
585+ if (parent && parent.nodeName.toLowerCase() === "pre") {
586+ _.util.setLanguage(parent, language);
587+ }
588+
589+ var code = element.textContent;
590+
591+ var env = {
592+ element: element,
593+ language: language,
594+ grammar: grammar,
595+ code: code,
596+ };
597+
598+ function insertHighlightedCode(highlightedCode) {
599+ env.highlightedCode = highlightedCode;
600+
601+ _.hooks.run("before-insert", env);
602+
603+ env.element.innerHTML = env.highlightedCode;
604+
605+ _.hooks.run("after-highlight", env);
606+ _.hooks.run("complete", env);
607+ callback && callback.call(env.element);
608+ }
609+
610+ _.hooks.run("before-sanity-check", env);
611+
612+ // plugins may change/add the parent/element
613+ parent = env.element.parentElement;
614+ if (
615+ parent &&
616+ parent.nodeName.toLowerCase() === "pre" &&
617+ !parent.hasAttribute("tabindex")
618+ ) {
619+ parent.setAttribute("tabindex", "0");
620+ }
621+
622+ if (!env.code) {
623+ _.hooks.run("complete", env);
624+ callback && callback.call(env.element);
625+ return;
626+ }
627+
628+ _.hooks.run("before-highlight", env);
629+
630+ if (!env.grammar) {
631+ insertHighlightedCode(_.util.encode(env.code));
632+ return;
633+ }
634+
635+ if (async && _self.Worker) {
636+ var worker = new Worker(_.filename);
637+
638+ worker.onmessage = function (evt) {
639+ insertHighlightedCode(evt.data);
640+ };
641+
642+ worker.postMessage(
643+ JSON.stringify({
644+ language: env.language,
645+ code: env.code,
646+ immediateClose: true,
647+ }),
648+ );
649+ } else {
650+ insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
651+ }
652+ },
653+
654+ /**
655+ * Low-level function, only use if you know what you’re doing. It accepts a string of text as input
656+ * and the language definitions to use, and returns a string with the HTML produced.
657+ *
658+ * The following hooks will be run:
659+ * 1. `before-tokenize`
660+ * 2. `after-tokenize`
661+ * 3. `wrap`: On each {@link Token}.
662+ *
663+ * @param {string} text A string with the code to be highlighted.
664+ * @param {Grammar} grammar An object containing the tokens to use.
665+ *
666+ * Usually a language definition like `Prism.languages.markup`.
667+ * @param {string} language The name of the language definition passed to `grammar`.
668+ * @returns {string} The highlighted HTML.
669+ * @memberof Prism
670+ * @public
671+ * @example
672+ * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
673+ */
674+ highlight: function (text, grammar, language) {
675+ var env = {
676+ code: text,
677+ grammar: grammar,
678+ language: language,
679+ };
680+ _.hooks.run("before-tokenize", env);
681+ if (!env.grammar) {
682+ throw new Error('The language "' + env.language + '" has no grammar.');
683+ }
684+ env.tokens = _.tokenize(env.code, env.grammar);
685+ _.hooks.run("after-tokenize", env);
686+ return Token.stringify(_.util.encode(env.tokens), env.language);
687+ },
688+
689+ /**
690+ * This is the heart of Prism, and the most low-level function you can use. It accepts a string of text as input
691+ * and the language definitions to use, and returns an array with the tokenized code.
692+ *
693+ * When the language definition includes nested tokens, the function is called recursively on each of these tokens.
694+ *
695+ * This method could be useful in other contexts as well, as a very crude parser.
696+ *
697+ * @param {string} text A string with the code to be highlighted.
698+ * @param {Grammar} grammar An object containing the tokens to use.
699+ *
700+ * Usually a language definition like `Prism.languages.markup`.
701+ * @returns {TokenStream} An array of strings and tokens, a token stream.
702+ * @memberof Prism
703+ * @public
704+ * @example
705+ * let code = `var foo = 0;`;
706+ * let tokens = Prism.tokenize(code, Prism.languages.javascript);
707+ * tokens.forEach(token => {
708+ * if (token instanceof Prism.Token && token.type === 'number') {
709+ * console.log(`Found numeric literal: ${token.content}`);
710+ * }
711+ * });
712+ */
713+ tokenize: function (text, grammar) {
714+ var rest = grammar.rest;
715+ if (rest) {
716+ for (var token in rest) {
717+ grammar[token] = rest[token];
718+ }
719+
720+ delete grammar.rest;
721+ }
722+
723+ var tokenList = new LinkedList();
724+ addAfter(tokenList, tokenList.head, text);
725+
726+ matchGrammar(text, tokenList, grammar, tokenList.head, 0);
727+
728+ return toArray(tokenList);
729+ },
730+
731+ /**
732+ * @namespace
733+ * @memberof Prism
734+ * @public
735+ */
736+ hooks: {
737+ all: {},
738+
739+ /**
740+ * Adds the given callback to the list of callbacks for the given hook.
741+ *
742+ * The callback will be invoked when the hook it is registered for is run.
743+ * Hooks are usually directly run by a highlight function but you can also run hooks yourself.
744+ *
745+ * One callback function can be registered to multiple hooks and the same hook multiple times.
746+ *
747+ * @param {string} name The name of the hook.
748+ * @param {HookCallback} callback The callback function which is given environment variables.
749+ * @public
750+ */
751+ add: function (name, callback) {
752+ var hooks = _.hooks.all;
753+
754+ hooks[name] = hooks[name] || [];
755+
756+ hooks[name].push(callback);
757+ },
758+
759+ /**
760+ * Runs a hook invoking all registered callbacks with the given environment variables.
761+ *
762+ * Callbacks will be invoked synchronously and in the order in which they were registered.
763+ *
764+ * @param {string} name The name of the hook.
765+ * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered.
766+ * @public
767+ */
768+ run: function (name, env) {
769+ var callbacks = _.hooks.all[name];
770+
771+ if (!callbacks || !callbacks.length) {
772+ return;
773+ }
774+
775+ for (var i = 0, callback; (callback = callbacks[i++]); ) {
776+ callback(env);
777+ }
778+ },
779+ },
780+
781+ Token: Token,
782+ };
783+ _self.Prism = _;
784+
785+ // Typescript note:
786+ // The following can be used to import the Token type in JSDoc:
787+ //
788+ // @typedef {InstanceType<import("./prism-core")["Token"]>} Token
789+
790+ /**
791+ * Creates a new token.
792+ *
793+ * @param {string} type See {@link Token#type type}
794+ * @param {string | TokenStream} content See {@link Token#content content}
795+ * @param {string|string[]} [alias] The alias(es) of the token.
796+ * @param {string} [matchedStr=""] A copy of the full string this token was created from.
797+ * @class
798+ * @global
799+ * @public
800+ */
801+ function Token(type, content, alias, matchedStr) {
802+ /**
803+ * The type of the token.
804+ *
805+ * This is usually the key of a pattern in a {@link Grammar}.
806+ *
807+ * @type {string}
808+ * @see GrammarToken
809+ * @public
810+ */
811+ this.type = type;
812+ /**
813+ * The strings or tokens contained by this token.
814+ *
815+ * This will be a token stream if the pattern matched also defined an `inside` grammar.
816+ *
817+ * @type {string | TokenStream}
818+ * @public
819+ */
820+ this.content = content;
821+ /**
822+ * The alias(es) of the token.
823+ *
824+ * @type {string|string[]}
825+ * @see GrammarToken
826+ * @public
827+ */
828+ this.alias = alias;
829+ // Copy of the full string this token was created from
830+ this.length = (matchedStr || "").length | 0;
831+ }
832+
833+ /**
834+ * A token stream is an array of strings and {@link Token Token} objects.
835+ *
836+ * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) that process
837+ * them.
838+ *
839+ * 1. No adjacent strings.
840+ * 2. No empty strings.
841+ *
842+ * The only exception here is the token stream that only contains the empty string and nothing else.
843+ *
844+ * @typedef {Array<string | Token>} TokenStream
845+ * @global
846+ * @public
847+ */
848+
849+ /**
850+ * Converts the given token or token stream to an HTML representation.
851+ *
852+ * The following hooks will be run:
853+ * 1. `wrap`: On each {@link Token}.
854+ *
855+ * @param {string | Token | TokenStream} o The token or token stream to be converted.
856+ * @param {string} language The name of current language.
857+ * @returns {string} The HTML representation of the token or token stream.
858+ * @memberof Token
859+ * @static
860+ */
861+ Token.stringify = function stringify(o, language) {
862+ if (typeof o == "string") {
863+ return o;
864+ }
865+ if (Array.isArray(o)) {
866+ var s = "";
867+ o.forEach(function (e) {
868+ s += stringify(e, language);
869+ });
870+ return s;
871+ }
872+
873+ var env = {
874+ type: o.type,
875+ content: stringify(o.content, language),
876+ tag: "span",
877+ classes: ["token", o.type],
878+ attributes: {},
879+ language: language,
880+ };
881+
882+ var aliases = o.alias;
883+ if (aliases) {
884+ if (Array.isArray(aliases)) {
885+ Array.prototype.push.apply(env.classes, aliases);
886+ } else {
887+ env.classes.push(aliases);
888+ }
889+ }
890+
891+ _.hooks.run("wrap", env);
892+
893+ var attributes = "";
894+ for (var name in env.attributes) {
895+ attributes +=
896+ " " +
897+ name +
898+ '="' +
899+ (env.attributes[name] || "").replace(/"/g, "&quot;") +
900+ '"';
901+ }
902+
903+ return (
904+ "<" +
905+ env.tag +
906+ ' class="' +
907+ env.classes.join(" ") +
908+ '"' +
909+ attributes +
910+ ">" +
911+ env.content +
912+ "</" +
913+ env.tag +
914+ ">"
915+ );
916+ };
917+
918+ /**
919+ * @param {RegExp} pattern
920+ * @param {number} pos
921+ * @param {string} text
922+ * @param {boolean} lookbehind
923+ * @returns {RegExpExecArray | null}
924+ */
925+ function matchPattern(pattern, pos, text, lookbehind) {
926+ pattern.lastIndex = pos;
927+ var match = pattern.exec(text);
928+ if (match && lookbehind && match[1]) {
929+ // change the match to remove the text matched by the Prism lookbehind group
930+ var lookbehindLength = match[1].length;
931+ match.index += lookbehindLength;
932+ match[0] = match[0].slice(lookbehindLength);
933+ }
934+ return match;
935+ }
936+
937+ /**
938+ * @param {string} text
939+ * @param {LinkedList<string | Token>} tokenList
940+ * @param {any} grammar
941+ * @param {LinkedListNode<string | Token>} startNode
942+ * @param {number} startPos
943+ * @param {RematchOptions} [rematch]
944+ * @returns {void}
945+ * @private
946+ *
947+ * @typedef RematchOptions
948+ * @property {string} cause
949+ * @property {number} reach
950+ */
951+ function matchGrammar(
952+ text,
953+ tokenList,
954+ grammar,
955+ startNode,
956+ startPos,
957+ rematch,
958+ ) {
959+ for (var token in grammar) {
960+ if (!grammar.hasOwnProperty(token) || !grammar[token]) {
961+ continue;
962+ }
963+
964+ var patterns = grammar[token];
965+ patterns = Array.isArray(patterns) ? patterns : [patterns];
966+
967+ for (var j = 0; j < patterns.length; ++j) {
968+ if (rematch && rematch.cause == token + "," + j) {
969+ return;
970+ }
971+
972+ var patternObj = patterns[j];
973+ var inside = patternObj.inside;
974+ var lookbehind = !!patternObj.lookbehind;
975+ var greedy = !!patternObj.greedy;
976+ var alias = patternObj.alias;
977+
978+ if (greedy && !patternObj.pattern.global) {
979+ // Without the global flag, lastIndex won't work
980+ var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];
981+ patternObj.pattern = RegExp(patternObj.pattern.source, flags + "g");
982+ }
983+
984+ /** @type {RegExp} */
985+ var pattern = patternObj.pattern || patternObj;
986+
987+ for (
988+ // iterate the token list and keep track of the current token/string position
989+ var currentNode = startNode.next, pos = startPos;
990+ currentNode !== tokenList.tail;
991+ pos += currentNode.value.length, currentNode = currentNode.next
992+ ) {
993+ if (rematch && pos >= rematch.reach) {
994+ break;
995+ }
996+
997+ var str = currentNode.value;
998+
999+ if (tokenList.length > text.length) {
1000+ // Something went terribly wrong, ABORT, ABORT!
1001+ return;
1002+ }
1003+
1004+ if (str instanceof Token) {
1005+ continue;
1006+ }
1007+
1008+ var removeCount = 1; // this is the to parameter of removeBetween
1009+ var match;
1010+
1011+ if (greedy) {
1012+ match = matchPattern(pattern, pos, text, lookbehind);
1013+ if (!match || match.index >= text.length) {
1014+ break;
1015+ }
1016+
1017+ var from = match.index;
1018+ var to = match.index + match[0].length;
1019+ var p = pos;
1020+
1021+ // find the node that contains the match
1022+ p += currentNode.value.length;
1023+ while (from >= p) {
1024+ currentNode = currentNode.next;
1025+ p += currentNode.value.length;
1026+ }
1027+ // adjust pos (and p)
1028+ p -= currentNode.value.length;
1029+ pos = p;
1030+
1031+ // the current node is a Token, then the match starts inside another Token, which is invalid
1032+ if (currentNode.value instanceof Token) {
1033+ continue;
1034+ }
1035+
1036+ // find the last node which is affected by this match
1037+ for (
1038+ var k = currentNode;
1039+ k !== tokenList.tail && (p < to || typeof k.value === "string");
1040+ k = k.next
1041+ ) {
1042+ removeCount++;
1043+ p += k.value.length;
1044+ }
1045+ removeCount--;
1046+
1047+ // replace with the new match
1048+ str = text.slice(pos, p);
1049+ match.index -= pos;
1050+ } else {
1051+ match = matchPattern(pattern, 0, str, lookbehind);
1052+ if (!match) {
1053+ continue;
1054+ }
1055+ }
1056+
1057+ // eslint-disable-next-line no-redeclare
1058+ var from = match.index;
1059+ var matchStr = match[0];
1060+ var before = str.slice(0, from);
1061+ var after = str.slice(from + matchStr.length);
1062+
1063+ var reach = pos + str.length;
1064+ if (rematch && reach > rematch.reach) {
1065+ rematch.reach = reach;
1066+ }
1067+
1068+ var removeFrom = currentNode.prev;
1069+
1070+ if (before) {
1071+ removeFrom = addAfter(tokenList, removeFrom, before);
1072+ pos += before.length;
1073+ }
1074+
1075+ removeRange(tokenList, removeFrom, removeCount);
1076+
1077+ var wrapped = new Token(
1078+ token,
1079+ inside ? _.tokenize(matchStr, inside) : matchStr,
1080+ alias,
1081+ matchStr,
1082+ );
1083+ currentNode = addAfter(tokenList, removeFrom, wrapped);
1084+
1085+ if (after) {
1086+ addAfter(tokenList, currentNode, after);
1087+ }
1088+
1089+ if (removeCount > 1) {
1090+ // at least one Token object was removed, so we have to do some rematching
1091+ // this can only happen if the current pattern is greedy
1092+
1093+ /** @type {RematchOptions} */
1094+ var nestedRematch = {
1095+ cause: token + "," + j,
1096+ reach: reach,
1097+ };
1098+ matchGrammar(
1099+ text,
1100+ tokenList,
1101+ grammar,
1102+ currentNode.prev,
1103+ pos,
1104+ nestedRematch,
1105+ );
1106+
1107+ // the reach might have been extended because of the rematching
1108+ if (rematch && nestedRematch.reach > rematch.reach) {
1109+ rematch.reach = nestedRematch.reach;
1110+ }
1111+ }
1112+ }
1113+ }
1114+ }
1115+ }
1116+
1117+ /**
1118+ * @typedef LinkedListNode
1119+ * @property {T} value
1120+ * @property {LinkedListNode<T> | null} prev The previous node.
1121+ * @property {LinkedListNode<T> | null} next The next node.
1122+ * @template T
1123+ * @private
1124+ */
1125+
1126+ /**
1127+ * @template T
1128+ * @private
1129+ */
1130+ function LinkedList() {
1131+ /** @type {LinkedListNode<T>} */
1132+ var head = { value: null, prev: null, next: null };
1133+ /** @type {LinkedListNode<T>} */
1134+ var tail = { value: null, prev: head, next: null };
1135+ head.next = tail;
1136+
1137+ /** @type {LinkedListNode<T>} */
1138+ this.head = head;
1139+ /** @type {LinkedListNode<T>} */
1140+ this.tail = tail;
1141+ this.length = 0;
1142+ }
1143+
1144+ /**
1145+ * Adds a new node with the given value to the list.
1146+ *
1147+ * @param {LinkedList<T>} list
1148+ * @param {LinkedListNode<T>} node
1149+ * @param {T} value
1150+ * @returns {LinkedListNode<T>} The added node.
1151+ * @template T
1152+ */
1153+ function addAfter(list, node, value) {
1154+ // assumes that node != list.tail && values.length >= 0
1155+ var next = node.next;
1156+
1157+ var newNode = { value: value, prev: node, next: next };
1158+ node.next = newNode;
1159+ next.prev = newNode;
1160+ list.length++;
1161+
1162+ return newNode;
1163+ }
1164+ /**
1165+ * Removes `count` nodes after the given node. The given node will not be removed.
1166+ *
1167+ * @param {LinkedList<T>} list
1168+ * @param {LinkedListNode<T>} node
1169+ * @param {number} count
1170+ * @template T
1171+ */
1172+ function removeRange(list, node, count) {
1173+ var next = node.next;
1174+ for (var i = 0; i < count && next !== list.tail; i++) {
1175+ next = next.next;
1176+ }
1177+ node.next = next;
1178+ next.prev = node;
1179+ list.length -= i;
1180+ }
1181+ /**
1182+ * @param {LinkedList<T>} list
1183+ * @returns {T[]}
1184+ * @template T
1185+ */
1186+ function toArray(list) {
1187+ var array = [];
1188+ var node = list.head.next;
1189+ while (node !== list.tail) {
1190+ array.push(node.value);
1191+ node = node.next;
1192+ }
1193+ return array;
1194+ }
1195+
1196+ if (!_self.document) {
1197+ if (!_self.addEventListener) {
1198+ // in Node.js
1199+ return _;
1200+ }
1201+
1202+ if (!_.disableWorkerMessageHandler) {
1203+ // In worker
1204+ _self.addEventListener(
1205+ "message",
1206+ function (evt) {
1207+ var message = JSON.parse(evt.data);
1208+ var lang = message.language;
1209+ var code = message.code;
1210+ var immediateClose = message.immediateClose;
1211+
1212+ _self.postMessage(_.highlight(code, _.languages[lang], lang));
1213+ if (immediateClose) {
1214+ _self.close();
1215+ }
1216+ },
1217+ false,
1218+ );
1219+ }
1220+
1221+ return _;
1222+ }
1223+
1224+ // Get current script and highlight
1225+ var script = _.util.currentScript();
1226+
1227+ if (script) {
1228+ _.filename = script.src;
1229+
1230+ if (script.hasAttribute("data-manual")) {
1231+ _.manual = true;
1232+ }
1233+ }
1234+
1235+ function highlightAutomaticallyCallback() {
1236+ if (!_.manual) {
1237+ _.highlightAll();
1238+ }
1239+ }
1240+
1241+ if (!_.manual) {
1242+ // If the document state is "loading", then we'll use DOMContentLoaded.
1243+ // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the
1244+ // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they
1245+ // might take longer one animation frame to execute which can create a race condition where only some plugins have
1246+ // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded.
1247+ // See https://github.com/PrismJS/prism/issues/2102
1248+ var readyState = document.readyState;
1249+ if (
1250+ readyState === "loading" ||
1251+ (readyState === "interactive" && script && script.defer)
1252+ ) {
1253+ document.addEventListener(
1254+ "DOMContentLoaded",
1255+ highlightAutomaticallyCallback,
1256+ );
1257+ } else {
1258+ if (window.requestAnimationFrame) {
1259+ window.requestAnimationFrame(highlightAutomaticallyCallback);
1260+ } else {
1261+ window.setTimeout(highlightAutomaticallyCallback, 16);
1262+ }
1263+ }
1264+ }
1265+
1266+ return _;
1267+})(_self);
1268+
1269+if (typeof module !== "undefined" && module.exports) {
1270+ module.exports = Prism;
12111271 }
12121272
12131273 // hack for components to work correctly in node.js
1214−if (typeof global !== 'undefined') {
1215− global.Prism = Prism;
1274+if (typeof global !== "undefined") {
1275+ global.Prism = Prism;
12161276 }
12171277
12181278 // some additional documentation/types
@@ -1263,1007 +1323,1096 @@ if (typeof global !== 'undefined') { · 44 unmodified lines
12631323 * @global
12641324 * @public
12651325 */
1266−;
12671326 Prism.languages.markup = {
1268− 'comment': {
1269− pattern: /<!--(?:(?!<!--)[\s\S])*?-->/,
1270− greedy: true
1271− },
1272− 'prolog': {
1273− pattern: /<\?[\s\S]+?\?>/,
1274− greedy: true
1275− },
1276− 'doctype': {
1277− // https://www.w3.org/TR/xml/#NT-doctypedecl
1278− pattern: /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,
1279− greedy: true,
1280− inside: {
1281− 'internal-subset': {
1282− pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/,
1283− lookbehind: true,
1284− greedy: true,
1285− inside: null // see below
1286− },
1287− 'string': {
1288− pattern: /"[^"]*"|'[^']*'/,
1289− greedy: true
1290− },
1291− 'punctuation': /^<!|>$|[[\]]/,
1292− 'doctype-tag': /^DOCTYPE/i,
1293− 'name': /[^\s<>'"]+/
1294− }
1295− },
1296− 'cdata': {
1297− pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
1298− greedy: true
1299− },
1300− 'tag': {
1301− pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,
1302− greedy: true,
1303− inside: {
1304− 'tag': {
1305− pattern: /^<\/?[^\s>\/]+/,
1306− inside: {
1307− 'punctuation': /^<\/?/,
1308− 'namespace': /^[^\s>\/:]+:/
1309− }
1310− },
1311− 'special-attr': [],
1312− 'attr-value': {
1313− pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
1314− inside: {
1315− 'punctuation': [
1316− {
1317− pattern: /^=/,
1318− alias: 'attr-equals'
1319− },
1320− {
1321− pattern: /^(\s*)["']|["']$/,
1322− lookbehind: true
1323− }
1324− ]
1325− }
1326− },
1327− 'punctuation': /\/?>/,
1328− 'attr-name': {
1329− pattern: /[^\s>\/]+/,
1330− inside: {
1331− 'namespace': /^[^\s>\/:]+:/
1332− }
1333− }
1334−
1335− }
1336− },
1337− 'entity': [
1338− {
1339− pattern: /&[\da-z]{1,8};/i,
1340− alias: 'named-entity'
1341− },
1342− /&#x?[\da-f]{1,8};/i
1343− ]
1327+ comment: {
1328+ pattern: /<!--(?:(?!<!--)[\s\S])*?-->/,
1329+ greedy: true,
1330+ },
1331+ prolog: {
1332+ pattern: /<\?[\s\S]+?\?>/,
1333+ greedy: true,
1334+ },
1335+ doctype: {
1336+ // https://www.w3.org/TR/xml/#NT-doctypedecl
1337+ pattern:
1338+ /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,
1339+ greedy: true,
1340+ inside: {
1341+ "internal-subset": {
1342+ pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/,
1343+ lookbehind: true,
1344+ greedy: true,
1345+ inside: null, // see below
1346+ },
1347+ string: {
1348+ pattern: /"[^"]*"|'[^']*'/,
1349+ greedy: true,
1350+ },
1351+ punctuation: /^<!|>$|[[\]]/,
1352+ "doctype-tag": /^DOCTYPE/i,
1353+ name: /[^\s<>'"]+/,
1354+ },
1355+ },
1356+ cdata: {
1357+ pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
1358+ greedy: true,
1359+ },
1360+ tag: {
1361+ pattern:
1362+ /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,
1363+ greedy: true,
1364+ inside: {
1365+ tag: {
1366+ pattern: /^<\/?[^\s>\/]+/,
1367+ inside: {
1368+ punctuation: /^<\/?/,
1369+ namespace: /^[^\s>\/:]+:/,
1370+ },
1371+ },
1372+ "special-attr": [],
1373+ "attr-value": {
1374+ pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
1375+ inside: {
1376+ punctuation: [
1377+ {
1378+ pattern: /^=/,
1379+ alias: "attr-equals",
1380+ },
1381+ {
1382+ pattern: /^(\s*)["']|["']$/,
1383+ lookbehind: true,
1384+ },
1385+ ],
1386+ },
1387+ },
1388+ punctuation: /\/?>/,
1389+ "attr-name": {
1390+ pattern: /[^\s>\/]+/,
1391+ inside: {
1392+ namespace: /^[^\s>\/:]+:/,
1393+ },
1394+ },
1395+ },
1396+ },
1397+ entity: [
1398+ {
1399+ pattern: /&[\da-z]{1,8};/i,
1400+ alias: "named-entity",
1401+ },
1402+ /&#x?[\da-f]{1,8};/i,
1403+ ],
13441404 };
13451405
1346−Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] =
1347− Prism.languages.markup['entity'];
1348−Prism.languages.markup['doctype'].inside['internal-subset'].inside = Prism.languages.markup;
1406+Prism.languages.markup["tag"].inside["attr-value"].inside["entity"] =
1407+ Prism.languages.markup["entity"];
1408+Prism.languages.markup["doctype"].inside["internal-subset"].inside =
1409+ Prism.languages.markup;
13491410
13501411 // Plugin to make entity title show the real entity, idea by Roman Komarov
1351−Prism.hooks.add('wrap', function (env) {
1352−
1353− if (env.type === 'entity') {
1354− env.attributes['title'] = env.content.replace(/&amp;/, '&');
1355− }
1412+Prism.hooks.add("wrap", function (env) {
1413+ if (env.type === "entity") {
1414+ env.attributes["title"] = env.content.replace(/&amp;/, "&");
1415+ }
13561416 });
13571417
1358−Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
1359− /**
1360− * Adds an inlined language to markup.
1361− *
1362− * An example of an inlined language is CSS with `<style>` tags.
1363− *
1364− * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as
1365− * case insensitive.
1366− * @param {string} lang The language key.
1367− * @example
1368− * addInlined('style', 'css');
1369− */
1370− value: function addInlined(tagName, lang) {
1371− var includedCdataInside = {};
1372− includedCdataInside['language-' + lang] = {
1373− pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
1374− lookbehind: true,
1375− inside: Prism.languages[lang]
1376− };
1377− includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i;
1378−
1379− var inside = {
1380− 'included-cdata': {
1381− pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
1382− inside: includedCdataInside
1383− }
1384− };
1385− inside['language-' + lang] = {
1386− pattern: /[\s\S]+/,
1387− inside: Prism.languages[lang]
1388− };
1389−
1390− var def = {};
1391− def[tagName] = {
1392− pattern: RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g, function () { return tagName; }), 'i'),
1393− lookbehind: true,
1394− greedy: true,
1395− inside: inside
1396− };
1397−
1398− Prism.languages.insertBefore('markup', 'cdata', def);
1399− }
1418+Object.defineProperty(Prism.languages.markup.tag, "addInlined", {
1419+ /**
1420+ * Adds an inlined language to markup.
1421+ *
1422+ * An example of an inlined language is CSS with `<style>` tags.
1423+ *
1424+ * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as
1425+ * case insensitive.
1426+ * @param {string} lang The language key.
1427+ * @example
1428+ * addInlined('style', 'css');
1429+ */
1430+ value: function addInlined(tagName, lang) {
1431+ var includedCdataInside = {};
1432+ includedCdataInside["language-" + lang] = {
1433+ pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
1434+ lookbehind: true,
1435+ inside: Prism.languages[lang],
1436+ };
1437+ includedCdataInside["cdata"] = /^<!\[CDATA\[|\]\]>$/i;
1438+
1439+ var inside = {
1440+ "included-cdata": {
1441+ pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
1442+ inside: includedCdataInside,
1443+ },
1444+ };
1445+ inside["language-" + lang] = {
1446+ pattern: /[\s\S]+/,
1447+ inside: Prism.languages[lang],
1448+ };
1449+
1450+ var def = {};
1451+ def[tagName] = {
1452+ pattern: RegExp(
1453+ /(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(
1454+ /__/g,
1455+ function () {
1456+ return tagName;
1457+ },
1458+ ),
1459+ "i",
1460+ ),
1461+ lookbehind: true,
1462+ greedy: true,
1463+ inside: inside,
1464+ };
1465+
1466+ Prism.languages.insertBefore("markup", "cdata", def);
1467+ },
14001468 });
1401−Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', {
1402− /**
1403− * Adds an pattern to highlight languages embedded in HTML attributes.
1404− *
1405− * An example of an inlined language is CSS with `style` attributes.
1406− *
1407− * @param {string} attrName The name of the tag that contains the inlined language. This name will be treated as
1408− * case insensitive.
1409− * @param {string} lang The language key.
1410− * @example
1411− * addAttribute('style', 'css');
1412− */
1413− value: function (attrName, lang) {
1414− Prism.languages.markup.tag.inside['special-attr'].push({
1415− pattern: RegExp(
1416− /(^|["'\s])/.source + '(?:' + attrName + ')' + /\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,
1417− 'i'
1418− ),
1419− lookbehind: true,
1420− inside: {
1421− 'attr-name': /^[^\s=]+/,
1422− 'attr-value': {
1423− pattern: /=[\s\S]+/,
1424− inside: {
1425− 'value': {
1426− pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,
1427− lookbehind: true,
1428− alias: [lang, 'language-' + lang],
1429− inside: Prism.languages[lang]
1430− },
1431− 'punctuation': [
1432− {
1433− pattern: /^=/,
1434− alias: 'attr-equals'
1435− },
1436− /"|'/
1437− ]
1438− }
1439− }
1440− }
1441− });
1442− }
1469+Object.defineProperty(Prism.languages.markup.tag, "addAttribute", {
1470+ /**
1471+ * Adds an pattern to highlight languages embedded in HTML attributes.
1472+ *
1473+ * An example of an inlined language is CSS with `style` attributes.
1474+ *
1475+ * @param {string} attrName The name of the tag that contains the inlined language. This name will be treated as
1476+ * case insensitive.
1477+ * @param {string} lang The language key.
1478+ * @example
1479+ * addAttribute('style', 'css');
1480+ */
1481+ value: function (attrName, lang) {
1482+ Prism.languages.markup.tag.inside["special-attr"].push({
1483+ pattern: RegExp(
1484+ /(^|["'\s])/.source +
1485+ "(?:" +
1486+ attrName +
1487+ ")" +
1488+ /\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,
1489+ "i",
1490+ ),
1491+ lookbehind: true,
1492+ inside: {
1493+ "attr-name": /^[^\s=]+/,
1494+ "attr-value": {
1495+ pattern: /=[\s\S]+/,
1496+ inside: {
1497+ value: {
1498+ pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,
1499+ lookbehind: true,
1500+ alias: [lang, "language-" + lang],
1501+ inside: Prism.languages[lang],
1502+ },
1503+ punctuation: [
1504+ {
1505+ pattern: /^=/,
1506+ alias: "attr-equals",
1507+ },
1508+ /"|'/,
1509+ ],
1510+ },
1511+ },
1512+ },
1513+ });
1514+ },
14431515 });
14441516
14451517 Prism.languages.html = Prism.languages.markup;
14461518 Prism.languages.mathml = Prism.languages.markup;
14471519 Prism.languages.svg = Prism.languages.markup;
14481520
1449−Prism.languages.xml = Prism.languages.extend('markup', {});
1521+Prism.languages.xml = Prism.languages.extend("markup", {});
14501522 Prism.languages.ssml = Prism.languages.xml;
14511523 Prism.languages.atom = Prism.languages.xml;
14521524 Prism.languages.rss = Prism.languages.xml;
14531525
14541526 (function (Prism) {
1455−
1456− var string = /(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;
1457−
1458− Prism.languages.css = {
1459− 'comment': /\/\*[\s\S]*?\*\//,
1460− 'atrule': {
1461− pattern: RegExp('@[\\w-](?:' + /[^;{\s"']|\s+(?!\s)/.source + '|' + string.source + ')*?' + /(?:;|(?=\s*\{))/.source),
1462− inside: {
1463− 'rule': /^@[\w-]+/,
1464− 'selector-function-argument': {
1465− pattern: /(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,
1466− lookbehind: true,
1467− alias: 'selector'
1468− },
1469− 'keyword': {
1470− pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/,
1471− lookbehind: true
1472− }
1473− // See rest below
1474− }
1475− },
1476− 'url': {
1477− // https://drafts.csswg.org/css-values-3/#urls
1478− pattern: RegExp('\\burl\\((?:' + string.source + '|' + /(?:[^\\\r\n()"']|\\[\s\S])*/.source + ')\\)', 'i'),
1479− greedy: true,
1480− inside: {
1481− 'function': /^url/i,
1482− 'punctuation': /^\(|\)$/,
1483− 'string': {
1484− pattern: RegExp('^' + string.source + '$'),
1485− alias: 'url'
1486− }
1487− }
1488− },
1489− 'selector': {
1490− pattern: RegExp('(^|[{}\\s])[^{}\\s](?:[^{};"\'\\s]|\\s+(?![\\s{])|' + string.source + ')*(?=\\s*\\{)'),
1491− lookbehind: true
1492− },
1493− 'string': {
1494− pattern: string,
1495− greedy: true
1496− },
1497− 'property': {
1498− pattern: /(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,
1499− lookbehind: true
1500− },
1501− 'important': /!important\b/i,
1502− 'function': {
1503− pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,
1504− lookbehind: true
1505− },
1506− 'punctuation': /[(){};:,]/
1507− };
1508−
1509− Prism.languages.css['atrule'].inside.rest = Prism.languages.css;
1510−
1511− var markup = Prism.languages.markup;
1512− if (markup) {
1513− markup.tag.addInlined('style', 'css');
1514− markup.tag.addAttribute('style', 'css');
1515− }
1516−
1517−}(Prism));
1527+ var string =
1528+ /(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;
1529+
1530+ Prism.languages.css = {
1531+ comment: /\/\*[\s\S]*?\*\//,
1532+ atrule: {
1533+ pattern: RegExp(
1534+ "@[\\w-](?:" +
1535+ /[^;{\s"']|\s+(?!\s)/.source +
1536+ "|" +
1537+ string.source +
1538+ ")*?" +
1539+ /(?:;|(?=\s*\{))/.source,
1540+ ),
1541+ inside: {
1542+ rule: /^@[\w-]+/,
1543+ "selector-function-argument": {
1544+ pattern:
1545+ /(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,
1546+ lookbehind: true,
1547+ alias: "selector",
1548+ },
1549+ keyword: {
1550+ pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/,
1551+ lookbehind: true,
1552+ },
1553+ // See rest below
1554+ },
1555+ },
1556+ url: {
1557+ // https://drafts.csswg.org/css-values-3/#urls
1558+ pattern: RegExp(
1559+ "\\burl\\((?:" +
1560+ string.source +
1561+ "|" +
1562+ /(?:[^\\\r\n()"']|\\[\s\S])*/.source +
1563+ ")\\)",
1564+ "i",
1565+ ),
1566+ greedy: true,
1567+ inside: {
1568+ function: /^url/i,
1569+ punctuation: /^\(|\)$/,
1570+ string: {
1571+ pattern: RegExp("^" + string.source + "$"),
1572+ alias: "url",
1573+ },
1574+ },
1575+ },
1576+ selector: {
1577+ pattern: RegExp(
1578+ "(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|" +
1579+ string.source +
1580+ ")*(?=\\s*\\{)",
1581+ ),
1582+ lookbehind: true,
1583+ },
1584+ string: {
1585+ pattern: string,
1586+ greedy: true,
1587+ },
1588+ property: {
1589+ pattern:
1590+ /(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,
1591+ lookbehind: true,
1592+ },
1593+ important: /!important\b/i,
1594+ function: {
1595+ pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,
1596+ lookbehind: true,
1597+ },
1598+ punctuation: /[(){};:,]/,
1599+ };
1600+
1601+ Prism.languages.css["atrule"].inside.rest = Prism.languages.css;
1602+
1603+ var markup = Prism.languages.markup;
1604+ if (markup) {
1605+ markup.tag.addInlined("style", "css");
1606+ markup.tag.addAttribute("style", "css");
1607+ }
1608+})(Prism);
15181609
15191610 Prism.languages.clike = {
1520− 'comment': [
1521− {
1522− pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
1523− lookbehind: true,
1524− greedy: true
1525− },
1526− {
1527− pattern: /(^|[^\\:])\/\/.*/,
1528− lookbehind: true,
1529− greedy: true
1530− }
1531− ],
1532− 'string': {
1533− pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
1534− greedy: true
1535− },
1536− 'class-name': {
1537− pattern: /(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,
1538− lookbehind: true,
1539− inside: {
1540− 'punctuation': /[.\\]/
1541− }
1542− },
1543− 'keyword': /\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,
1544− 'boolean': /\b(?:false|true)\b/,
1545− 'function': /\b\w+(?=\()/,
1546− 'number': /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,
1547− 'operator': /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,
1548− 'punctuation': /[{}[\];(),.:]/
1611+ comment: [
1612+ {
1613+ pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
1614+ lookbehind: true,
1615+ greedy: true,
1616+ },
1617+ {
1618+ pattern: /(^|[^\\:])\/\/.*/,
1619+ lookbehind: true,
1620+ greedy: true,
1621+ },
1622+ ],
1623+ string: {
1624+ pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
1625+ greedy: true,
1626+ },
1627+ "class-name": {
1628+ pattern:
1629+ /(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,
1630+ lookbehind: true,
1631+ inside: {
1632+ punctuation: /[.\\]/,
1633+ },
1634+ },
1635+ keyword:
1636+ /\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,
1637+ boolean: /\b(?:false|true)\b/,
1638+ function: /\b\w+(?=\()/,
1639+ number: /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,
1640+ operator: /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,
1641+ punctuation: /[{}[\];(),.:]/,
15491642 };
15501643
1551−Prism.languages.javascript = Prism.languages.extend('clike', {
1552− 'class-name': [
1553− Prism.languages.clike['class-name'],
1554− {
1555− pattern: /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,
1556− lookbehind: true
1557− }
1558− ],
1559− 'keyword': [
1560− {
1561− pattern: /((?:^|\})\s*)catch\b/,
1562− lookbehind: true
1563− },
1564− {
1565− pattern: /(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
1566− lookbehind: true
1567− },
1568− ],
1569− // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
1570− 'function': /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
1571− 'number': {
1572− pattern: RegExp(
1573− /(^|[^\w$])/.source +
1574− '(?:' +
1575− (
1576− // constant
1577− /NaN|Infinity/.source +
1578− '|' +
1579− // binary integer
1580− /0[bB][01]+(?:_[01]+)*n?/.source +
1581− '|' +
1582− // octal integer
1583− /0[oO][0-7]+(?:_[0-7]+)*n?/.source +
1584− '|' +
1585− // hexadecimal integer
1586− /0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source +
1587− '|' +
1588− // decimal bigint
1589− /\d+(?:_\d+)*n/.source +
1590− '|' +
1591− // decimal number (integer or float) but no bigint
1592− /(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source
1593− ) +
1594− ')' +
1595− /(?![\w$])/.source
1596− ),
1597− lookbehind: true
1598− },
1599− 'operator': /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/
1644+Prism.languages.javascript = Prism.languages.extend("clike", {
1645+ "class-name": [
1646+ Prism.languages.clike["class-name"],
1647+ {
1648+ pattern:
1649+ /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,
1650+ lookbehind: true,
1651+ },
1652+ ],
1653+ keyword: [
1654+ {
1655+ pattern: /((?:^|\})\s*)catch\b/,
1656+ lookbehind: true,
1657+ },
1658+ {
1659+ pattern:
1660+ /(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
1661+ lookbehind: true,
1662+ },
1663+ ],
1664+ // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
1665+ function:
1666+ /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
1667+ number: {
1668+ pattern: RegExp(
1669+ /(^|[^\w$])/.source +
1670+ "(?:" +
1671+ // constant
1672+ (/NaN|Infinity/.source +
1673+ "|" +
1674+ // binary integer
1675+ /0[bB][01]+(?:_[01]+)*n?/.source +
1676+ "|" +
1677+ // octal integer
1678+ /0[oO][0-7]+(?:_[0-7]+)*n?/.source +
1679+ "|" +
1680+ // hexadecimal integer
1681+ /0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source +
1682+ "|" +
1683+ // decimal bigint
1684+ /\d+(?:_\d+)*n/.source +
1685+ "|" +
1686+ // decimal number (integer or float) but no bigint
1687+ /(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/
1688+ .source) +
1689+ ")" +
1690+ /(?![\w$])/.source,
1691+ ),
1692+ lookbehind: true,
1693+ },
1694+ operator:
1695+ /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/,
16001696 });
16011697
1602−Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/;
1603−
1604−Prism.languages.insertBefore('javascript', 'keyword', {
1605− 'regex': {
1606− pattern: RegExp(
1607− // lookbehind
1608− // eslint-disable-next-line regexp/no-dupe-characters-character-class
1609− /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source +
1610− // Regex pattern:
1611− // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character
1612− // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible
1613− // with the only syntax, so we have to define 2 different regex patterns.
1614− /\//.source +
1615− '(?:' +
1616− /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source +
1617− '|' +
1618− // `v` flag syntax. This supports 3 levels of nested character classes.
1619− /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source +
1620− ')' +
1621− // lookahead
1622− /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source
1623− ),
1624− lookbehind: true,
1625− greedy: true,
1626− inside: {
1627− 'regex-source': {
1628− pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/,
1629− lookbehind: true,
1630− alias: 'language-regex',
1631− inside: Prism.languages.regex
1632− },
1633− 'regex-delimiter': /^\/|\/$/,
1634− 'regex-flags': /^[a-z]+$/,
1635− }
1636− },
1637− // This must be declared before keyword because we use "function" inside the look-forward
1638− 'function-variable': {
1639− pattern: /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,
1640− alias: 'function'
1641− },
1642− 'parameter': [
1643− {
1644− pattern: /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,
1645− lookbehind: true,
1646− inside: Prism.languages.javascript
1647− },
1648− {
1649− pattern: /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,
1650− lookbehind: true,
1651− inside: Prism.languages.javascript
1652− },
1653− {
1654− pattern: /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,
1655− lookbehind: true,
1656− inside: Prism.languages.javascript
1657− },
1658− {
1659− pattern: /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,
1660− lookbehind: true,
1661− inside: Prism.languages.javascript
1662− }
1663− ],
1664− 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/
1698+Prism.languages.javascript["class-name"][0].pattern =
1699+ /(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/;
1700+
1701+Prism.languages.insertBefore("javascript", "keyword", {
1702+ regex: {
1703+ pattern: RegExp(
1704+ // lookbehind
1705+ // eslint-disable-next-line regexp/no-dupe-characters-character-class
1706+ /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source +
1707+ // Regex pattern:
1708+ // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character
1709+ // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible
1710+ // with the only syntax, so we have to define 2 different regex patterns.
1711+ /\//.source +
1712+ "(?:" +
1713+ /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/
1714+ .source +
1715+ "|" +
1716+ // `v` flag syntax. This supports 3 levels of nested character classes.
1717+ /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/
1718+ .source +
1719+ ")" +
1720+ // lookahead
1721+ /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/
1722+ .source,
1723+ ),
1724+ lookbehind: true,
1725+ greedy: true,
1726+ inside: {
1727+ "regex-source": {
1728+ pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/,
1729+ lookbehind: true,
1730+ alias: "language-regex",
1731+ inside: Prism.languages.regex,
1732+ },
1733+ "regex-delimiter": /^\/|\/$/,
1734+ "regex-flags": /^[a-z]+$/,
1735+ },
1736+ },
1737+ // This must be declared before keyword because we use "function" inside the look-forward
1738+ "function-variable": {
1739+ pattern:
1740+ /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,
1741+ alias: "function",
1742+ },
1743+ parameter: [
1744+ {
1745+ pattern:
1746+ /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,
1747+ lookbehind: true,
1748+ inside: Prism.languages.javascript,
1749+ },
1750+ {
1751+ pattern:
1752+ /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,
1753+ lookbehind: true,
1754+ inside: Prism.languages.javascript,
1755+ },
1756+ {
1757+ pattern:
1758+ /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,
1759+ lookbehind: true,
1760+ inside: Prism.languages.javascript,
1761+ },
1762+ {
1763+ pattern:
1764+ /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,
1765+ lookbehind: true,
1766+ inside: Prism.languages.javascript,
1767+ },
1768+ ],
1769+ constant: /\b[A-Z](?:[A-Z_]|\dx?)*\b/,
16651770 });
16661771
1667−Prism.languages.insertBefore('javascript', 'string', {
1668− 'hashbang': {
1669− pattern: /^#!.*/,
1670− greedy: true,
1671− alias: 'comment'
1672− },
1673− 'template-string': {
1674− pattern: /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,
1675− greedy: true,
1676− inside: {
1677− 'template-punctuation': {
1678− pattern: /^`|`$/,
1679− alias: 'string'
1680− },
1681− 'interpolation': {
1682− pattern: /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,
1683− lookbehind: true,
1684− inside: {
1685− 'interpolation-punctuation': {
1686− pattern: /^\$\{|\}$/,
1687− alias: 'punctuation'
1688− },
1689− rest: Prism.languages.javascript
1690− }
1691− },
1692− 'string': /[\s\S]+/
1693− }
1694− },
1695− 'string-property': {
1696− pattern: /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,
1697− lookbehind: true,
1698− greedy: true,
1699− alias: 'property'
1700− }
1772+Prism.languages.insertBefore("javascript", "string", {
1773+ hashbang: {
1774+ pattern: /^#!.*/,
1775+ greedy: true,
1776+ alias: "comment",
1777+ },
1778+ "template-string": {
1779+ pattern:
1780+ /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,
1781+ greedy: true,
1782+ inside: {
1783+ "template-punctuation": {
1784+ pattern: /^`|`$/,
1785+ alias: "string",
1786+ },
1787+ interpolation: {
1788+ pattern:
1789+ /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,
1790+ lookbehind: true,
1791+ inside: {
1792+ "interpolation-punctuation": {
1793+ pattern: /^\$\{|\}$/,
1794+ alias: "punctuation",
1795+ },
1796+ rest: Prism.languages.javascript,
1797+ },
1798+ },
1799+ string: /[\s\S]+/,
1800+ },
1801+ },
1802+ "string-property": {
1803+ pattern:
1804+ /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,
1805+ lookbehind: true,
1806+ greedy: true,
1807+ alias: "property",
1808+ },
17011809 });
17021810
1703−Prism.languages.insertBefore('javascript', 'operator', {
1704− 'literal-property': {
1705− pattern: /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,
1706− lookbehind: true,
1707− alias: 'property'
1708− },
1811+Prism.languages.insertBefore("javascript", "operator", {
1812+ "literal-property": {
1813+ pattern:
1814+ /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,
1815+ lookbehind: true,
1816+ alias: "property",
1817+ },
17091818 });
17101819
17111820 if (Prism.languages.markup) {
1712− Prism.languages.markup.tag.addInlined('script', 'javascript');
1713−
1714− // add attribute support for all DOM events.
1715− // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events
1716− Prism.languages.markup.tag.addAttribute(
1717− /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,
1718− 'javascript'
1719− );
1821+ Prism.languages.markup.tag.addInlined("script", "javascript");
1822+
1823+ // add attribute support for all DOM events.
1824+ // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events
1825+ Prism.languages.markup.tag.addAttribute(
1826+ /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/
1827+ .source,
1828+ "javascript",
1829+ );
17201830 }
17211831
17221832 Prism.languages.js = Prism.languages.javascript;
17231833
17241834 (function (Prism) {
1725− // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|'
1726− // + LC_ALL, RANDOM, REPLY, SECONDS.
1727− // + make sure PS1..4 are here as they are not always set,
1728− // - some useless things.
1729− var envVars = '\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b';
1730−
1731− var commandAfterHeredoc = {
1732− pattern: /(^(["']?)\w+\2)[ \t]+\S.*/,
1733− lookbehind: true,
1734− alias: 'punctuation', // this looks reasonably well in all themes
1735− inside: null // see below
1736− };
1737−
1738− var insideString = {
1739− 'bash': commandAfterHeredoc,
1740− 'environment': {
1741− pattern: RegExp('\\$' + envVars),
1742− alias: 'constant'
1743− },
1744− 'variable': [
1745− // [0]: Arithmetic Environment
1746− {
1747− pattern: /\$?\(\([\s\S]+?\)\)/,
1748− greedy: true,
1749− inside: {
1750− // If there is a $ sign at the beginning highlight $(( and )) as variable
1751− 'variable': [
1752− {
1753− pattern: /(^\$\(\([\s\S]+)\)\)/,
1754− lookbehind: true
1755− },
1756− /^\$\(\(/
1757− ],
1758− 'number': /\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,
1759− // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic
1760− 'operator': /--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,
1761− // If there is no $ sign at the beginning highlight (( and )) as punctuation
1762− 'punctuation': /\(\(?|\)\)?|,|;/
1763− }
1764− },
1765− // [1]: Command Substitution
1766− {
1767− pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,
1768− greedy: true,
1769− inside: {
1770− 'variable': /^\$\(|^`|\)$|`$/
1771− }
1772− },
1773− // [2]: Brace expansion
1774− {
1775− pattern: /\$\{[^}]+\}/,
1776− greedy: true,
1777− inside: {
1778− 'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,
1779− 'punctuation': /[\[\]]/,
1780− 'environment': {
1781− pattern: RegExp('(\\{)' + envVars),
1782− lookbehind: true,
1783− alias: 'constant'
1784− }
1785− }
1786− },
1787− /\$(?:\w+|[#?*!@$])/
1788− ],
1789− // Escape sequences from echo and printf's manuals, and escaped quotes.
1790− 'entity': /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/
1791− };
1792−
1793− Prism.languages.bash = {
1794− 'shebang': {
1795− pattern: /^#!\s*\/.*/,
1796− alias: 'important'
1797− },
1798− 'comment': {
1799− pattern: /(^|[^"{\\$])#.*/,
1800− lookbehind: true
1801− },
1802− 'function-name': [
1803− // a) function foo {
1804− // b) foo() {
1805− // c) function foo() {
1806− // but not “foo {”
1807− {
1808− // a) and c)
1809− pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,
1810− lookbehind: true,
1811− alias: 'function'
1812− },
1813− {
1814− // b)
1815− pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/,
1816− alias: 'function'
1817− }
1818− ],
1819− // Highlight variable names as variables in for and select beginnings.
1820− 'for-or-select': {
1821− pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/,
1822− alias: 'variable',
1823− lookbehind: true
1824− },
1825− // Highlight variable names as variables in the left-hand part
1826− // of assignments (“=” and “+=”).
1827− 'assign-left': {
1828− pattern: /(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,
1829− inside: {
1830− 'environment': {
1831− pattern: RegExp('(^|[\\s;|&]|[<>]\\()' + envVars),
1832− lookbehind: true,
1833− alias: 'constant'
1834− }
1835− },
1836− alias: 'variable',
1837− lookbehind: true
1838− },
1839− // Highlight parameter names as variables
1840− 'parameter': {
1841− pattern: /(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,
1842− alias: 'variable',
1843− lookbehind: true
1844− },
1845− 'string': [
1846− // Support for Here-documents https://en.wikipedia.org/wiki/Here_document
1847− {
1848− pattern: /((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,
1849− lookbehind: true,
1850− greedy: true,
1851− inside: insideString
1852− },
1853− // Here-document with quotes around the tag
1854− // → No expansion (so no “inside”).
1855− {
1856− pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,
1857− lookbehind: true,
1858− greedy: true,
1859− inside: {
1860− 'bash': commandAfterHeredoc
1861− }
1862− },
1863− // “Normal” string
1864− {
1865− // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
1866− pattern: /(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,
1867− lookbehind: true,
1868− greedy: true,
1869− inside: insideString
1870− },
1871− {
1872− // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html
1873− pattern: /(^|[^$\\])'[^']*'/,
1874− lookbehind: true,
1875− greedy: true
1876− },
1877− {
1878− // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
1879− pattern: /\$'(?:[^'\\]|\\[\s\S])*'/,
1880− greedy: true,
1881− inside: {
1882− 'entity': insideString.entity
1883− }
1884− }
1885− ],
1886− 'environment': {
1887− pattern: RegExp('\\$?' + envVars),
1888− alias: 'constant'
1889− },
1890− 'variable': insideString.variable,
1891− 'function': {
1892− pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,
1893− lookbehind: true
1894− },
1895− 'keyword': {
1896− pattern: /(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,
1897− lookbehind: true
1898− },
1899− // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
1900− 'builtin': {
1901− pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,
1902− lookbehind: true,
1903− // Alias added to make those easier to distinguish from strings.
1904− alias: 'class-name'
1905− },
1906− 'boolean': {
1907− pattern: /(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,
1908− lookbehind: true
1909− },
1910− 'file-descriptor': {
1911− pattern: /\B&\d\b/,
1912− alias: 'important'
1913− },
1914− 'operator': {
1915− // Lots of redirections here, but not just that.
1916− pattern: /\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,
1917− inside: {
1918− 'file-descriptor': {
1919− pattern: /^\d/,
1920− alias: 'important'
1921− }
1922− }
1923− },
1924− 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,
1925− 'number': {
1926− pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,
1927− lookbehind: true
1928− }
1929− };
1930−
1931− commandAfterHeredoc.inside = Prism.languages.bash;
1932−
1933− /* Patterns in command substitution. */
1934− var toBeCopied = [
1935− 'comment',
1936− 'function-name',
1937− 'for-or-select',
1938− 'assign-left',
1939− 'parameter',
1940− 'string',
1941− 'environment',
1942− 'function',
1943− 'keyword',
1944− 'builtin',
1945− 'boolean',
1946− 'file-descriptor',
1947− 'operator',
1948− 'punctuation',
1949− 'number'
1950− ];
1951− var inside = insideString.variable[1].inside;
1952− for (var i = 0; i < toBeCopied.length; i++) {
1953− inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]];
1954− }
1955−
1956− Prism.languages.sh = Prism.languages.bash;
1957− Prism.languages.shell = Prism.languages.bash;
1958−}(Prism));
1959−
1960−Prism.languages.c = Prism.languages.extend('clike', {
1961− 'comment': {
1962− pattern: /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,
1963− greedy: true
1964− },
1965− 'string': {
1966− // https://en.cppreference.com/w/c/language/string_literal
1967− pattern: /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,
1968− greedy: true
1969− },
1970− 'class-name': {
1971− pattern: /(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,
1972− lookbehind: true
1973− },
1974− 'keyword': /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,
1975− 'function': /\b[a-z_]\w*(?=\s*\()/i,
1976− 'number': /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,
1977− 'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/
1835+ // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|'
1836+ // + LC_ALL, RANDOM, REPLY, SECONDS.
1837+ // + make sure PS1..4 are here as they are not always set,
1838+ // - some useless things.
1839+ var envVars =
1840+ "\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b";
1841+
1842+ var commandAfterHeredoc = {
1843+ pattern: /(^(["']?)\w+\2)[ \t]+\S.*/,
1844+ lookbehind: true,
1845+ alias: "punctuation", // this looks reasonably well in all themes
1846+ inside: null, // see below
1847+ };
1848+
1849+ var insideString = {
1850+ bash: commandAfterHeredoc,
1851+ environment: {
1852+ pattern: RegExp("\\$" + envVars),
1853+ alias: "constant",
1854+ },
1855+ variable: [
1856+ // [0]: Arithmetic Environment
1857+ {
1858+ pattern: /\$?\(\([\s\S]+?\)\)/,
1859+ greedy: true,
1860+ inside: {
1861+ // If there is a $ sign at the beginning highlight $(( and )) as variable
1862+ variable: [
1863+ {
1864+ pattern: /(^\$\(\([\s\S]+)\)\)/,
1865+ lookbehind: true,
1866+ },
1867+ /^\$\(\(/,
1868+ ],
1869+ number: /\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,
1870+ // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic
1871+ operator: /--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,
1872+ // If there is no $ sign at the beginning highlight (( and )) as punctuation
1873+ punctuation: /\(\(?|\)\)?|,|;/,
1874+ },
1875+ },
1876+ // [1]: Command Substitution
1877+ {
1878+ pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,
1879+ greedy: true,
1880+ inside: {
1881+ variable: /^\$\(|^`|\)$|`$/,
1882+ },
1883+ },
1884+ // [2]: Brace expansion
1885+ {
1886+ pattern: /\$\{[^}]+\}/,
1887+ greedy: true,
1888+ inside: {
1889+ operator: /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,
1890+ punctuation: /[\[\]]/,
1891+ environment: {
1892+ pattern: RegExp("(\\{)" + envVars),
1893+ lookbehind: true,
1894+ alias: "constant",
1895+ },
1896+ },
1897+ },
1898+ /\$(?:\w+|[#?*!@$])/,
1899+ ],
1900+ // Escape sequences from echo and printf's manuals, and escaped quotes.
1901+ entity:
1902+ /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/,
1903+ };
1904+
1905+ Prism.languages.bash = {
1906+ shebang: {
1907+ pattern: /^#!\s*\/.*/,
1908+ alias: "important",
1909+ },
1910+ comment: {
1911+ pattern: /(^|[^"{\\$])#.*/,
1912+ lookbehind: true,
1913+ },
1914+ "function-name": [
1915+ // a) function foo {
1916+ // b) foo() {
1917+ // c) function foo() {
1918+ // but not “foo {”
1919+ {
1920+ // a) and c)
1921+ pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,
1922+ lookbehind: true,
1923+ alias: "function",
1924+ },
1925+ {
1926+ // b)
1927+ pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/,
1928+ alias: "function",
1929+ },
1930+ ],
1931+ // Highlight variable names as variables in for and select beginnings.
1932+ "for-or-select": {
1933+ pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/,
1934+ alias: "variable",
1935+ lookbehind: true,
1936+ },
1937+ // Highlight variable names as variables in the left-hand part
1938+ // of assignments (“=” and “+=”).
1939+ "assign-left": {
1940+ pattern: /(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,
1941+ inside: {
1942+ environment: {
1943+ pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars),
1944+ lookbehind: true,
1945+ alias: "constant",
1946+ },
1947+ },
1948+ alias: "variable",
1949+ lookbehind: true,
1950+ },
1951+ // Highlight parameter names as variables
1952+ parameter: {
1953+ pattern: /(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,
1954+ alias: "variable",
1955+ lookbehind: true,
1956+ },
1957+ string: [
1958+ // Support for Here-documents https://en.wikipedia.org/wiki/Here_document
1959+ {
1960+ pattern: /((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,
1961+ lookbehind: true,
1962+ greedy: true,
1963+ inside: insideString,
1964+ },
1965+ // Here-document with quotes around the tag
1966+ // → No expansion (so no “inside”).
1967+ {
1968+ pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,
1969+ lookbehind: true,
1970+ greedy: true,
1971+ inside: {
1972+ bash: commandAfterHeredoc,
1973+ },
1974+ },
1975+ // “Normal” string
1976+ {
1977+ // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
1978+ pattern:
1979+ /(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,
1980+ lookbehind: true,
1981+ greedy: true,
1982+ inside: insideString,
1983+ },
1984+ {
1985+ // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html
1986+ pattern: /(^|[^$\\])'[^']*'/,
1987+ lookbehind: true,
1988+ greedy: true,
1989+ },
1990+ {
1991+ // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
1992+ pattern: /\$'(?:[^'\\]|\\[\s\S])*'/,
1993+ greedy: true,
1994+ inside: {
1995+ entity: insideString.entity,
1996+ },
1997+ },
1998+ ],
1999+ environment: {
2000+ pattern: RegExp("\\$?" + envVars),
2001+ alias: "constant",
2002+ },
2003+ variable: insideString.variable,
2004+ function: {
2005+ pattern:
2006+ /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,
2007+ lookbehind: true,
2008+ },
2009+ keyword: {
2010+ pattern:
2011+ /(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,
2012+ lookbehind: true,
2013+ },
2014+ // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
2015+ builtin: {
2016+ pattern:
2017+ /(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,
2018+ lookbehind: true,
2019+ // Alias added to make those easier to distinguish from strings.
2020+ alias: "class-name",
2021+ },
2022+ boolean: {
2023+ pattern: /(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,
2024+ lookbehind: true,
2025+ },
2026+ "file-descriptor": {
2027+ pattern: /\B&\d\b/,
2028+ alias: "important",
2029+ },
2030+ operator: {
2031+ // Lots of redirections here, but not just that.
2032+ pattern:
2033+ /\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,
2034+ inside: {
2035+ "file-descriptor": {
2036+ pattern: /^\d/,
2037+ alias: "important",
2038+ },
2039+ },
2040+ },
2041+ punctuation: /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,
2042+ number: {
2043+ pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,
2044+ lookbehind: true,
2045+ },
2046+ };
2047+
2048+ commandAfterHeredoc.inside = Prism.languages.bash;
2049+
2050+ /* Patterns in command substitution. */
2051+ var toBeCopied = [
2052+ "comment",
2053+ "function-name",
2054+ "for-or-select",
2055+ "assign-left",
2056+ "parameter",
2057+ "string",
2058+ "environment",
2059+ "function",
2060+ "keyword",
2061+ "builtin",
2062+ "boolean",
2063+ "file-descriptor",
2064+ "operator",
2065+ "punctuation",
2066+ "number",
2067+ ];
2068+ var inside = insideString.variable[1].inside;
2069+ for (var i = 0; i < toBeCopied.length; i++) {
2070+ inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]];
2071+ }
2072+
2073+ Prism.languages.sh = Prism.languages.bash;
2074+ Prism.languages.shell = Prism.languages.bash;
2075+})(Prism);
2076+
2077+Prism.languages.c = Prism.languages.extend("clike", {
2078+ comment: {
2079+ pattern:
2080+ /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,
2081+ greedy: true,
2082+ },
2083+ string: {
2084+ // https://en.cppreference.com/w/c/language/string_literal
2085+ pattern: /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,
2086+ greedy: true,
2087+ },
2088+ "class-name": {
2089+ pattern:
2090+ /(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,
2091+ lookbehind: true,
2092+ },
2093+ keyword:
2094+ /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,
2095+ function: /\b[a-z_]\w*(?=\s*\()/i,
2096+ number:
2097+ /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,
2098+ operator: />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,
19782099 });
19792100
1980−Prism.languages.insertBefore('c', 'string', {
1981− 'char': {
1982− // https://en.cppreference.com/w/c/language/character_constant
1983− pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,
1984− greedy: true
1985− }
2101+Prism.languages.insertBefore("c", "string", {
2102+ char: {
2103+ // https://en.cppreference.com/w/c/language/character_constant
2104+ pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,
2105+ greedy: true,
2106+ },
19862107 });
19872108
1988−Prism.languages.insertBefore('c', 'string', {
1989− 'macro': {
1990− // allow for multiline macro definitions
1991− // spaces after the # character compile fine with gcc
1992− pattern: /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,
1993− lookbehind: true,
1994− greedy: true,
1995− alias: 'property',
1996− inside: {
1997− 'string': [
1998− {
1999− // highlight the path of the include statement as a string
2000− pattern: /^(#\s*include\s*)<[^>]+>/,
2001− lookbehind: true
2002− },
2003− Prism.languages.c['string']
2004− ],
2005− 'char': Prism.languages.c['char'],
2006− 'comment': Prism.languages.c['comment'],
2007− 'macro-name': [
2008− {
2009− pattern: /(^#\s*define\s+)\w+\b(?!\()/i,
2010− lookbehind: true
2011− },
2012− {
2013− pattern: /(^#\s*define\s+)\w+\b(?=\()/i,
2014− lookbehind: true,
2015− alias: 'function'
2016− }
2017− ],
2018− // highlight macro directives as keywords
2019− 'directive': {
2020− pattern: /^(#\s*)[a-z]+/,
2021− lookbehind: true,
2022− alias: 'keyword'
2023− },
2024− 'directive-hash': /^#/,
2025− 'punctuation': /##|\\(?=[\r\n])/,
2026− 'expression': {
2027− pattern: /\S[\s\S]*/,
2028− inside: Prism.languages.c
2029− }
2030− }
2031− }
2109+Prism.languages.insertBefore("c", "string", {
2110+ macro: {
2111+ // allow for multiline macro definitions
2112+ // spaces after the # character compile fine with gcc
2113+ pattern:
2114+ /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,
2115+ lookbehind: true,
2116+ greedy: true,
2117+ alias: "property",
2118+ inside: {
2119+ string: [
2120+ {
2121+ // highlight the path of the include statement as a string
2122+ pattern: /^(#\s*include\s*)<[^>]+>/,
2123+ lookbehind: true,
2124+ },
2125+ Prism.languages.c["string"],
2126+ ],
2127+ char: Prism.languages.c["char"],
2128+ comment: Prism.languages.c["comment"],
2129+ "macro-name": [
2130+ {
2131+ pattern: /(^#\s*define\s+)\w+\b(?!\()/i,
2132+ lookbehind: true,
2133+ },
2134+ {
2135+ pattern: /(^#\s*define\s+)\w+\b(?=\()/i,
2136+ lookbehind: true,
2137+ alias: "function",
2138+ },
2139+ ],
2140+ // highlight macro directives as keywords
2141+ directive: {
2142+ pattern: /^(#\s*)[a-z]+/,
2143+ lookbehind: true,
2144+ alias: "keyword",
2145+ },
2146+ "directive-hash": /^#/,
2147+ punctuation: /##|\\(?=[\r\n])/,
2148+ expression: {
2149+ pattern: /\S[\s\S]*/,
2150+ inside: Prism.languages.c,
2151+ },
2152+ },
2153+ },
20322154 });
20332155
2034−Prism.languages.insertBefore('c', 'function', {
2035− // highlight predefined macros as constants
2036− 'constant': /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/
2156+Prism.languages.insertBefore("c", "function", {
2157+ // highlight predefined macros as constants
2158+ constant:
2159+ /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/,
20372160 });
20382161
2039−delete Prism.languages.c['boolean'];
2162+delete Prism.languages.c["boolean"];
20402163
20412164 (function (Prism) {
2042−
2043− var keyword = /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;
2044− var modName = /\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g, function () { return keyword.source; });
2045−
2046− Prism.languages.cpp = Prism.languages.extend('c', {
2047− 'class-name': [
2048− {
2049− pattern: RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source
2050− .replace(/<keyword>/g, function () { return keyword.source; })),
2051− lookbehind: true
2052− },
2053− // This is intended to capture the class name of method implementations like:
2054− // void foo::bar() const {}
2055− // However! The `foo` in the above example could also be a namespace, so we only capture the class name if
2056− // it starts with an uppercase letter. This approximation should give decent results.
2057− /\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,
2058− // This will capture the class name before destructors like:
2059− // Foo::~Foo() {}
2060− /\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,
2061− // This also intends to capture the class name of method implementations but here the class has template
2062− // parameters, so it can't be a namespace (until C++ adds generic namespaces).
2063− /\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/
2064− ],
2065− 'keyword': keyword,
2066− 'number': {
2067− pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,
2068− greedy: true
2069− },
2070− 'operator': />>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
2071− 'boolean': /\b(?:false|true)\b/
2072− });
2073−
2074− Prism.languages.insertBefore('cpp', 'string', {
2075− 'module': {
2076− // https://en.cppreference.com/w/cpp/language/modules
2077− pattern: RegExp(
2078− /(\b(?:import|module)\s+)/.source +
2079− '(?:' +
2080− // header-name
2081− /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source +
2082− '|' +
2083− // module name or partition or both
2084− /<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g, function () { return modName; }) +
2085− ')'
2086− ),
2087− lookbehind: true,
2088− greedy: true,
2089− inside: {
2090− 'string': /^[<"][\s\S]+/,
2091− 'operator': /:/,
2092− 'punctuation': /\./
2093− }
2094− },
2095− 'raw-string': {
2096− pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
2097− alias: 'string',
2098− greedy: true
2099− }
2100− });
2101−
2102− Prism.languages.insertBefore('cpp', 'keyword', {
2103− 'generic-function': {
2104− pattern: /\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,
2105− inside: {
2106− 'function': /^\w+/,
2107− 'generic': {
2108− pattern: /<[\s\S]+/,
2109− alias: 'class-name',
2110− inside: Prism.languages.cpp
2111− }
2112− }
2113− }
2114− });
2115−
2116− Prism.languages.insertBefore('cpp', 'operator', {
2117− 'double-colon': {
2118− pattern: /::/,
2119− alias: 'punctuation'
2120− }
2121− });
2122−
2123− Prism.languages.insertBefore('cpp', 'class-name', {
2124− // the base clause is an optional list of parent classes
2125− // https://en.cppreference.com/w/cpp/language/class
2126− 'base-clause': {
2127− pattern: /(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,
2128− lookbehind: true,
2129− greedy: true,
2130− inside: Prism.languages.extend('cpp', {})
2131− }
2132− });
2133−
2134− Prism.languages.insertBefore('inside', 'double-colon', {
2135− // All untokenized words that are not namespaces should be class names
2136− 'class-name': /\b[a-z_]\w*\b(?!\s*::)/i
2137− }, Prism.languages.cpp['base-clause']);
2138−
2139−}(Prism));
2165+ var keyword =
2166+ /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;
2167+ var modName = /\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(
2168+ /<keyword>/g,
2169+ function () {
2170+ return keyword.source;
2171+ },
2172+ );
2173+
2174+ Prism.languages.cpp = Prism.languages.extend("c", {
2175+ "class-name": [
2176+ {
2177+ pattern: RegExp(
2178+ /(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace(
2179+ /<keyword>/g,
2180+ function () {
2181+ return keyword.source;
2182+ },
2183+ ),
2184+ ),
2185+ lookbehind: true,
2186+ },
2187+ // This is intended to capture the class name of method implementations like:
2188+ // void foo::bar() const {}
2189+ // However! The `foo` in the above example could also be a namespace, so we only capture the class name if
2190+ // it starts with an uppercase letter. This approximation should give decent results.
2191+ /\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,
2192+ // This will capture the class name before destructors like:
2193+ // Foo::~Foo() {}
2194+ /\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,
2195+ // This also intends to capture the class name of method implementations but here the class has template
2196+ // parameters, so it can't be a namespace (until C++ adds generic namespaces).
2197+ /\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/,
2198+ ],
2199+ keyword: keyword,
2200+ number: {
2201+ pattern:
2202+ /(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,
2203+ greedy: true,
2204+ },
2205+ operator:
2206+ />>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
2207+ boolean: /\b(?:false|true)\b/,
2208+ });
2209+
2210+ Prism.languages.insertBefore("cpp", "string", {
2211+ module: {
2212+ // https://en.cppreference.com/w/cpp/language/modules
2213+ pattern: RegExp(
2214+ /(\b(?:import|module)\s+)/.source +
2215+ "(?:" +
2216+ // header-name
2217+ /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source +
2218+ "|" +
2219+ // module name or partition or both
2220+ /<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(
2221+ /<mod-name>/g,
2222+ function () {
2223+ return modName;
2224+ },
2225+ ) +
2226+ ")",
2227+ ),
2228+ lookbehind: true,
2229+ greedy: true,
2230+ inside: {
2231+ string: /^[<"][\s\S]+/,
2232+ operator: /:/,
2233+ punctuation: /\./,
2234+ },
2235+ },
2236+ "raw-string": {
2237+ pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
2238+ alias: "string",
2239+ greedy: true,
2240+ },
2241+ });
2242+
2243+ Prism.languages.insertBefore("cpp", "keyword", {
2244+ "generic-function": {
2245+ pattern: /\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,
2246+ inside: {
2247+ function: /^\w+/,
2248+ generic: {
2249+ pattern: /<[\s\S]+/,
2250+ alias: "class-name",
2251+ inside: Prism.languages.cpp,
2252+ },
2253+ },
2254+ },
2255+ });
2256+
2257+ Prism.languages.insertBefore("cpp", "operator", {
2258+ "double-colon": {
2259+ pattern: /::/,
2260+ alias: "punctuation",
2261+ },
2262+ });
2263+
2264+ Prism.languages.insertBefore("cpp", "class-name", {
2265+ // the base clause is an optional list of parent classes
2266+ // https://en.cppreference.com/w/cpp/language/class
2267+ "base-clause": {
2268+ pattern:
2269+ /(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,
2270+ lookbehind: true,
2271+ greedy: true,
2272+ inside: Prism.languages.extend("cpp", {}),
2273+ },
2274+ });
2275+
2276+ Prism.languages.insertBefore(
2277+ "inside",
2278+ "double-colon",
2279+ {
2280+ // All untokenized words that are not namespaces should be class names
2281+ "class-name": /\b[a-z_]\w*\b(?!\s*::)/i,
2282+ },
2283+ Prism.languages.cpp["base-clause"],
2284+ );
2285+})(Prism);
21402286
21412287 Prism.languages.cmake = {
2142− 'comment': /#.*/,
2143− 'string': {
2144− pattern: /"(?:[^\\"]|\\.)*"/,
2145− greedy: true,
2146− inside: {
2147− 'interpolation': {
2148− pattern: /\$\{(?:[^{}$]|\$\{[^{}$]*\})*\}/,
2149− inside: {
2150− 'punctuation': /\$\{|\}/,
2151− 'variable': /\w+/
2152− }
2153− }
2154− }
2155− },
2156− 'variable': /\b(?:CMAKE_\w+|\w+_(?:(?:BINARY|SOURCE)_DIR|DESCRIPTION|HOMEPAGE_URL|ROOT|VERSION(?:_MAJOR|_MINOR|_PATCH|_TWEAK)?)|(?:ANDROID|APPLE|BORLAND|BUILD_SHARED_LIBS|CACHE|CPACK_(?:ABSOLUTE_DESTINATION_FILES|COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY|ERROR_ON_ABSOLUTE_INSTALL_DESTINATION|INCLUDE_TOPLEVEL_DIRECTORY|INSTALL_DEFAULT_DIRECTORY_PERMISSIONS|INSTALL_SCRIPT|PACKAGING_INSTALL_PREFIX|SET_DESTDIR|WARN_ON_ABSOLUTE_INSTALL_DESTINATION)|CTEST_(?:BINARY_DIRECTORY|BUILD_COMMAND|BUILD_NAME|BZR_COMMAND|BZR_UPDATE_OPTIONS|CHANGE_ID|CHECKOUT_COMMAND|CONFIGURATION_TYPE|CONFIGURE_COMMAND|COVERAGE_COMMAND|COVERAGE_EXTRA_FLAGS|CURL_OPTIONS|CUSTOM_(?:COVERAGE_EXCLUDE|ERROR_EXCEPTION|ERROR_MATCH|ERROR_POST_CONTEXT|ERROR_PRE_CONTEXT|MAXIMUM_FAILED_TEST_OUTPUT_SIZE|MAXIMUM_NUMBER_OF_(?:ERRORS|WARNINGS)|MAXIMUM_PASSED_TEST_OUTPUT_SIZE|MEMCHECK_IGNORE|POST_MEMCHECK|POST_TEST|PRE_MEMCHECK|PRE_TEST|TESTS_IGNORE|WARNING_EXCEPTION|WARNING_MATCH)|CVS_CHECKOUT|CVS_COMMAND|CVS_UPDATE_OPTIONS|DROP_LOCATION|DROP_METHOD|DROP_SITE|DROP_SITE_CDASH|DROP_SITE_PASSWORD|DROP_SITE_USER|EXTRA_COVERAGE_GLOB|GIT_COMMAND|GIT_INIT_SUBMODULES|GIT_UPDATE_CUSTOM|GIT_UPDATE_OPTIONS|HG_COMMAND|HG_UPDATE_OPTIONS|LABELS_FOR_SUBPROJECTS|MEMORYCHECK_(?:COMMAND|COMMAND_OPTIONS|SANITIZER_OPTIONS|SUPPRESSIONS_FILE|TYPE)|NIGHTLY_START_TIME|P4_CLIENT|P4_COMMAND|P4_OPTIONS|P4_UPDATE_OPTIONS|RUN_CURRENT_SCRIPT|SCP_COMMAND|SITE|SOURCE_DIRECTORY|SUBMIT_URL|SVN_COMMAND|SVN_OPTIONS|SVN_UPDATE_OPTIONS|TEST_LOAD|TEST_TIMEOUT|TRIGGER_SITE|UPDATE_COMMAND|UPDATE_OPTIONS|UPDATE_VERSION_ONLY|USE_LAUNCHERS)|CYGWIN|ENV|EXECUTABLE_OUTPUT_PATH|GHS-MULTI|IOS|LIBRARY_OUTPUT_PATH|MINGW|MSVC(?:10|11|12|14|60|70|71|80|90|_IDE|_TOOLSET_VERSION|_VERSION)?|MSYS|PROJECT_NAME|UNIX|WIN32|WINCE|WINDOWS_PHONE|WINDOWS_STORE|XCODE))\b/,
2157− 'property': /\b(?:cxx_\w+|(?:ARCHIVE_OUTPUT_(?:DIRECTORY|NAME)|COMPILE_DEFINITIONS|COMPILE_PDB_NAME|COMPILE_PDB_OUTPUT_DIRECTORY|EXCLUDE_FROM_DEFAULT_BUILD|IMPORTED_(?:IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_LANGUAGES|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|NO_SONAME|OBJECTS|SONAME)|INTERPROCEDURAL_OPTIMIZATION|LIBRARY_OUTPUT_DIRECTORY|LIBRARY_OUTPUT_NAME|LINK_FLAGS|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_NAME|PDB_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_NAME|STATIC_LIBRARY_FLAGS|VS_CSHARP|VS_DOTNET_REFERENCEPROP|VS_DOTNET_REFERENCE|VS_GLOBAL_SECTION_POST|VS_GLOBAL_SECTION_PRE|VS_GLOBAL|XCODE_ATTRIBUTE)_\w+|\w+_(?:CLANG_TIDY|COMPILER_LAUNCHER|CPPCHECK|CPPLINT|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|ABSTRACT|ADDITIONAL_MAKE_CLEAN_FILES|ADVANCED|ALIASED_TARGET|ALLOW_DUPLICATE_CUSTOM_TARGETS|ANDROID_(?:ANT_ADDITIONAL_OPTIONS|API|API_MIN|ARCH|ASSETS_DIRECTORIES|GUI|JAR_DEPENDENCIES|NATIVE_LIB_DEPENDENCIES|NATIVE_LIB_DIRECTORIES|PROCESS_MAX|PROGUARD|PROGUARD_CONFIG_PATH|SECURE_PROPS_PATH|SKIP_ANT_STEP|STL_TYPE)|ARCHIVE_OUTPUT_DIRECTORY|ATTACHED_FILES|ATTACHED_FILES_ON_FAIL|AUTOGEN_(?:BUILD_DIR|ORIGIN_DEPENDS|PARALLEL|SOURCE_GROUP|TARGETS_FOLDER|TARGET_DEPENDS)|AUTOMOC|AUTOMOC_(?:COMPILER_PREDEFINES|DEPEND_FILTERS|EXECUTABLE|MACRO_NAMES|MOC_OPTIONS|SOURCE_GROUP|TARGETS_FOLDER)|AUTORCC|AUTORCC_EXECUTABLE|AUTORCC_OPTIONS|AUTORCC_SOURCE_GROUP|AUTOUIC|AUTOUIC_EXECUTABLE|AUTOUIC_OPTIONS|AUTOUIC_SEARCH_PATHS|BINARY_DIR|BUILDSYSTEM_TARGETS|BUILD_RPATH|BUILD_RPATH_USE_ORIGIN|BUILD_WITH_INSTALL_NAME_DIR|BUILD_WITH_INSTALL_RPATH|BUNDLE|BUNDLE_EXTENSION|CACHE_VARIABLES|CLEAN_NO_CUSTOM|COMMON_LANGUAGE_RUNTIME|COMPATIBLE_INTERFACE_(?:BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|COMPILE_(?:DEFINITIONS|FEATURES|FLAGS|OPTIONS|PDB_NAME|PDB_OUTPUT_DIRECTORY)|COST|CPACK_DESKTOP_SHORTCUTS|CPACK_NEVER_OVERWRITE|CPACK_PERMANENT|CPACK_STARTUP_SHORTCUTS|CPACK_START_MENU_SHORTCUTS|CPACK_WIX_ACL|CROSSCOMPILING_EMULATOR|CUDA_EXTENSIONS|CUDA_PTX_COMPILATION|CUDA_RESOLVE_DEVICE_SYMBOLS|CUDA_SEPARABLE_COMPILATION|CUDA_STANDARD|CUDA_STANDARD_REQUIRED|CXX_EXTENSIONS|CXX_STANDARD|CXX_STANDARD_REQUIRED|C_EXTENSIONS|C_STANDARD|C_STANDARD_REQUIRED|DEBUG_CONFIGURATIONS|DEFINE_SYMBOL|DEFINITIONS|DEPENDS|DEPLOYMENT_ADDITIONAL_FILES|DEPLOYMENT_REMOTE_DIRECTORY|DISABLED|DISABLED_FEATURES|ECLIPSE_EXTRA_CPROJECT_CONTENTS|ECLIPSE_EXTRA_NATURES|ENABLED_FEATURES|ENABLED_LANGUAGES|ENABLE_EXPORTS|ENVIRONMENT|EXCLUDE_FROM_ALL|EXCLUDE_FROM_DEFAULT_BUILD|EXPORT_NAME|EXPORT_PROPERTIES|EXTERNAL_OBJECT|EchoString|FAIL_REGULAR_EXPRESSION|FIND_LIBRARY_USE_LIB32_PATHS|FIND_LIBRARY_USE_LIB64_PATHS|FIND_LIBRARY_USE_LIBX32_PATHS|FIND_LIBRARY_USE_OPENBSD_VERSIONING|FIXTURES_CLEANUP|FIXTURES_REQUIRED|FIXTURES_SETUP|FOLDER|FRAMEWORK|Fortran_FORMAT|Fortran_MODULE_DIRECTORY|GENERATED|GENERATOR_FILE_NAME|GENERATOR_IS_MULTI_CONFIG|GHS_INTEGRITY_APP|GHS_NO_SOURCE_GROUP_FILE|GLOBAL_DEPENDS_DEBUG_MODE|GLOBAL_DEPENDS_NO_CYCLES|GNUtoMS|HAS_CXX|HEADER_FILE_ONLY|HELPSTRING|IMPLICIT_DEPENDS_INCLUDE_TRANSFORM|IMPORTED|IMPORTED_(?:COMMON_LANGUAGE_RUNTIME|CONFIGURATIONS|GLOBAL|IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_(?:LANGUAGES|LIBRARIES|MULTIPLICITY)|LOCATION|NO_SONAME|OBJECTS|SONAME)|IMPORT_PREFIX|IMPORT_SUFFIX|INCLUDE_DIRECTORIES|INCLUDE_REGULAR_EXPRESSION|INSTALL_NAME_DIR|INSTALL_RPATH|INSTALL_RPATH_USE_LINK_PATH|INTERFACE_(?:AUTOUIC_OPTIONS|COMPILE_DEFINITIONS|COMPILE_FEATURES|COMPILE_OPTIONS|INCLUDE_DIRECTORIES|LINK_DEPENDS|LINK_DIRECTORIES|LINK_LIBRARIES|LINK_OPTIONS|POSITION_INDEPENDENT_CODE|SOURCES|SYSTEM_INCLUDE_DIRECTORIES)|INTERPROCEDURAL_OPTIMIZATION|IN_TRY_COMPILE|IOS_INSTALL_COMBINED|JOB_POOLS|JOB_POOL_COMPILE|JOB_POOL_LINK|KEEP_EXTENSION|LABELS|LANGUAGE|LIBRARY_OUTPUT_DIRECTORY|LINKER_LANGUAGE|LINK_(?:DEPENDS|DEPENDS_NO_SHARED|DIRECTORIES|FLAGS|INTERFACE_LIBRARIES|INTERFACE_MULTIPLICITY|LIBRARIES|OPTIONS|SEARCH_END_STATIC|SEARCH_START_STATIC|WHAT_YOU_USE)|LISTFILE_STACK|LOCATION|MACOSX_BUNDLE|MACOSX_BUNDLE_INFO_PLIST|MACOSX_FRAMEWORK_INFO_PLIST|MACOSX_PACKAGE_LOCATION|MACOSX_RPATH|MACROS|MANUALLY_ADDED_DEPENDENCIES|MEASUREMENT|MODIFIED|NAME|NO_SONAME|NO_SYSTEM_FROM_IMPORTED|OBJECT_DEPENDS|OBJECT_OUTPUTS|OSX_ARCHITECTURES|OUTPUT_NAME|PACKAGES_FOUND|PACKAGES_NOT_FOUND|PARENT_DIRECTORY|PASS_REGULAR_EXPRESSION|PDB_NAME|PDB_OUTPUT_DIRECTORY|POSITION_INDEPENDENT_CODE|POST_INSTALL_SCRIPT|PREDEFINED_TARGETS_FOLDER|PREFIX|PRE_INSTALL_SCRIPT|PRIVATE_HEADER|PROCESSORS|PROCESSOR_AFFINITY|PROJECT_LABEL|PUBLIC_HEADER|REPORT_UNDEFINED_PROPERTIES|REQUIRED_FILES|RESOURCE|RESOURCE_LOCK|RULE_LAUNCH_COMPILE|RULE_LAUNCH_CUSTOM|RULE_LAUNCH_LINK|RULE_MESSAGES|RUNTIME_OUTPUT_DIRECTORY|RUN_SERIAL|SKIP_AUTOGEN|SKIP_AUTOMOC|SKIP_AUTORCC|SKIP_AUTOUIC|SKIP_BUILD_RPATH|SKIP_RETURN_CODE|SOURCES|SOURCE_DIR|SOVERSION|STATIC_LIBRARY_FLAGS|STATIC_LIBRARY_OPTIONS|STRINGS|SUBDIRECTORIES|SUFFIX|SYMBOLIC|TARGET_ARCHIVES_MAY_BE_SHARED_LIBS|TARGET_MESSAGES|TARGET_SUPPORTS_SHARED_LIBS|TESTS|TEST_INCLUDE_FILE|TEST_INCLUDE_FILES|TIMEOUT|TIMEOUT_AFTER_MATCH|TYPE|USE_FOLDERS|VALUE|VARIABLES|VERSION|VISIBILITY_INLINES_HIDDEN|VS_(?:CONFIGURATION_TYPE|COPY_TO_OUT_DIR|DEBUGGER_(?:COMMAND|COMMAND_ARGUMENTS|ENVIRONMENT|WORKING_DIRECTORY)|DEPLOYMENT_CONTENT|DEPLOYMENT_LOCATION|DOTNET_REFERENCES|DOTNET_REFERENCES_COPY_LOCAL|INCLUDE_IN_VSIX|IOT_STARTUP_TASK|KEYWORD|RESOURCE_GENERATOR|SCC_AUXPATH|SCC_LOCALPATH|SCC_PROJECTNAME|SCC_PROVIDER|SDK_REFERENCES|SHADER_(?:DISABLE_OPTIMIZATIONS|ENABLE_DEBUG|ENTRYPOINT|FLAGS|MODEL|OBJECT_FILE_NAME|OUTPUT_HEADER_FILE|TYPE|VARIABLE_NAME)|STARTUP_PROJECT|TOOL_OVERRIDE|USER_PROPS|WINRT_COMPONENT|WINRT_EXTENSIONS|WINRT_REFERENCES|XAML_TYPE)|WILL_FAIL|WIN32_EXECUTABLE|WINDOWS_EXPORT_ALL_SYMBOLS|WORKING_DIRECTORY|WRAP_EXCLUDE|XCODE_(?:EMIT_EFFECTIVE_PLATFORM_NAME|EXPLICIT_FILE_TYPE|FILE_ATTRIBUTES|LAST_KNOWN_FILE_TYPE|PRODUCT_TYPE|SCHEME_(?:ADDRESS_SANITIZER|ADDRESS_SANITIZER_USE_AFTER_RETURN|ARGUMENTS|DISABLE_MAIN_THREAD_CHECKER|DYNAMIC_LIBRARY_LOADS|DYNAMIC_LINKER_API_USAGE|ENVIRONMENT|EXECUTABLE|GUARD_MALLOC|MAIN_THREAD_CHECKER_STOP|MALLOC_GUARD_EDGES|MALLOC_SCRIBBLE|MALLOC_STACK|THREAD_SANITIZER(?:_STOP)?|UNDEFINED_BEHAVIOUR_SANITIZER(?:_STOP)?|ZOMBIE_OBJECTS))|XCTEST)\b/,
2158− 'keyword': /\b(?:add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|break|build_command|build_name|cmake_host_system_information|cmake_minimum_required|cmake_parse_arguments|cmake_policy|configure_file|continue|create_test_sourcelist|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload|define_property|else|elseif|enable_language|enable_testing|endforeach|endfunction|endif|endmacro|endwhile|exec_program|execute_process|export|export_library_dependencies|file|find_file|find_library|find_package|find_path|find_program|fltk_wrap_ui|foreach|function|get_cmake_property|get_directory_property|get_filename_component|get_property|get_source_file_property|get_target_property|get_test_property|if|include|include_directories|include_external_msproject|include_guard|include_regular_expression|install|install_files|install_programs|install_targets|link_directories|link_libraries|list|load_cache|load_command|macro|make_directory|mark_as_advanced|math|message|option|output_required_files|project|qt_wrap_cpp|qt_wrap_ui|remove|remove_definitions|return|separate_arguments|set|set_directory_properties|set_property|set_source_files_properties|set_target_properties|set_tests_properties|site_name|source_group|string|subdir_depends|subdirs|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_sources|try_compile|try_run|unset|use_mangled_mesa|utility_source|variable_requires|variable_watch|while|write_file)(?=\s*\()\b/,
2159− 'boolean': /\b(?:FALSE|OFF|ON|TRUE)\b/,
2160− 'namespace': /\b(?:INTERFACE|PRIVATE|PROPERTIES|PUBLIC|SHARED|STATIC|TARGET_OBJECTS)\b/,
2161− 'operator': /\b(?:AND|DEFINED|EQUAL|GREATER|LESS|MATCHES|NOT|OR|STREQUAL|STRGREATER|STRLESS|VERSION_EQUAL|VERSION_GREATER|VERSION_LESS)\b/,
2162− 'inserted': {
2163− pattern: /\b\w+::\w+\b/,
2164− alias: 'class-name'
2165− },
2166− 'number': /\b\d+(?:\.\d+)*\b/,
2167− 'function': /\b[a-z_]\w*(?=\s*\()\b/i,
2168− 'punctuation': /[()>}]|\$[<{]/
2288+ comment: /#.*/,
2289+ string: {
2290+ pattern: /"(?:[^\\"]|\\.)*"/,
2291+ greedy: true,
2292+ inside: {
2293+ interpolation: {
2294+ pattern: /\$\{(?:[^{}$]|\$\{[^{}$]*\})*\}/,
2295+ inside: {
2296+ punctuation: /\$\{|\}/,
2297+ variable: /\w+/,
2298+ },
2299+ },
2300+ },
2301+ },
2302+ variable:
2303+ /\b(?:CMAKE_\w+|\w+_(?:(?:BINARY|SOURCE)_DIR|DESCRIPTION|HOMEPAGE_URL|ROOT|VERSION(?:_MAJOR|_MINOR|_PATCH|_TWEAK)?)|(?:ANDROID|APPLE|BORLAND|BUILD_SHARED_LIBS|CACHE|CPACK_(?:ABSOLUTE_DESTINATION_FILES|COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY|ERROR_ON_ABSOLUTE_INSTALL_DESTINATION|INCLUDE_TOPLEVEL_DIRECTORY|INSTALL_DEFAULT_DIRECTORY_PERMISSIONS|INSTALL_SCRIPT|PACKAGING_INSTALL_PREFIX|SET_DESTDIR|WARN_ON_ABSOLUTE_INSTALL_DESTINATION)|CTEST_(?:BINARY_DIRECTORY|BUILD_COMMAND|BUILD_NAME|BZR_COMMAND|BZR_UPDATE_OPTIONS|CHANGE_ID|CHECKOUT_COMMAND|CONFIGURATION_TYPE|CONFIGURE_COMMAND|COVERAGE_COMMAND|COVERAGE_EXTRA_FLAGS|CURL_OPTIONS|CUSTOM_(?:COVERAGE_EXCLUDE|ERROR_EXCEPTION|ERROR_MATCH|ERROR_POST_CONTEXT|ERROR_PRE_CONTEXT|MAXIMUM_FAILED_TEST_OUTPUT_SIZE|MAXIMUM_NUMBER_OF_(?:ERRORS|WARNINGS)|MAXIMUM_PASSED_TEST_OUTPUT_SIZE|MEMCHECK_IGNORE|POST_MEMCHECK|POST_TEST|PRE_MEMCHECK|PRE_TEST|TESTS_IGNORE|WARNING_EXCEPTION|WARNING_MATCH)|CVS_CHECKOUT|CVS_COMMAND|CVS_UPDATE_OPTIONS|DROP_LOCATION|DROP_METHOD|DROP_SITE|DROP_SITE_CDASH|DROP_SITE_PASSWORD|DROP_SITE_USER|EXTRA_COVERAGE_GLOB|GIT_COMMAND|GIT_INIT_SUBMODULES|GIT_UPDATE_CUSTOM|GIT_UPDATE_OPTIONS|HG_COMMAND|HG_UPDATE_OPTIONS|LABELS_FOR_SUBPROJECTS|MEMORYCHECK_(?:COMMAND|COMMAND_OPTIONS|SANITIZER_OPTIONS|SUPPRESSIONS_FILE|TYPE)|NIGHTLY_START_TIME|P4_CLIENT|P4_COMMAND|P4_OPTIONS|P4_UPDATE_OPTIONS|RUN_CURRENT_SCRIPT|SCP_COMMAND|SITE|SOURCE_DIRECTORY|SUBMIT_URL|SVN_COMMAND|SVN_OPTIONS|SVN_UPDATE_OPTIONS|TEST_LOAD|TEST_TIMEOUT|TRIGGER_SITE|UPDATE_COMMAND|UPDATE_OPTIONS|UPDATE_VERSION_ONLY|USE_LAUNCHERS)|CYGWIN|ENV|EXECUTABLE_OUTPUT_PATH|GHS-MULTI|IOS|LIBRARY_OUTPUT_PATH|MINGW|MSVC(?:10|11|12|14|60|70|71|80|90|_IDE|_TOOLSET_VERSION|_VERSION)?|MSYS|PROJECT_NAME|UNIX|WIN32|WINCE|WINDOWS_PHONE|WINDOWS_STORE|XCODE))\b/,
2304+ property:
2305+ /\b(?:cxx_\w+|(?:ARCHIVE_OUTPUT_(?:DIRECTORY|NAME)|COMPILE_DEFINITIONS|COMPILE_PDB_NAME|COMPILE_PDB_OUTPUT_DIRECTORY|EXCLUDE_FROM_DEFAULT_BUILD|IMPORTED_(?:IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_LANGUAGES|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|NO_SONAME|OBJECTS|SONAME)|INTERPROCEDURAL_OPTIMIZATION|LIBRARY_OUTPUT_DIRECTORY|LIBRARY_OUTPUT_NAME|LINK_FLAGS|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_NAME|PDB_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_NAME|STATIC_LIBRARY_FLAGS|VS_CSHARP|VS_DOTNET_REFERENCEPROP|VS_DOTNET_REFERENCE|VS_GLOBAL_SECTION_POST|VS_GLOBAL_SECTION_PRE|VS_GLOBAL|XCODE_ATTRIBUTE)_\w+|\w+_(?:CLANG_TIDY|COMPILER_LAUNCHER|CPPCHECK|CPPLINT|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|ABSTRACT|ADDITIONAL_MAKE_CLEAN_FILES|ADVANCED|ALIASED_TARGET|ALLOW_DUPLICATE_CUSTOM_TARGETS|ANDROID_(?:ANT_ADDITIONAL_OPTIONS|API|API_MIN|ARCH|ASSETS_DIRECTORIES|GUI|JAR_DEPENDENCIES|NATIVE_LIB_DEPENDENCIES|NATIVE_LIB_DIRECTORIES|PROCESS_MAX|PROGUARD|PROGUARD_CONFIG_PATH|SECURE_PROPS_PATH|SKIP_ANT_STEP|STL_TYPE)|ARCHIVE_OUTPUT_DIRECTORY|ATTACHED_FILES|ATTACHED_FILES_ON_FAIL|AUTOGEN_(?:BUILD_DIR|ORIGIN_DEPENDS|PARALLEL|SOURCE_GROUP|TARGETS_FOLDER|TARGET_DEPENDS)|AUTOMOC|AUTOMOC_(?:COMPILER_PREDEFINES|DEPEND_FILTERS|EXECUTABLE|MACRO_NAMES|MOC_OPTIONS|SOURCE_GROUP|TARGETS_FOLDER)|AUTORCC|AUTORCC_EXECUTABLE|AUTORCC_OPTIONS|AUTORCC_SOURCE_GROUP|AUTOUIC|AUTOUIC_EXECUTABLE|AUTOUIC_OPTIONS|AUTOUIC_SEARCH_PATHS|BINARY_DIR|BUILDSYSTEM_TARGETS|BUILD_RPATH|BUILD_RPATH_USE_ORIGIN|BUILD_WITH_INSTALL_NAME_DIR|BUILD_WITH_INSTALL_RPATH|BUNDLE|BUNDLE_EXTENSION|CACHE_VARIABLES|CLEAN_NO_CUSTOM|COMMON_LANGUAGE_RUNTIME|COMPATIBLE_INTERFACE_(?:BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|COMPILE_(?:DEFINITIONS|FEATURES|FLAGS|OPTIONS|PDB_NAME|PDB_OUTPUT_DIRECTORY)|COST|CPACK_DESKTOP_SHORTCUTS|CPACK_NEVER_OVERWRITE|CPACK_PERMANENT|CPACK_STARTUP_SHORTCUTS|CPACK_START_MENU_SHORTCUTS|CPACK_WIX_ACL|CROSSCOMPILING_EMULATOR|CUDA_EXTENSIONS|CUDA_PTX_COMPILATION|CUDA_RESOLVE_DEVICE_SYMBOLS|CUDA_SEPARABLE_COMPILATION|CUDA_STANDARD|CUDA_STANDARD_REQUIRED|CXX_EXTENSIONS|CXX_STANDARD|CXX_STANDARD_REQUIRED|C_EXTENSIONS|C_STANDARD|C_STANDARD_REQUIRED|DEBUG_CONFIGURATIONS|DEFINE_SYMBOL|DEFINITIONS|DEPENDS|DEPLOYMENT_ADDITIONAL_FILES|DEPLOYMENT_REMOTE_DIRECTORY|DISABLED|DISABLED_FEATURES|ECLIPSE_EXTRA_CPROJECT_CONTENTS|ECLIPSE_EXTRA_NATURES|ENABLED_FEATURES|ENABLED_LANGUAGES|ENABLE_EXPORTS|ENVIRONMENT|EXCLUDE_FROM_ALL|EXCLUDE_FROM_DEFAULT_BUILD|EXPORT_NAME|EXPORT_PROPERTIES|EXTERNAL_OBJECT|EchoString|FAIL_REGULAR_EXPRESSION|FIND_LIBRARY_USE_LIB32_PATHS|FIND_LIBRARY_USE_LIB64_PATHS|FIND_LIBRARY_USE_LIBX32_PATHS|FIND_LIBRARY_USE_OPENBSD_VERSIONING|FIXTURES_CLEANUP|FIXTURES_REQUIRED|FIXTURES_SETUP|FOLDER|FRAMEWORK|Fortran_FORMAT|Fortran_MODULE_DIRECTORY|GENERATED|GENERATOR_FILE_NAME|GENERATOR_IS_MULTI_CONFIG|GHS_INTEGRITY_APP|GHS_NO_SOURCE_GROUP_FILE|GLOBAL_DEPENDS_DEBUG_MODE|GLOBAL_DEPENDS_NO_CYCLES|GNUtoMS|HAS_CXX|HEADER_FILE_ONLY|HELPSTRING|IMPLICIT_DEPENDS_INCLUDE_TRANSFORM|IMPORTED|IMPORTED_(?:COMMON_LANGUAGE_RUNTIME|CONFIGURATIONS|GLOBAL|IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_(?:LANGUAGES|LIBRARIES|MULTIPLICITY)|LOCATION|NO_SONAME|OBJECTS|SONAME)|IMPORT_PREFIX|IMPORT_SUFFIX|INCLUDE_DIRECTORIES|INCLUDE_REGULAR_EXPRESSION|INSTALL_NAME_DIR|INSTALL_RPATH|INSTALL_RPATH_USE_LINK_PATH|INTERFACE_(?:AUTOUIC_OPTIONS|COMPILE_DEFINITIONS|COMPILE_FEATURES|COMPILE_OPTIONS|INCLUDE_DIRECTORIES|LINK_DEPENDS|LINK_DIRECTORIES|LINK_LIBRARIES|LINK_OPTIONS|POSITION_INDEPENDENT_CODE|SOURCES|SYSTEM_INCLUDE_DIRECTORIES)|INTERPROCEDURAL_OPTIMIZATION|IN_TRY_COMPILE|IOS_INSTALL_COMBINED|JOB_POOLS|JOB_POOL_COMPILE|JOB_POOL_LINK|KEEP_EXTENSION|LABELS|LANGUAGE|LIBRARY_OUTPUT_DIRECTORY|LINKER_LANGUAGE|LINK_(?:DEPENDS|DEPENDS_NO_SHARED|DIRECTORIES|FLAGS|INTERFACE_LIBRARIES|INTERFACE_MULTIPLICITY|LIBRARIES|OPTIONS|SEARCH_END_STATIC|SEARCH_START_STATIC|WHAT_YOU_USE)|LISTFILE_STACK|LOCATION|MACOSX_BUNDLE|MACOSX_BUNDLE_INFO_PLIST|MACOSX_FRAMEWORK_INFO_PLIST|MACOSX_PACKAGE_LOCATION|MACOSX_RPATH|MACROS|MANUALLY_ADDED_DEPENDENCIES|MEASUREMENT|MODIFIED|NAME|NO_SONAME|NO_SYSTEM_FROM_IMPORTED|OBJECT_DEPENDS|OBJECT_OUTPUTS|OSX_ARCHITECTURES|OUTPUT_NAME|PACKAGES_FOUND|PACKAGES_NOT_FOUND|PARENT_DIRECTORY|PASS_REGULAR_EXPRESSION|PDB_NAME|PDB_OUTPUT_DIRECTORY|POSITION_INDEPENDENT_CODE|POST_INSTALL_SCRIPT|PREDEFINED_TARGETS_FOLDER|PREFIX|PRE_INSTALL_SCRIPT|PRIVATE_HEADER|PROCESSORS|PROCESSOR_AFFINITY|PROJECT_LABEL|PUBLIC_HEADER|REPORT_UNDEFINED_PROPERTIES|REQUIRED_FILES|RESOURCE|RESOURCE_LOCK|RULE_LAUNCH_COMPILE|RULE_LAUNCH_CUSTOM|RULE_LAUNCH_LINK|RULE_MESSAGES|RUNTIME_OUTPUT_DIRECTORY|RUN_SERIAL|SKIP_AUTOGEN|SKIP_AUTOMOC|SKIP_AUTORCC|SKIP_AUTOUIC|SKIP_BUILD_RPATH|SKIP_RETURN_CODE|SOURCES|SOURCE_DIR|SOVERSION|STATIC_LIBRARY_FLAGS|STATIC_LIBRARY_OPTIONS|STRINGS|SUBDIRECTORIES|SUFFIX|SYMBOLIC|TARGET_ARCHIVES_MAY_BE_SHARED_LIBS|TARGET_MESSAGES|TARGET_SUPPORTS_SHARED_LIBS|TESTS|TEST_INCLUDE_FILE|TEST_INCLUDE_FILES|TIMEOUT|TIMEOUT_AFTER_MATCH|TYPE|USE_FOLDERS|VALUE|VARIABLES|VERSION|VISIBILITY_INLINES_HIDDEN|VS_(?:CONFIGURATION_TYPE|COPY_TO_OUT_DIR|DEBUGGER_(?:COMMAND|COMMAND_ARGUMENTS|ENVIRONMENT|WORKING_DIRECTORY)|DEPLOYMENT_CONTENT|DEPLOYMENT_LOCATION|DOTNET_REFERENCES|DOTNET_REFERENCES_COPY_LOCAL|INCLUDE_IN_VSIX|IOT_STARTUP_TASK|KEYWORD|RESOURCE_GENERATOR|SCC_AUXPATH|SCC_LOCALPATH|SCC_PROJECTNAME|SCC_PROVIDER|SDK_REFERENCES|SHADER_(?:DISABLE_OPTIMIZATIONS|ENABLE_DEBUG|ENTRYPOINT|FLAGS|MODEL|OBJECT_FILE_NAME|OUTPUT_HEADER_FILE|TYPE|VARIABLE_NAME)|STARTUP_PROJECT|TOOL_OVERRIDE|USER_PROPS|WINRT_COMPONENT|WINRT_EXTENSIONS|WINRT_REFERENCES|XAML_TYPE)|WILL_FAIL|WIN32_EXECUTABLE|WINDOWS_EXPORT_ALL_SYMBOLS|WORKING_DIRECTORY|WRAP_EXCLUDE|XCODE_(?:EMIT_EFFECTIVE_PLATFORM_NAME|EXPLICIT_FILE_TYPE|FILE_ATTRIBUTES|LAST_KNOWN_FILE_TYPE|PRODUCT_TYPE|SCHEME_(?:ADDRESS_SANITIZER|ADDRESS_SANITIZER_USE_AFTER_RETURN|ARGUMENTS|DISABLE_MAIN_THREAD_CHECKER|DYNAMIC_LIBRARY_LOADS|DYNAMIC_LINKER_API_USAGE|ENVIRONMENT|EXECUTABLE|GUARD_MALLOC|MAIN_THREAD_CHECKER_STOP|MALLOC_GUARD_EDGES|MALLOC_SCRIBBLE|MALLOC_STACK|THREAD_SANITIZER(?:_STOP)?|UNDEFINED_BEHAVIOUR_SANITIZER(?:_STOP)?|ZOMBIE_OBJECTS))|XCTEST)\b/,
2306+ keyword:
2307+ /\b(?:add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|break|build_command|build_name|cmake_host_system_information|cmake_minimum_required|cmake_parse_arguments|cmake_policy|configure_file|continue|create_test_sourcelist|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload|define_property|else|elseif|enable_language|enable_testing|endforeach|endfunction|endif|endmacro|endwhile|exec_program|execute_process|export|export_library_dependencies|file|find_file|find_library|find_package|find_path|find_program|fltk_wrap_ui|foreach|function|get_cmake_property|get_directory_property|get_filename_component|get_property|get_source_file_property|get_target_property|get_test_property|if|include|include_directories|include_external_msproject|include_guard|include_regular_expression|install|install_files|install_programs|install_targets|link_directories|link_libraries|list|load_cache|load_command|macro|make_directory|mark_as_advanced|math|message|option|output_required_files|project|qt_wrap_cpp|qt_wrap_ui|remove|remove_definitions|return|separate_arguments|set|set_directory_properties|set_property|set_source_files_properties|set_target_properties|set_tests_properties|site_name|source_group|string|subdir_depends|subdirs|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_sources|try_compile|try_run|unset|use_mangled_mesa|utility_source|variable_requires|variable_watch|while|write_file)(?=\s*\()\b/,
2308+ boolean: /\b(?:FALSE|OFF|ON|TRUE)\b/,
2309+ namespace:
2310+ /\b(?:INTERFACE|PRIVATE|PROPERTIES|PUBLIC|SHARED|STATIC|TARGET_OBJECTS)\b/,
2311+ operator:
2312+ /\b(?:AND|DEFINED|EQUAL|GREATER|LESS|MATCHES|NOT|OR|STREQUAL|STRGREATER|STRLESS|VERSION_EQUAL|VERSION_GREATER|VERSION_LESS)\b/,
2313+ inserted: {
2314+ pattern: /\b\w+::\w+\b/,
2315+ alias: "class-name",
2316+ },
2317+ number: /\b\d+(?:\.\d+)*\b/,
2318+ function: /\b[a-z_]\w*(?=\s*\()\b/i,
2319+ punctuation: /[()>}]|\$[<{]/,
21692320 };
21702321
21712322 (function (Prism) {
2172−
2173− // Ignore comments starting with { to privilege string interpolation highlighting
2174− var comment = /#(?!\{).+/;
2175− var interpolation = {
2176− pattern: /#\{[^}]+\}/,
2177− alias: 'variable'
2178− };
2179−
2180− Prism.languages.coffeescript = Prism.languages.extend('javascript', {
2181− 'comment': comment,
2182− 'string': [
2183−
2184− // Strings are multiline
2185− {
2186− pattern: /'(?:\\[\s\S]|[^\\'])*'/,
2187− greedy: true
2188− },
2189−
2190− {
2191− // Strings are multiline
2192− pattern: /"(?:\\[\s\S]|[^\\"])*"/,
2193− greedy: true,
2194− inside: {
2195− 'interpolation': interpolation
2196− }
2197− }
2198− ],
2199− 'keyword': /\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,
2200− 'class-member': {
2201− pattern: /@(?!\d)\w+/,
2202− alias: 'variable'
2203− }
2204− });
2205−
2206− Prism.languages.insertBefore('coffeescript', 'comment', {
2207− 'multiline-comment': {
2208− pattern: /###[\s\S]+?###/,
2209− alias: 'comment'
2210− },
2211−
2212− // Block regexp can contain comments and interpolation
2213− 'block-regex': {
2214− pattern: /\/{3}[\s\S]*?\/{3}/,
2215− alias: 'regex',
2216− inside: {
2217− 'comment': comment,
2218− 'interpolation': interpolation
2219− }
2220− }
2221− });
2222−
2223− Prism.languages.insertBefore('coffeescript', 'string', {
2224− 'inline-javascript': {
2225− pattern: /`(?:\\[\s\S]|[^\\`])*`/,
2226− inside: {
2227− 'delimiter': {
2228− pattern: /^`|`$/,
2229− alias: 'punctuation'
2230− },
2231− 'script': {
2232− pattern: /[\s\S]+/,
2233− alias: 'language-javascript',
2234− inside: Prism.languages.javascript
2235− }
2236− }
2237− },
2238−
2239− // Block strings
2240− 'multiline-string': [
2241− {
2242− pattern: /'''[\s\S]*?'''/,
2243− greedy: true,
2244− alias: 'string'
2245− },
2246− {
2247− pattern: /"""[\s\S]*?"""/,
2248− greedy: true,
2249− alias: 'string',
2250− inside: {
2251− interpolation: interpolation
2252− }
2253− }
2254− ]
2255−
2256− });
2257−
2258− Prism.languages.insertBefore('coffeescript', 'keyword', {
2259− // Object property
2260− 'property': /(?!\d)\w+(?=\s*:(?!:))/
2261− });
2262−
2263− delete Prism.languages.coffeescript['template-string'];
2264−
2265− Prism.languages.coffee = Prism.languages.coffeescript;
2266−}(Prism));
2323+ // Ignore comments starting with { to privilege string interpolation highlighting
2324+ var comment = /#(?!\{).+/;
2325+ var interpolation = {
2326+ pattern: /#\{[^}]+\}/,
2327+ alias: "variable",
2328+ };
2329+
2330+ Prism.languages.coffeescript = Prism.languages.extend("javascript", {
2331+ comment: comment,
2332+ string: [
2333+ // Strings are multiline
2334+ {
2335+ pattern: /'(?:\\[\s\S]|[^\\'])*'/,
2336+ greedy: true,
2337+ },
2338+
2339+ {
2340+ // Strings are multiline
2341+ pattern: /"(?:\\[\s\S]|[^\\"])*"/,
2342+ greedy: true,
2343+ inside: {
2344+ interpolation: interpolation,
2345+ },
2346+ },
2347+ ],
2348+ keyword:
2349+ /\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,
2350+ "class-member": {
2351+ pattern: /@(?!\d)\w+/,
2352+ alias: "variable",
2353+ },
2354+ });
2355+
2356+ Prism.languages.insertBefore("coffeescript", "comment", {
2357+ "multiline-comment": {
2358+ pattern: /###[\s\S]+?###/,
2359+ alias: "comment",
2360+ },
2361+
2362+ // Block regexp can contain comments and interpolation
2363+ "block-regex": {
2364+ pattern: /\/{3}[\s\S]*?\/{3}/,
2365+ alias: "regex",
2366+ inside: {
2367+ comment: comment,
2368+ interpolation: interpolation,
2369+ },
2370+ },
2371+ });
2372+
2373+ Prism.languages.insertBefore("coffeescript", "string", {
2374+ "inline-javascript": {
2375+ pattern: /`(?:\\[\s\S]|[^\\`])*`/,
2376+ inside: {
2377+ delimiter: {
2378+ pattern: /^`|`$/,
2379+ alias: "punctuation",
2380+ },
2381+ script: {
2382+ pattern: /[\s\S]+/,
2383+ alias: "language-javascript",
2384+ inside: Prism.languages.javascript,
2385+ },
2386+ },
2387+ },
2388+
2389+ // Block strings
2390+ "multiline-string": [
2391+ {
2392+ pattern: /'''[\s\S]*?'''/,
2393+ greedy: true,
2394+ alias: "string",
2395+ },
2396+ {
2397+ pattern: /"""[\s\S]*?"""/,
2398+ greedy: true,
2399+ alias: "string",
2400+ inside: {
2401+ interpolation: interpolation,
2402+ },
2403+ },
2404+ ],
2405+ });
2406+
2407+ Prism.languages.insertBefore("coffeescript", "keyword", {
2408+ // Object property
2409+ property: /(?!\d)\w+(?=\s*:(?!:))/,
2410+ });
2411+
2412+ delete Prism.languages.coffeescript["template-string"];
2413+
2414+ Prism.languages.coffee = Prism.languages.coffeescript;
2415+})(Prism);
22672416
22682417 /**
22692418 * Original by Samuel Flores
@@ -2272,1833 +2421,1968 @@ Prism.languages.cmake = { · 2 unmodified lines
22722421 * constant, builtin, variable, symbol, regex
22732422 */
22742423 (function (Prism) {
2275− Prism.languages.ruby = Prism.languages.extend('clike', {
2276− 'comment': {
2277− pattern: /#.*|^=begin\s[\s\S]*?^=end/m,
2278− greedy: true
2279− },
2280− 'class-name': {
2281− pattern: /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,
2282− lookbehind: true,
2283− inside: {
2284− 'punctuation': /[.\\]/
2285− }
2286− },
2287− 'keyword': /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,
2288− 'operator': /\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,
2289− 'punctuation': /[(){}[\].,;]/,
2290− });
2291−
2292− Prism.languages.insertBefore('ruby', 'operator', {
2293− 'double-colon': {
2294− pattern: /::/,
2295− alias: 'punctuation'
2296− },
2297− });
2298−
2299− var interpolation = {
2300− pattern: /((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,
2301− lookbehind: true,
2302− inside: {
2303− 'content': {
2304− pattern: /^(#\{)[\s\S]+(?=\}$)/,
2305− lookbehind: true,
2306− inside: Prism.languages.ruby
2307− },
2308− 'delimiter': {
2309− pattern: /^#\{|\}$/,
2310− alias: 'punctuation'
2311− }
2312− }
2313− };
2314−
2315− delete Prism.languages.ruby.function;
2316−
2317− var percentExpression = '(?:' + [
2318− /([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,
2319− /\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,
2320− /\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,
2321− /\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,
2322− /<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source
2323− ].join('|') + ')';
2324−
2325− var symbolName = /(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source;
2326−
2327− Prism.languages.insertBefore('ruby', 'keyword', {
2328− 'regex-literal': [
2329− {
2330− pattern: RegExp(/%r/.source + percentExpression + /[egimnosux]{0,6}/.source),
2331− greedy: true,
2332− inside: {
2333− 'interpolation': interpolation,
2334− 'regex': /[\s\S]+/
2335− }
2336− },
2337− {
2338− pattern: /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,
2339− lookbehind: true,
2340− greedy: true,
2341− inside: {
2342− 'interpolation': interpolation,
2343− 'regex': /[\s\S]+/
2344− }
2345− }
2346− ],
2347− 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,
2348− 'symbol': [
2349− {
2350− pattern: RegExp(/(^|[^:]):/.source + symbolName),
2351− lookbehind: true,
2352− greedy: true
2353− },
2354− {
2355− pattern: RegExp(/([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source),
2356− lookbehind: true,
2357− greedy: true
2358− },
2359− ],
2360− 'method-definition': {
2361− pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,
2362− lookbehind: true,
2363− inside: {
2364− 'function': /\b\w+$/,
2365− 'keyword': /^self\b/,
2366− 'class-name': /^\w+/,
2367− 'punctuation': /\./
2368− }
2369− }
2370− });
2371−
2372− Prism.languages.insertBefore('ruby', 'string', {
2373− 'string-literal': [
2374− {
2375− pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression),
2376− greedy: true,
2377− inside: {
2378− 'interpolation': interpolation,
2379− 'string': /[\s\S]+/
2380− }
2381− },
2382− {
2383− pattern: /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,
2384− greedy: true,
2385− inside: {
2386− 'interpolation': interpolation,
2387− 'string': /[\s\S]+/
2388− }
2389− },
2390− {
2391− pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
2392− alias: 'heredoc-string',
2393− greedy: true,
2394− inside: {
2395− 'delimiter': {
2396− pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,
2397− inside: {
2398− 'symbol': /\b\w+/,
2399− 'punctuation': /^<<[-~]?/
2400− }
2401− },
2402− 'interpolation': interpolation,
2403− 'string': /[\s\S]+/
2404− }
2405− },
2406− {
2407− pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
2408− alias: 'heredoc-string',
2409− greedy: true,
2410− inside: {
2411− 'delimiter': {
2412− pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,
2413− inside: {
2414− 'symbol': /\b\w+/,
2415− 'punctuation': /^<<[-~]?'|'$/,
2416− }
2417− },
2418− 'string': /[\s\S]+/
2419− }
2420− }
2421− ],
2422− 'command-literal': [
2423− {
2424− pattern: RegExp(/%x/.source + percentExpression),
2425− greedy: true,
2426− inside: {
2427− 'interpolation': interpolation,
2428− 'command': {
2429− pattern: /[\s\S]+/,
2430− alias: 'string'
2431− }
2432− }
2433− },
2434− {
2435− pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,
2436− greedy: true,
2437− inside: {
2438− 'interpolation': interpolation,
2439− 'command': {
2440− pattern: /[\s\S]+/,
2441− alias: 'string'
2442− }
2443− }
2444− }
2445− ]
2446− });
2447−
2448− delete Prism.languages.ruby.string;
2449−
2450− Prism.languages.insertBefore('ruby', 'number', {
2451− 'builtin': /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,
2452− 'constant': /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/
2453− });
2454−
2455− Prism.languages.rb = Prism.languages.ruby;
2456−}(Prism));
2424+ Prism.languages.ruby = Prism.languages.extend("clike", {
2425+ comment: {
2426+ pattern: /#.*|^=begin\s[\s\S]*?^=end/m,
2427+ greedy: true,
2428+ },
2429+ "class-name": {
2430+ pattern:
2431+ /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,
2432+ lookbehind: true,
2433+ inside: {
2434+ punctuation: /[.\\]/,
2435+ },
2436+ },
2437+ keyword:
2438+ /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,
2439+ operator:
2440+ /\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,
2441+ punctuation: /[(){}[\].,;]/,
2442+ });
2443+
2444+ Prism.languages.insertBefore("ruby", "operator", {
2445+ "double-colon": {
2446+ pattern: /::/,
2447+ alias: "punctuation",
2448+ },
2449+ });
2450+
2451+ var interpolation = {
2452+ pattern: /((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,
2453+ lookbehind: true,
2454+ inside: {
2455+ content: {
2456+ pattern: /^(#\{)[\s\S]+(?=\}$)/,
2457+ lookbehind: true,
2458+ inside: Prism.languages.ruby,
2459+ },
2460+ delimiter: {
2461+ pattern: /^#\{|\}$/,
2462+ alias: "punctuation",
2463+ },
2464+ },
2465+ };
2466+
2467+ delete Prism.languages.ruby.function;
2468+
2469+ var percentExpression =
2470+ "(?:" +
2471+ [
2472+ /([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,
2473+ /\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,
2474+ /\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,
2475+ /\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,
2476+ /<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source,
2477+ ].join("|") +
2478+ ")";
2479+
2480+ var symbolName =
2481+ /(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/
2482+ .source;
2483+
2484+ Prism.languages.insertBefore("ruby", "keyword", {
2485+ "regex-literal": [
2486+ {
2487+ pattern: RegExp(
2488+ /%r/.source + percentExpression + /[egimnosux]{0,6}/.source,
2489+ ),
2490+ greedy: true,
2491+ inside: {
2492+ interpolation: interpolation,
2493+ regex: /[\s\S]+/,
2494+ },
2495+ },
2496+ {
2497+ pattern:
2498+ /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,
2499+ lookbehind: true,
2500+ greedy: true,
2501+ inside: {
2502+ interpolation: interpolation,
2503+ regex: /[\s\S]+/,
2504+ },
2505+ },
2506+ ],
2507+ variable: /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,
2508+ symbol: [
2509+ {
2510+ pattern: RegExp(/(^|[^:]):/.source + symbolName),
2511+ lookbehind: true,
2512+ greedy: true,
2513+ },
2514+ {
2515+ pattern: RegExp(
2516+ /([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source,
2517+ ),
2518+ lookbehind: true,
2519+ greedy: true,
2520+ },
2521+ ],
2522+ "method-definition": {
2523+ pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,
2524+ lookbehind: true,
2525+ inside: {
2526+ function: /\b\w+$/,
2527+ keyword: /^self\b/,
2528+ "class-name": /^\w+/,
2529+ punctuation: /\./,
2530+ },
2531+ },
2532+ });
2533+
2534+ Prism.languages.insertBefore("ruby", "string", {
2535+ "string-literal": [
2536+ {
2537+ pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression),
2538+ greedy: true,
2539+ inside: {
2540+ interpolation: interpolation,
2541+ string: /[\s\S]+/,
2542+ },
2543+ },
2544+ {
2545+ pattern:
2546+ /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,
2547+ greedy: true,
2548+ inside: {
2549+ interpolation: interpolation,
2550+ string: /[\s\S]+/,
2551+ },
2552+ },
2553+ {
2554+ pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
2555+ alias: "heredoc-string",
2556+ greedy: true,
2557+ inside: {
2558+ delimiter: {
2559+ pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,
2560+ inside: {
2561+ symbol: /\b\w+/,
2562+ punctuation: /^<<[-~]?/,
2563+ },
2564+ },
2565+ interpolation: interpolation,
2566+ string: /[\s\S]+/,
2567+ },
2568+ },
2569+ {
2570+ pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
2571+ alias: "heredoc-string",
2572+ greedy: true,
2573+ inside: {
2574+ delimiter: {
2575+ pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,
2576+ inside: {
2577+ symbol: /\b\w+/,
2578+ punctuation: /^<<[-~]?'|'$/,
2579+ },
2580+ },
2581+ string: /[\s\S]+/,
2582+ },
2583+ },
2584+ ],
2585+ "command-literal": [
2586+ {
2587+ pattern: RegExp(/%x/.source + percentExpression),
2588+ greedy: true,
2589+ inside: {
2590+ interpolation: interpolation,
2591+ command: {
2592+ pattern: /[\s\S]+/,
2593+ alias: "string",
2594+ },
2595+ },
2596+ },
2597+ {
2598+ pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,
2599+ greedy: true,
2600+ inside: {
2601+ interpolation: interpolation,
2602+ command: {
2603+ pattern: /[\s\S]+/,
2604+ alias: "string",
2605+ },
2606+ },
2607+ },
2608+ ],
2609+ });
2610+
2611+ delete Prism.languages.ruby.string;
2612+
2613+ Prism.languages.insertBefore("ruby", "number", {
2614+ builtin:
2615+ /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,
2616+ constant: /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/,
2617+ });
2618+
2619+ Prism.languages.rb = Prism.languages.ruby;
2620+})(Prism);
24572621
24582622 (function (Prism) {
2459− Prism.languages.crystal = Prism.languages.extend('ruby', {
2460− 'keyword': [
2461− /\b(?:__DIR__|__END_LINE__|__FILE__|__LINE__|abstract|alias|annotation|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|ifdef|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|ptr|require|rescue|return|select|self|sizeof|struct|super|then|type|typeof|undef|uninitialized|union|unless|until|when|while|with|yield)\b/,
2462− {
2463− pattern: /(\.\s*)(?:is_a|responds_to)\?/,
2464− lookbehind: true
2465− }
2466− ],
2467− 'number': /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[\da-fA-F_]*[\da-fA-F]|(?:\d(?:[\d_]*\d)?)(?:\.[\d_]*\d)?(?:[eE][+-]?[\d_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/,
2468− 'operator': [
2469− /->/,
2470− Prism.languages.ruby.operator,
2471− ],
2472− 'punctuation': /[(){}[\].,;\\]/,
2473− });
2474−
2475− Prism.languages.insertBefore('crystal', 'string-literal', {
2476− 'attribute': {
2477− pattern: /@\[.*?\]/,
2478− inside: {
2479− 'delimiter': {
2480− pattern: /^@\[|\]$/,
2481− alias: 'punctuation'
2482− },
2483− 'attribute': {
2484− pattern: /^(\s*)\w+/,
2485− lookbehind: true,
2486− alias: 'class-name'
2487− },
2488− 'args': {
2489− pattern: /\S(?:[\s\S]*\S)?/,
2490− inside: Prism.languages.crystal
2491− },
2492− }
2493− },
2494− 'expansion': {
2495− pattern: /\{(?:\{.*?\}|%.*?%)\}/,
2496− inside: {
2497− 'content': {
2498− pattern: /^(\{.)[\s\S]+(?=.\}$)/,
2499− lookbehind: true,
2500− inside: Prism.languages.crystal
2501− },
2502− 'delimiter': {
2503− pattern: /^\{[\{%]|[\}%]\}$/,
2504− alias: 'operator'
2505− }
2506− }
2507− },
2508− 'char': {
2509− pattern: /'(?:[^\\\r\n]{1,2}|\\(?:.|u(?:[A-Fa-f0-9]{1,4}|\{[A-Fa-f0-9]{1,6}\})))'/,
2510− greedy: true
2511− }
2512− });
2513−
2514−}(Prism));
2515−
2516−Prism.languages.d = Prism.languages.extend('clike', {
2517− 'comment': [
2518− {
2519− // Shebang
2520− pattern: /^\s*#!.+/,
2521− greedy: true
2522− },
2523− {
2524− pattern: RegExp(/(^|[^\\])/.source + '(?:' + [
2525− // /+ comment +/
2526− // Allow one level of nesting
2527− /\/\+(?:\/\+(?:[^+]|\+(?!\/))*\+\/|(?!\/\+)[\s\S])*?\+\//.source,
2528− // // comment
2529− /\/\/.*/.source,
2530− // /* comment */
2531− /\/\*[\s\S]*?\*\//.source
2532− ].join('|') + ')'),
2533− lookbehind: true,
2534− greedy: true
2535− }
2536− ],
2537− 'string': [
2538− {
2539− pattern: RegExp([
2540− // r"", x""
2541− /\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/.source,
2542−
2543− // q"[]", q"()", q"<>", q"{}"
2544− /\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/.source,
2545−
2546− // q"IDENT
2547− // ...
2548− // IDENT"
2549− /\bq"((?!\d)\w+)$[\s\S]*?^\1"/.source,
2550−
2551− // q"//", q"||", etc.
2552− // eslint-disable-next-line regexp/strict
2553− /\bq"(.)[\s\S]*?\2"/.source,
2554−

6302 more lines aren’t shown here.

assets/javascripts/vendor/raven.js+3290−2911
@@ -10,2457 +10,2776 @@ · 9 unmodified lines
1010 *
1111 */
1212
13−(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Raven = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
14−function RavenConfigError(message) {
15− this.name = 'RavenConfigError';
16− this.message = message;
17−}
18−RavenConfigError.prototype = new Error();
19−RavenConfigError.prototype.constructor = RavenConfigError;
20−
21−module.exports = RavenConfigError;
22−
23−},{}],2:[function(_dereq_,module,exports){
24−var wrapMethod = function(console, level, callback) {
25− var originalConsoleLevel = console[level];
26− var originalConsole = console;
27−
28− if (!(level in console)) {
29− return;
13+(function (f) {
14+ if (typeof exports === "object" && typeof module !== "undefined") {
15+ module.exports = f();
16+ } else if (typeof define === "function" && define.amd) {
17+ define([], f);
18+ } else {
19+ var g;
20+ if (typeof window !== "undefined") {
21+ g = window;
22+ } else if (typeof global !== "undefined") {
23+ g = global;
24+ } else if (typeof self !== "undefined") {
25+ g = self;
26+ } else {
27+ g = this;
28+ }
29+ g.Raven = f();
3030 }
31+})(function () {
32+ var define, module, exports;
33+ return (function e(t, n, r) {
34+ function s(o, u) {
35+ if (!n[o]) {
36+ if (!t[o]) {
37+ var a = typeof require == "function" && require;
38+ if (!u && a) return a(o, !0);
39+ if (i) return i(o, !0);
40+ var f = new Error("Cannot find module '" + o + "'");
41+ throw ((f.code = "MODULE_NOT_FOUND"), f);
42+ }
43+ var l = (n[o] = { exports: {} });
44+ t[o][0].call(
45+ l.exports,
46+ function (e) {
47+ var n = t[o][1][e];
48+ return s(n ? n : e);
49+ },
50+ l,
51+ l.exports,
52+ e,
53+ t,
54+ n,
55+ r,
56+ );
57+ }
58+ return n[o].exports;
59+ }
60+ var i = typeof require == "function" && require;
61+ for (var o = 0; o < r.length; o++) s(r[o]);
62+ return s;
63+ })(
64+ {
65+ 1: [
66+ function (_dereq_, module, exports) {
67+ function RavenConfigError(message) {
68+ this.name = "RavenConfigError";
69+ this.message = message;
70+ }
71+ RavenConfigError.prototype = new Error();
72+ RavenConfigError.prototype.constructor = RavenConfigError;
3173
32− var sentryLevel = level === 'warn' ? 'warning' : level;
74+ module.exports = RavenConfigError;
75+ },
76+ {},
77+ ],
78+ 2: [
79+ function (_dereq_, module, exports) {
80+ var wrapMethod = function (console, level, callback) {
81+ var originalConsoleLevel = console[level];
82+ var originalConsole = console;
83+
84+ if (!(level in console)) {
85+ return;
86+ }
3387
34− console[level] = function() {
35− var args = [].slice.call(arguments);
88+ var sentryLevel = level === "warn" ? "warning" : level;
3689
37− var msg = '' + args.join(' ');
38− var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}};
90+ console[level] = function () {
91+ var args = [].slice.call(arguments);
3992
40− if (level === 'assert') {
41− if (args[0] === false) {
42− // Default browsers message
43− msg = 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert');
44− data.extra.arguments = args.slice(1);
45− callback && callback(msg, data);
46− }
47− } else {
48− callback && callback(msg, data);
49− }
93+ var msg = "" + args.join(" ");
94+ var data = {
95+ level: sentryLevel,
96+ logger: "console",
97+ extra: { arguments: args },
98+ };
5099
51− // this fails for some browsers. :(
52− if (originalConsoleLevel) {
53− // IE9 doesn't allow calling apply on console functions directly
54− // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
55− Function.prototype.apply.call(originalConsoleLevel, originalConsole, args);
56− }
57− };
58−};
59−
60−module.exports = {
61− wrapMethod: wrapMethod
62−};
63−
64−},{}],3:[function(_dereq_,module,exports){
65−(function (global){
66−/*global XDomainRequest:false */
67−
68−var TraceKit = _dereq_(6);
69−var stringify = _dereq_(7);
70−var RavenConfigError = _dereq_(1);
71−
72−var utils = _dereq_(5);
73−var isError = utils.isError;
74−var isObject = utils.isObject;
75−var isErrorEvent = utils.isErrorEvent;
76−var isUndefined = utils.isUndefined;
77−var isFunction = utils.isFunction;
78−var isString = utils.isString;
79−var isArray = utils.isArray;
80−var isEmptyObject = utils.isEmptyObject;
81−var each = utils.each;
82−var objectMerge = utils.objectMerge;
83−var truncate = utils.truncate;
84−var objectFrozen = utils.objectFrozen;
85−var hasKey = utils.hasKey;
86−var joinRegExp = utils.joinRegExp;
87−var urlencode = utils.urlencode;
88−var uuid4 = utils.uuid4;
89−var htmlTreeAsString = utils.htmlTreeAsString;
90−var isSameException = utils.isSameException;
91−var isSameStacktrace = utils.isSameStacktrace;
92−var parseUrl = utils.parseUrl;
93−var fill = utils.fill;
94−
95−var wrapConsoleMethod = _dereq_(2).wrapMethod;
96−
97−var dsnKeys = 'source protocol user pass host port path'.split(' '),
98− dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
99−
100−function now() {
101− return +new Date();
102−}
103−
104−// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
105−var _window =
106− typeof window !== 'undefined'
107− ? window
108− : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
109−var _document = _window.document;
110−var _navigator = _window.navigator;
111−
112−function keepOriginalCallback(original, callback) {
113− return isFunction(callback)
114− ? function(data) {
115− return callback(data, original);
116− }
117− : callback;
118−}
119−
120−// First, check for JSON support
121−// If there is no JSON, we no-op the core features of Raven
122−// since JSON is required to encode the payload
123−function Raven() {
124− this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
125− // Raven can run in contexts where there's no document (react-native)
126− this._hasDocument = !isUndefined(_document);
127− this._hasNavigator = !isUndefined(_navigator);
128− this._lastCapturedException = null;
129− this._lastData = null;
130− this._lastEventId = null;
131− this._globalServer = null;
132− this._globalKey = null;
133− this._globalProject = null;
134− this._globalContext = {};
135− this._globalOptions = {
136− logger: 'javascript',
137− ignoreErrors: [],
138− ignoreUrls: [],
139− whitelistUrls: [],
140− includePaths: [],
141− collectWindowErrors: true,
142− maxMessageLength: 0,
143−
144− // By default, truncates URL values to 250 chars
145− maxUrlLength: 250,
146− stackTraceLimit: 50,
147− autoBreadcrumbs: true,
148− instrument: true,
149− sampleRate: 1
150− };
151− this._ignoreOnError = 0;
152− this._isRavenInstalled = false;
153− this._originalErrorStackTraceLimit = Error.stackTraceLimit;
154− // capture references to window.console *and* all its methods first
155− // before the console plugin has a chance to monkey patch
156− this._originalConsole = _window.console || {};
157− this._originalConsoleMethods = {};
158− this._plugins = [];
159− this._startTime = now();
160− this._wrappedBuiltIns = [];
161− this._breadcrumbs = [];
162− this._lastCapturedEvent = null;
163− this._keypressTimeout;
164− this._location = _window.location;
165− this._lastHref = this._location && this._location.href;
166− this._resetBackoff();
167−
168− // eslint-disable-next-line guard-for-in
169− for (var method in this._originalConsole) {
170− this._originalConsoleMethods[method] = this._originalConsole[method];
171− }
172−}
100+ if (level === "assert") {
101+ if (args[0] === false) {
102+ // Default browsers message
103+ msg =
104+ "Assertion failed: " +
105+ (args.slice(1).join(" ") || "console.assert");
106+ data.extra.arguments = args.slice(1);
107+ callback && callback(msg, data);
108+ }
109+ } else {
110+ callback && callback(msg, data);
111+ }
173112
174−/*
175− * The core Raven singleton
176− *
177− * @this {Raven}
178− */
113+ // this fails for some browsers. :(
114+ if (originalConsoleLevel) {
115+ // IE9 doesn't allow calling apply on console functions directly
116+ // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
117+ Function.prototype.apply.call(
118+ originalConsoleLevel,
119+ originalConsole,
120+ args,
121+ );
122+ }
123+ };
124+ };
179125
180−Raven.prototype = {
181− // Hardcode version string so that raven source can be loaded directly via
182− // webpack (using a build step causes webpack #1617). Grunt verifies that
183− // this value matches package.json during build.
184− // See: https://github.com/getsentry/raven-js/issues/465
185− VERSION: '3.20.1',
186−
187− debug: false,
188−
189− TraceKit: TraceKit, // alias to TraceKit
190−
191− /*
192− * Configure Raven with a DSN and extra options
193− *
194− * @param {string} dsn The public Sentry DSN
195− * @param {object} options Set of global options [optional]
196− * @return {Raven}
197− */
198− config: function(dsn, options) {
199− var self = this;
200−
201− if (self._globalServer) {
202− this._logDebug('error', 'Error: Raven has already been configured');
203− return self;
204− }
205− if (!dsn) return self;
206−
207− var globalOptions = self._globalOptions;
208−
209− // merge in options
210− if (options) {
211− each(options, function(key, value) {
212− // tags and extra are special and need to be put into context
213− if (key === 'tags' || key === 'extra' || key === 'user') {
214− self._globalContext[key] = value;
215− } else {
216− globalOptions[key] = value;
217− }
218− });
219− }
126+ module.exports = {
127+ wrapMethod: wrapMethod,
128+ };
129+ },
130+ {},
131+ ],
132+ 3: [
133+ function (_dereq_, module, exports) {
134+ (function (global) {
135+ /*global XDomainRequest:false */
136+
137+ var TraceKit = _dereq_(6);
138+ var stringify = _dereq_(7);
139+ var RavenConfigError = _dereq_(1);
140+
141+ var utils = _dereq_(5);
142+ var isError = utils.isError;
143+ var isObject = utils.isObject;
144+ var isErrorEvent = utils.isErrorEvent;
145+ var isUndefined = utils.isUndefined;
146+ var isFunction = utils.isFunction;
147+ var isString = utils.isString;
148+ var isArray = utils.isArray;
149+ var isEmptyObject = utils.isEmptyObject;
150+ var each = utils.each;
151+ var objectMerge = utils.objectMerge;
152+ var truncate = utils.truncate;
153+ var objectFrozen = utils.objectFrozen;
154+ var hasKey = utils.hasKey;
155+ var joinRegExp = utils.joinRegExp;
156+ var urlencode = utils.urlencode;
157+ var uuid4 = utils.uuid4;
158+ var htmlTreeAsString = utils.htmlTreeAsString;
159+ var isSameException = utils.isSameException;
160+ var isSameStacktrace = utils.isSameStacktrace;
161+ var parseUrl = utils.parseUrl;
162+ var fill = utils.fill;
163+
164+ var wrapConsoleMethod = _dereq_(2).wrapMethod;
165+
166+ var dsnKeys = "source protocol user pass host port path".split(" "),
167+ dsnPattern =
168+ /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
169+
170+ function now() {
171+ return +new Date();
172+ }
220173
221− self.setDSN(dsn);
222−
223− // "Script error." is hard coded into browsers for errors that it can't read.
224− // this is the result of a script being pulled in from an external domain and CORS.
225− globalOptions.ignoreErrors.push(/^Script error\.?$/);
226− globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
227−
228− // join regexp rules into one big rule
229− globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
230− globalOptions.ignoreUrls = globalOptions.ignoreUrls.length
231− ? joinRegExp(globalOptions.ignoreUrls)
232− : false;
233− globalOptions.whitelistUrls = globalOptions.whitelistUrls.length
234− ? joinRegExp(globalOptions.whitelistUrls)
235− : false;
236− globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
237− globalOptions.maxBreadcrumbs = Math.max(
238− 0,
239− Math.min(globalOptions.maxBreadcrumbs || 100, 100)
240− ); // default and hard limit is 100
241−
242− var autoBreadcrumbDefaults = {
243− xhr: true,
244− console: true,
245− dom: true,
246− location: true,
247− sentry: true
248− };
249−
250− var autoBreadcrumbs = globalOptions.autoBreadcrumbs;
251− if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {
252− autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);
253− } else if (autoBreadcrumbs !== false) {
254− autoBreadcrumbs = autoBreadcrumbDefaults;
255− }
256− globalOptions.autoBreadcrumbs = autoBreadcrumbs;
174+ // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
175+ var _window =
176+ typeof window !== "undefined"
177+ ? window
178+ : typeof global !== "undefined"
179+ ? global
180+ : typeof self !== "undefined"
181+ ? self
182+ : {};
183+ var _document = _window.document;
184+ var _navigator = _window.navigator;
185+
186+ function keepOriginalCallback(original, callback) {
187+ return isFunction(callback)
188+ ? function (data) {
189+ return callback(data, original);
190+ }
191+ : callback;
192+ }
257193
258− var instrumentDefaults = {
259− tryCatch: true
260− };
194+ // First, check for JSON support
195+ // If there is no JSON, we no-op the core features of Raven
196+ // since JSON is required to encode the payload
197+ function Raven() {
198+ this._hasJSON = !!(typeof JSON === "object" && JSON.stringify);
199+ // Raven can run in contexts where there's no document (react-native)
200+ this._hasDocument = !isUndefined(_document);
201+ this._hasNavigator = !isUndefined(_navigator);
202+ this._lastCapturedException = null;
203+ this._lastData = null;
204+ this._lastEventId = null;
205+ this._globalServer = null;
206+ this._globalKey = null;
207+ this._globalProject = null;
208+ this._globalContext = {};
209+ this._globalOptions = {
210+ logger: "javascript",
211+ ignoreErrors: [],
212+ ignoreUrls: [],
213+ whitelistUrls: [],
214+ includePaths: [],
215+ collectWindowErrors: true,
216+ maxMessageLength: 0,
217+
218+ // By default, truncates URL values to 250 chars
219+ maxUrlLength: 250,
220+ stackTraceLimit: 50,
221+ autoBreadcrumbs: true,
222+ instrument: true,
223+ sampleRate: 1,
224+ };
225+ this._ignoreOnError = 0;
226+ this._isRavenInstalled = false;
227+ this._originalErrorStackTraceLimit = Error.stackTraceLimit;
228+ // capture references to window.console *and* all its methods first
229+ // before the console plugin has a chance to monkey patch
230+ this._originalConsole = _window.console || {};
231+ this._originalConsoleMethods = {};
232+ this._plugins = [];
233+ this._startTime = now();
234+ this._wrappedBuiltIns = [];
235+ this._breadcrumbs = [];
236+ this._lastCapturedEvent = null;
237+ this._keypressTimeout;
238+ this._location = _window.location;
239+ this._lastHref = this._location && this._location.href;
240+ this._resetBackoff();
241+
242+ // eslint-disable-next-line guard-for-in
243+ for (var method in this._originalConsole) {
244+ this._originalConsoleMethods[method] =
245+ this._originalConsole[method];
246+ }
247+ }
261248
262− var instrument = globalOptions.instrument;
263− if ({}.toString.call(instrument) === '[object Object]') {
264− instrument = objectMerge(instrumentDefaults, instrument);
265− } else if (instrument !== false) {
266− instrument = instrumentDefaults;
267− }
268− globalOptions.instrument = instrument;
269−
270− TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
271−
272− // return for chaining
273− return self;
274− },
275−
276− /*
277− * Installs a global window.onerror error handler
278− * to capture and report uncaught exceptions.
279− * At this point, install() is required to be called due
280− * to the way TraceKit is set up.
281− *
282− * @return {Raven}
283− */
284− install: function() {
285− var self = this;
286− if (self.isSetup() && !self._isRavenInstalled) {
287− TraceKit.report.subscribe(function() {
288− self._handleOnErrorStackInfo.apply(self, arguments);
289− });
290−
291− self._patchFunctionToString();
292−
293− if (self._globalOptions.instrument && self._globalOptions.instrument.tryCatch) {
294− self._instrumentTryCatch();
295− }
249+ /*
250+ * The core Raven singleton
251+ *
252+ * @this {Raven}
253+ */
254+
255+ Raven.prototype = {
256+ // Hardcode version string so that raven source can be loaded directly via
257+ // webpack (using a build step causes webpack #1617). Grunt verifies that
258+ // this value matches package.json during build.
259+ // See: https://github.com/getsentry/raven-js/issues/465
260+ VERSION: "3.20.1",
261+
262+ debug: false,
263+
264+ TraceKit: TraceKit, // alias to TraceKit
265+
266+ /*
267+ * Configure Raven with a DSN and extra options
268+ *
269+ * @param {string} dsn The public Sentry DSN
270+ * @param {object} options Set of global options [optional]
271+ * @return {Raven}
272+ */
273+ config: function (dsn, options) {
274+ var self = this;
275+
276+ if (self._globalServer) {
277+ this._logDebug(
278+ "error",
279+ "Error: Raven has already been configured",
280+ );
281+ return self;
282+ }
283+ if (!dsn) return self;
284+
285+ var globalOptions = self._globalOptions;
286+
287+ // merge in options
288+ if (options) {
289+ each(options, function (key, value) {
290+ // tags and extra are special and need to be put into context
291+ if (key === "tags" || key === "extra" || key === "user") {
292+ self._globalContext[key] = value;
293+ } else {
294+ globalOptions[key] = value;
295+ }
296+ });
297+ }
296298
297− if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs();
299+ self.setDSN(dsn);
300+
301+ // "Script error." is hard coded into browsers for errors that it can't read.
302+ // this is the result of a script being pulled in from an external domain and CORS.
303+ globalOptions.ignoreErrors.push(/^Script error\.?$/);
304+ globalOptions.ignoreErrors.push(
305+ /^Javascript error: Script error\.? on line 0$/,
306+ );
307+
308+ // join regexp rules into one big rule
309+ globalOptions.ignoreErrors = joinRegExp(
310+ globalOptions.ignoreErrors,
311+ );
312+ globalOptions.ignoreUrls = globalOptions.ignoreUrls.length
313+ ? joinRegExp(globalOptions.ignoreUrls)
314+ : false;
315+ globalOptions.whitelistUrls = globalOptions.whitelistUrls.length
316+ ? joinRegExp(globalOptions.whitelistUrls)
317+ : false;
318+ globalOptions.includePaths = joinRegExp(
319+ globalOptions.includePaths,
320+ );
321+ globalOptions.maxBreadcrumbs = Math.max(
322+ 0,
323+ Math.min(globalOptions.maxBreadcrumbs || 100, 100),
324+ ); // default and hard limit is 100
325+
326+ var autoBreadcrumbDefaults = {
327+ xhr: true,
328+ console: true,
329+ dom: true,
330+ location: true,
331+ sentry: true,
332+ };
298333
299− // Install all of the plugins
300− self._drainPlugins();
334+ var autoBreadcrumbs = globalOptions.autoBreadcrumbs;
335+ if ({}.toString.call(autoBreadcrumbs) === "[object Object]") {
336+ autoBreadcrumbs = objectMerge(
337+ autoBreadcrumbDefaults,
338+ autoBreadcrumbs,
339+ );
340+ } else if (autoBreadcrumbs !== false) {
341+ autoBreadcrumbs = autoBreadcrumbDefaults;
342+ }
343+ globalOptions.autoBreadcrumbs = autoBreadcrumbs;
301344
302− self._isRavenInstalled = true;
303− }
345+ var instrumentDefaults = {
346+ tryCatch: true,
347+ };
304348
305− Error.stackTraceLimit = self._globalOptions.stackTraceLimit;
306− return this;
307− },
308−
309− /*
310− * Set the DSN (can be called multiple time unlike config)
311− *
312− * @param {string} dsn The public Sentry DSN
313− */
314− setDSN: function(dsn) {
315− var self = this,
316− uri = self._parseDSN(dsn),
317− lastSlash = uri.path.lastIndexOf('/'),
318− path = uri.path.substr(1, lastSlash);
319−
320− self._dsn = dsn;
321− self._globalKey = uri.user;
322− self._globalSecret = uri.pass && uri.pass.substr(1);
323− self._globalProject = uri.path.substr(lastSlash + 1);
324−
325− self._globalServer = self._getGlobalServer(uri);
326−
327− self._globalEndpoint =
328− self._globalServer + '/' + path + 'api/' + self._globalProject + '/store/';
329−
330− // Reset backoff state since we may be pointing at a
331− // new project/server
332− this._resetBackoff();
333− },
334−
335− /*
336− * Wrap code within a context so Raven can capture errors
337− * reliably across domains that is executed immediately.
338− *
339− * @param {object} options A specific set of options for this context [optional]
340− * @param {function} func The callback to be immediately executed within the context
341− * @param {array} args An array of arguments to be called with the callback [optional]
342− */
343− context: function(options, func, args) {
344− if (isFunction(options)) {
345− args = func || [];
346− func = options;
347− options = undefined;
348− }
349+ var instrument = globalOptions.instrument;
350+ if ({}.toString.call(instrument) === "[object Object]") {
351+ instrument = objectMerge(instrumentDefaults, instrument);
352+ } else if (instrument !== false) {
353+ instrument = instrumentDefaults;
354+ }
355+ globalOptions.instrument = instrument;
356+
357+ TraceKit.collectWindowErrors =
358+ !!globalOptions.collectWindowErrors;
359+
360+ // return for chaining
361+ return self;
362+ },
363+
364+ /*
365+ * Installs a global window.onerror error handler
366+ * to capture and report uncaught exceptions.
367+ * At this point, install() is required to be called due
368+ * to the way TraceKit is set up.
369+ *
370+ * @return {Raven}
371+ */
372+ install: function () {
373+ var self = this;
374+ if (self.isSetup() && !self._isRavenInstalled) {
375+ TraceKit.report.subscribe(function () {
376+ self._handleOnErrorStackInfo.apply(self, arguments);
377+ });
378+
379+ self._patchFunctionToString();
380+
381+ if (
382+ self._globalOptions.instrument &&
383+ self._globalOptions.instrument.tryCatch
384+ ) {
385+ self._instrumentTryCatch();
386+ }
349387
350− return this.wrap(options, func).apply(this, args);
351− },
352−
353− /*
354− * Wrap code within a context and returns back a new function to be executed
355− *
356− * @param {object} options A specific set of options for this context [optional]
357− * @param {function} func The function to be wrapped in a new context
358− * @param {function} func A function to call before the try/catch wrapper [optional, private]
359− * @return {function} The newly wrapped functions with a context
360− */
361− wrap: function(options, func, _before) {
362− var self = this;
363− // 1 argument has been passed, and it's not a function
364− // so just return it
365− if (isUndefined(func) && !isFunction(options)) {
366− return options;
367− }
388+ if (self._globalOptions.autoBreadcrumbs)
389+ self._instrumentBreadcrumbs();
368390
369− // options is optional
370− if (isFunction(options)) {
371− func = options;
372− options = undefined;
373− }
391+ // Install all of the plugins
392+ self._drainPlugins();
374393
375− // At this point, we've passed along 2 arguments, and the second one
376− // is not a function either, so we'll just return the second argument.
377− if (!isFunction(func)) {
378− return func;
379− }
394+ self._isRavenInstalled = true;
395+ }
380396
381− // We don't wanna wrap it twice!
382− try {
383− if (func.__raven__) {
384− return func;
385− }
397+ Error.stackTraceLimit = self._globalOptions.stackTraceLimit;
398+ return this;
399+ },
400+
401+ /*
402+ * Set the DSN (can be called multiple time unlike config)
403+ *
404+ * @param {string} dsn The public Sentry DSN
405+ */
406+ setDSN: function (dsn) {
407+ var self = this,
408+ uri = self._parseDSN(dsn),
409+ lastSlash = uri.path.lastIndexOf("/"),
410+ path = uri.path.substr(1, lastSlash);
411+
412+ self._dsn = dsn;
413+ self._globalKey = uri.user;
414+ self._globalSecret = uri.pass && uri.pass.substr(1);
415+ self._globalProject = uri.path.substr(lastSlash + 1);
416+
417+ self._globalServer = self._getGlobalServer(uri);
418+
419+ self._globalEndpoint =
420+ self._globalServer +
421+ "/" +
422+ path +
423+ "api/" +
424+ self._globalProject +
425+ "/store/";
426+
427+ // Reset backoff state since we may be pointing at a
428+ // new project/server
429+ this._resetBackoff();
430+ },
431+
432+ /*
433+ * Wrap code within a context so Raven can capture errors
434+ * reliably across domains that is executed immediately.
435+ *
436+ * @param {object} options A specific set of options for this context [optional]
437+ * @param {function} func The callback to be immediately executed within the context
438+ * @param {array} args An array of arguments to be called with the callback [optional]
439+ */
440+ context: function (options, func, args) {
441+ if (isFunction(options)) {
442+ args = func || [];
443+ func = options;
444+ options = undefined;
445+ }
386446
387− // If this has already been wrapped in the past, return that
388− if (func.__raven_wrapper__) {
389− return func.__raven_wrapper__;
390− }
391− } catch (e) {
392− // Just accessing custom props in some Selenium environments
393− // can cause a "Permission denied" exception (see raven-js#495).
394− // Bail on wrapping and return the function as-is (defers to window.onerror).
395− return func;
396− }
447+ return this.wrap(options, func).apply(this, args);
448+ },
449+
450+ /*
451+ * Wrap code within a context and returns back a new function to be executed
452+ *
453+ * @param {object} options A specific set of options for this context [optional]
454+ * @param {function} func The function to be wrapped in a new context
455+ * @param {function} func A function to call before the try/catch wrapper [optional, private]
456+ * @return {function} The newly wrapped functions with a context
457+ */
458+ wrap: function (options, func, _before) {
459+ var self = this;
460+ // 1 argument has been passed, and it's not a function
461+ // so just return it
462+ if (isUndefined(func) && !isFunction(options)) {
463+ return options;
464+ }
397465
398− function wrapped() {
399− var args = [],
400− i = arguments.length,
401− deep = !options || (options && options.deep !== false);
466+ // options is optional
467+ if (isFunction(options)) {
468+ func = options;
469+ options = undefined;
470+ }
402471
403− if (_before && isFunction(_before)) {
404− _before.apply(this, arguments);
405− }
472+ // At this point, we've passed along 2 arguments, and the second one
473+ // is not a function either, so we'll just return the second argument.
474+ if (!isFunction(func)) {
475+ return func;
476+ }
406477
407− // Recursively wrap all of a function's arguments that are
408− // functions themselves.
409− while (i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
410−
411− try {
412− // Attempt to invoke user-land function
413− // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
414− // means Raven caught an error invoking your application code. This is
415− // expected behavior and NOT indicative of a bug with Raven.js.
416− return func.apply(this, args);
417− } catch (e) {
418− self._ignoreNextOnError();
419− self.captureException(e, options);
420− throw e;
421− }
422− }
478+ // We don't wanna wrap it twice!
479+ try {
480+ if (func.__raven__) {
481+ return func;
482+ }
423483
424− // copy over properties of the old function
425− for (var property in func) {
426− if (hasKey(func, property)) {
427− wrapped[property] = func[property];
428− }
429− }
430− wrapped.prototype = func.prototype;
431−
432− func.__raven_wrapper__ = wrapped;
433− // Signal that this function has been wrapped/filled already
434− // for both debugging and to prevent it to being wrapped/filled twice
435− wrapped.__raven__ = true;
436− wrapped.__orig__ = func;
437−
438− return wrapped;
439− },
440−
441− /*
442− * Uninstalls the global error handler.
443− *
444− * @return {Raven}
445− */
446− uninstall: function() {
447− TraceKit.report.uninstall();
448−
449− this._unpatchFunctionToString();
450− this._restoreBuiltIns();
451−
452− Error.stackTraceLimit = this._originalErrorStackTraceLimit;
453− this._isRavenInstalled = false;
454−
455− return this;
456− },
457−
458− /*
459− * Manually capture an exception and send it over to Sentry
460− *
461− * @param {error} ex An exception to be logged
462− * @param {object} options A specific set of options for this error [optional]
463− * @return {Raven}
464− */
465− captureException: function(ex, options) {
466− // Cases for sending ex as a message, rather than an exception
467− var isNotError = !isError(ex);
468− var isNotErrorEvent = !isErrorEvent(ex);
469− var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error;
470−
471− if ((isNotError && isNotErrorEvent) || isErrorEventWithoutError) {
472− return this.captureMessage(
473− ex,
474− objectMerge(
475− {
476− trimHeadFrames: 1,
477− stacktrace: true // if we fall back to captureMessage, default to attempting a new trace
478− },
479− options
480− )
481− );
482− }
484+ // If this has already been wrapped in the past, return that
485+ if (func.__raven_wrapper__) {
486+ return func.__raven_wrapper__;
487+ }
488+ } catch (e) {
489+ // Just accessing custom props in some Selenium environments
490+ // can cause a "Permission denied" exception (see raven-js#495).
491+ // Bail on wrapping and return the function as-is (defers to window.onerror).
492+ return func;
493+ }
483494
484− // Get actual Error from ErrorEvent
485− if (isErrorEvent(ex)) ex = ex.error;
486−
487− // Store the raw exception object for potential debugging and introspection
488− this._lastCapturedException = ex;
489−
490− // TraceKit.report will re-raise any exception passed to it,
491− // which means you have to wrap it in try/catch. Instead, we
492− // can wrap it here and only re-raise if TraceKit.report
493− // raises an exception different from the one we asked to
494− // report on.
495− try {
496− var stack = TraceKit.computeStackTrace(ex);
497− this._handleStackInfo(stack, options);
498− } catch (ex1) {
499− if (ex !== ex1) {
500− throw ex1;
501− }
502− }
495+ function wrapped() {
496+ var args = [],
497+ i = arguments.length,
498+ deep = !options || (options && options.deep !== false);
503499
504− return this;
505− },
506−
507− /*
508− * Manually send a message to Sentry
509− *
510− * @param {string} msg A plain message to be captured in Sentry
511− * @param {object} options A specific set of options for this message [optional]
512− * @return {Raven}
513− */
514− captureMessage: function(msg, options) {
515− // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
516− // early call; we'll error on the side of logging anything called before configuration since it's
517− // probably something you should see:
518− if (
519− !!this._globalOptions.ignoreErrors.test &&
520− this._globalOptions.ignoreErrors.test(msg)
521− ) {
522− return;
523− }
500+ if (_before && isFunction(_before)) {
501+ _before.apply(this, arguments);
502+ }
524503
525− options = options || {};
526−
527− var data = objectMerge(
528− {
529− message: msg + '' // Make sure it's actually a string
530− },
531− options
532− );
533−
534− var ex;
535− // Generate a "synthetic" stack trace from this point.
536− // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
537− // of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
538− // or if it catches a thrown object without a "stack" property.
539− try {
540− throw new Error(msg);
541− } catch (ex1) {
542− ex = ex1;
543− }
504+ // Recursively wrap all of a function's arguments that are
505+ // functions themselves.
506+ while (i--)
507+ args[i] = deep
508+ ? self.wrap(options, arguments[i])
509+ : arguments[i];
544510
545− // null exception name so `Error` isn't prefixed to msg
546− ex.name = null;
547− var stack = TraceKit.computeStackTrace(ex);
511+ try {
512+ // Attempt to invoke user-land function
513+ // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
514+ // means Raven caught an error invoking your application code. This is
515+ // expected behavior and NOT indicative of a bug with Raven.js.
516+ return func.apply(this, args);
517+ } catch (e) {
518+ self._ignoreNextOnError();
519+ self.captureException(e, options);
520+ throw e;
521+ }
522+ }
548523
549− // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
550− var initialCall = isArray(stack.stack) && stack.stack[1];
551− var fileurl = (initialCall && initialCall.url) || '';
524+ // copy over properties of the old function
525+ for (var property in func) {
526+ if (hasKey(func, property)) {
527+ wrapped[property] = func[property];
528+ }
529+ }
530+ wrapped.prototype = func.prototype;
531+
532+ func.__raven_wrapper__ = wrapped;
533+ // Signal that this function has been wrapped/filled already
534+ // for both debugging and to prevent it to being wrapped/filled twice
535+ wrapped.__raven__ = true;
536+ wrapped.__orig__ = func;
537+
538+ return wrapped;
539+ },
540+
541+ /*
542+ * Uninstalls the global error handler.
543+ *
544+ * @return {Raven}
545+ */
546+ uninstall: function () {
547+ TraceKit.report.uninstall();
548+
549+ this._unpatchFunctionToString();
550+ this._restoreBuiltIns();
551+
552+ Error.stackTraceLimit = this._originalErrorStackTraceLimit;
553+ this._isRavenInstalled = false;
554+
555+ return this;
556+ },
557+
558+ /*
559+ * Manually capture an exception and send it over to Sentry
560+ *
561+ * @param {error} ex An exception to be logged
562+ * @param {object} options A specific set of options for this error [optional]
563+ * @return {Raven}
564+ */
565+ captureException: function (ex, options) {
566+ // Cases for sending ex as a message, rather than an exception
567+ var isNotError = !isError(ex);
568+ var isNotErrorEvent = !isErrorEvent(ex);
569+ var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error;
570+
571+ if (
572+ (isNotError && isNotErrorEvent) ||
573+ isErrorEventWithoutError
574+ ) {
575+ return this.captureMessage(
576+ ex,
577+ objectMerge(
578+ {
579+ trimHeadFrames: 1,
580+ stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
581+ },
582+ options,
583+ ),
584+ );
585+ }
552586
553− if (
554− !!this._globalOptions.ignoreUrls.test &&
555− this._globalOptions.ignoreUrls.test(fileurl)
556− ) {
557− return;
558− }
587+ // Get actual Error from ErrorEvent
588+ if (isErrorEvent(ex)) ex = ex.error;
559589
560− if (
561− !!this._globalOptions.whitelistUrls.test &&
562− !this._globalOptions.whitelistUrls.test(fileurl)
563− ) {
564− return;
565− }
590+ // Store the raw exception object for potential debugging and introspection
591+ this._lastCapturedException = ex;
566592
567− if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
568− options = objectMerge(
569− {
570− // fingerprint on msg, not stack trace (legacy behavior, could be
571− // revisited)
572− fingerprint: msg,
573− // since we know this is a synthetic trace, the top N-most frames
574− // MUST be from Raven.js, so mark them as in_app later by setting
575− // trimHeadFrames
576− trimHeadFrames: (options.trimHeadFrames || 0) + 1
577− },
578− options
579− );
580−
581− var frames = this._prepareFrames(stack, options);
582− data.stacktrace = {
583− // Sentry expects frames oldest to newest
584− frames: frames.reverse()
585− };
586− }
593+ // TraceKit.report will re-raise any exception passed to it,
594+ // which means you have to wrap it in try/catch. Instead, we
595+ // can wrap it here and only re-raise if TraceKit.report
596+ // raises an exception different from the one we asked to
597+ // report on.
598+ try {
599+ var stack = TraceKit.computeStackTrace(ex);
600+ this._handleStackInfo(stack, options);
601+ } catch (ex1) {
602+ if (ex !== ex1) {
603+ throw ex1;
604+ }
605+ }
587606
588− // Fire away!
589− this._send(data);
607+ return this;
608+ },
609+
610+ /*
611+ * Manually send a message to Sentry
612+ *
613+ * @param {string} msg A plain message to be captured in Sentry
614+ * @param {object} options A specific set of options for this message [optional]
615+ * @return {Raven}
616+ */
617+ captureMessage: function (msg, options) {
618+ // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
619+ // early call; we'll error on the side of logging anything called before configuration since it's
620+ // probably something you should see:
621+ if (
622+ !!this._globalOptions.ignoreErrors.test &&
623+ this._globalOptions.ignoreErrors.test(msg)
624+ ) {
625+ return;
626+ }
590627
591− return this;
592− },
628+ options = options || {};
593629
594− captureBreadcrumb: function(obj) {
595− var crumb = objectMerge(
596− {
597− timestamp: now() / 1000
598− },
599− obj
600− );
630+ var data = objectMerge(
631+ {
632+ message: msg + "", // Make sure it's actually a string
633+ },
634+ options,
635+ );
601636
602− if (isFunction(this._globalOptions.breadcrumbCallback)) {
603− var result = this._globalOptions.breadcrumbCallback(crumb);
637+ var ex;
638+ // Generate a "synthetic" stack trace from this point.
639+ // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
640+ // of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
641+ // or if it catches a thrown object without a "stack" property.
642+ try {
643+ throw new Error(msg);
644+ } catch (ex1) {
645+ ex = ex1;
646+ }
604647
605− if (isObject(result) && !isEmptyObject(result)) {
606− crumb = result;
607− } else if (result === false) {
608− return this;
609− }
610− }
648+ // null exception name so `Error` isn't prefixed to msg
649+ ex.name = null;
650+ var stack = TraceKit.computeStackTrace(ex);
611651
612− this._breadcrumbs.push(crumb);
613− if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
614− this._breadcrumbs.shift();
615− }
616− return this;
617− },
652+ // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
653+ var initialCall = isArray(stack.stack) && stack.stack[1];
654+ var fileurl = (initialCall && initialCall.url) || "";
618655
619− addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
620− var pluginArgs = [].slice.call(arguments, 1);
656+ if (
657+ !!this._globalOptions.ignoreUrls.test &&
658+ this._globalOptions.ignoreUrls.test(fileurl)
659+ ) {
660+ return;
661+ }
621662
622− this._plugins.push([plugin, pluginArgs]);
623− if (this._isRavenInstalled) {
624− this._drainPlugins();
625− }
663+ if (
664+ !!this._globalOptions.whitelistUrls.test &&
665+ !this._globalOptions.whitelistUrls.test(fileurl)
666+ ) {
667+ return;
668+ }
626669
627− return this;
628− },
629−
630− /*
631− * Set/clear a user to be sent along with the payload.
632− *
633− * @param {object} user An object representing user data [optional]
634− * @return {Raven}
635− */
636− setUserContext: function(user) {
637− // Intentionally do not merge here since that's an unexpected behavior.
638− this._globalContext.user = user;
639−
640− return this;
641− },
642−
643− /*
644− * Merge extra attributes to be sent along with the payload.
645− *
646− * @param {object} extra An object representing extra data [optional]
647− * @return {Raven}
648− */
649− setExtraContext: function(extra) {
650− this._mergeContext('extra', extra);
651−
652− return this;
653− },
654−
655− /*
656− * Merge tags to be sent along with the payload.
657− *
658− * @param {object} tags An object representing tags [optional]
659− * @return {Raven}
660− */
661− setTagsContext: function(tags) {
662− this._mergeContext('tags', tags);
663−
664− return this;
665− },
666−
667− /*
668− * Clear all of the context.
669− *
670− * @return {Raven}
671− */
672− clearContext: function() {
673− this._globalContext = {};
674−
675− return this;
676− },
677−
678− /*
679− * Get a copy of the current context. This cannot be mutated.
680− *
681− * @return {object} copy of context
682− */
683− getContext: function() {
684− // lol javascript
685− return JSON.parse(stringify(this._globalContext));
686− },
687−
688− /*
689− * Set environment of application
690− *
691− * @param {string} environment Typically something like 'production'.
692− * @return {Raven}
693− */
694− setEnvironment: function(environment) {
695− this._globalOptions.environment = environment;
696−
697− return this;
698− },
699−
700− /*
701− * Set release version of application
702− *
703− * @param {string} release Typically something like a git SHA to identify version
704− * @return {Raven}
705− */
706− setRelease: function(release) {
707− this._globalOptions.release = release;
708−
709− return this;
710− },
711−
712− /*
713− * Set the dataCallback option
714− *
715− * @param {function} callback The callback to run which allows the
716− * data blob to be mutated before sending
717− * @return {Raven}
718− */
719− setDataCallback: function(callback) {
720− var original = this._globalOptions.dataCallback;
721− this._globalOptions.dataCallback = keepOriginalCallback(original, callback);
722− return this;
723− },
724−
725− /*
726− * Set the breadcrumbCallback option
727− *
728− * @param {function} callback The callback to run which allows filtering
729− * or mutating breadcrumbs
730− * @return {Raven}
731− */
732− setBreadcrumbCallback: function(callback) {
733− var original = this._globalOptions.breadcrumbCallback;
734− this._globalOptions.breadcrumbCallback = keepOriginalCallback(original, callback);
735− return this;
736− },
737−
738− /*
739− * Set the shouldSendCallback option
740− *
741− * @param {function} callback The callback to run which allows
742− * introspecting the blob before sending
743− * @return {Raven}
744− */
745− setShouldSendCallback: function(callback) {
746− var original = this._globalOptions.shouldSendCallback;
747− this._globalOptions.shouldSendCallback = keepOriginalCallback(original, callback);
748− return this;
749− },
750−
751− /**
752− * Override the default HTTP transport mechanism that transmits data
753− * to the Sentry server.
754− *
755− * @param {function} transport Function invoked instead of the default
756− * `makeRequest` handler.
757− *
758− * @return {Raven}
759− */
760− setTransport: function(transport) {
761− this._globalOptions.transport = transport;
762−
763− return this;
764− },
765−
766− /*
767− * Get the latest raw exception that was captured by Raven.
768− *
769− * @return {error}
770− */
771− lastException: function() {
772− return this._lastCapturedException;
773− },
774−
775− /*
776− * Get the last event id
777− *
778− * @return {string}
779− */
780− lastEventId: function() {
781− return this._lastEventId;
782− },
783−
784− /*
785− * Determine if Raven is setup and ready to go.
786− *
787− * @return {boolean}
788− */
789− isSetup: function() {
790− if (!this._hasJSON) return false; // needs JSON support
791− if (!this._globalServer) {
792− if (!this.ravenNotConfiguredError) {
793− this.ravenNotConfiguredError = true;
794− this._logDebug('error', 'Error: Raven has not been configured.');
795− }
796− return false;
797− }
798− return true;
799− },
670+ if (
671+ this._globalOptions.stacktrace ||
672+ (options && options.stacktrace)
673+ ) {
674+ options = objectMerge(
675+ {
676+ // fingerprint on msg, not stack trace (legacy behavior, could be
677+ // revisited)
678+ fingerprint: msg,
679+ // since we know this is a synthetic trace, the top N-most frames
680+ // MUST be from Raven.js, so mark them as in_app later by setting
681+ // trimHeadFrames
682+ trimHeadFrames: (options.trimHeadFrames || 0) + 1,
683+ },
684+ options,
685+ );
686+
687+ var frames = this._prepareFrames(stack, options);
688+ data.stacktrace = {
689+ // Sentry expects frames oldest to newest
690+ frames: frames.reverse(),
691+ };
692+ }
800693
801− afterLoad: function() {
802− // TODO: remove window dependence?
694+ // Fire away!
695+ this._send(data);
803696
804− // Attempt to initialize Raven on load
805− var RavenConfig = _window.RavenConfig;
806− if (RavenConfig) {
807− this.config(RavenConfig.dsn, RavenConfig.config).install();
808− }
809− },
697+ return this;
698+ },
810699
811− showReportDialog: function(options) {
812− if (
813− !_document // doesn't work without a document (React native)
814− )
815− return;
700+ captureBreadcrumb: function (obj) {
701+ var crumb = objectMerge(
702+ {
703+ timestamp: now() / 1000,
704+ },
705+ obj,
706+ );
816707
817− options = options || {};
708+ if (isFunction(this._globalOptions.breadcrumbCallback)) {
709+ var result = this._globalOptions.breadcrumbCallback(crumb);
818710
819− var lastEventId = options.eventId || this.lastEventId();
820− if (!lastEventId) {
821− throw new RavenConfigError('Missing eventId');
822− }
711+ if (isObject(result) && !isEmptyObject(result)) {
712+ crumb = result;
713+ } else if (result === false) {
714+ return this;
715+ }
716+ }
823717
824− var dsn = options.dsn || this._dsn;
825− if (!dsn) {
826− throw new RavenConfigError('Missing DSN');
827− }
718+ this._breadcrumbs.push(crumb);
719+ if (
720+ this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs
721+ ) {
722+ this._breadcrumbs.shift();
723+ }
724+ return this;
725+ },
828726
829− var encode = encodeURIComponent;
830− var qs = '';
831− qs += '?eventId=' + encode(lastEventId);
832− qs += '&dsn=' + encode(dsn);
727+ addPlugin: function (plugin /*arg1, arg2, ... argN*/) {
728+ var pluginArgs = [].slice.call(arguments, 1);
833729
834− var user = options.user || this._globalContext.user;
835− if (user) {
836− if (user.name) qs += '&name=' + encode(user.name);
837− if (user.email) qs += '&email=' + encode(user.email);
838− }
730+ this._plugins.push([plugin, pluginArgs]);
731+ if (this._isRavenInstalled) {
732+ this._drainPlugins();
733+ }
839734
840− var globalServer = this._getGlobalServer(this._parseDSN(dsn));
735+ return this;
736+ },
737+
738+ /*
739+ * Set/clear a user to be sent along with the payload.
740+ *
741+ * @param {object} user An object representing user data [optional]
742+ * @return {Raven}
743+ */
744+ setUserContext: function (user) {
745+ // Intentionally do not merge here since that's an unexpected behavior.
746+ this._globalContext.user = user;
747+
748+ return this;
749+ },
750+
751+ /*
752+ * Merge extra attributes to be sent along with the payload.
753+ *
754+ * @param {object} extra An object representing extra data [optional]
755+ * @return {Raven}
756+ */
757+ setExtraContext: function (extra) {
758+ this._mergeContext("extra", extra);
759+
760+ return this;
761+ },
762+
763+ /*
764+ * Merge tags to be sent along with the payload.
765+ *
766+ * @param {object} tags An object representing tags [optional]
767+ * @return {Raven}
768+ */
769+ setTagsContext: function (tags) {
770+ this._mergeContext("tags", tags);
771+
772+ return this;
773+ },
774+
775+ /*
776+ * Clear all of the context.
777+ *
778+ * @return {Raven}
779+ */
780+ clearContext: function () {
781+ this._globalContext = {};
782+
783+ return this;
784+ },
785+
786+ /*
787+ * Get a copy of the current context. This cannot be mutated.
788+ *
789+ * @return {object} copy of context
790+ */
791+ getContext: function () {
792+ // lol javascript
793+ return JSON.parse(stringify(this._globalContext));
794+ },
795+
796+ /*
797+ * Set environment of application
798+ *
799+ * @param {string} environment Typically something like 'production'.
800+ * @return {Raven}
801+ */
802+ setEnvironment: function (environment) {
803+ this._globalOptions.environment = environment;
804+
805+ return this;
806+ },
807+
808+ /*
809+ * Set release version of application
810+ *
811+ * @param {string} release Typically something like a git SHA to identify version
812+ * @return {Raven}
813+ */
814+ setRelease: function (release) {
815+ this._globalOptions.release = release;
816+
817+ return this;
818+ },
819+
820+ /*
821+ * Set the dataCallback option
822+ *
823+ * @param {function} callback The callback to run which allows the
824+ * data blob to be mutated before sending
825+ * @return {Raven}
826+ */
827+ setDataCallback: function (callback) {
828+ var original = this._globalOptions.dataCallback;
829+ this._globalOptions.dataCallback = keepOriginalCallback(
830+ original,
831+ callback,
832+ );
833+ return this;
834+ },
835+
836+ /*
837+ * Set the breadcrumbCallback option
838+ *
839+ * @param {function} callback The callback to run which allows filtering
840+ * or mutating breadcrumbs
841+ * @return {Raven}
842+ */
843+ setBreadcrumbCallback: function (callback) {
844+ var original = this._globalOptions.breadcrumbCallback;
845+ this._globalOptions.breadcrumbCallback = keepOriginalCallback(
846+ original,
847+ callback,
848+ );
849+ return this;
850+ },
851+
852+ /*
853+ * Set the shouldSendCallback option
854+ *
855+ * @param {function} callback The callback to run which allows
856+ * introspecting the blob before sending
857+ * @return {Raven}
858+ */
859+ setShouldSendCallback: function (callback) {
860+ var original = this._globalOptions.shouldSendCallback;
861+ this._globalOptions.shouldSendCallback = keepOriginalCallback(
862+ original,
863+ callback,
864+ );
865+ return this;
866+ },
867+
868+ /**
869+ * Override the default HTTP transport mechanism that transmits data
870+ * to the Sentry server.
871+ *
872+ * @param {function} transport Function invoked instead of the default
873+ * `makeRequest` handler.
874+ *
875+ * @return {Raven}
876+ */
877+ setTransport: function (transport) {
878+ this._globalOptions.transport = transport;
879+
880+ return this;
881+ },
882+
883+ /*
884+ * Get the latest raw exception that was captured by Raven.
885+ *
886+ * @return {error}
887+ */
888+ lastException: function () {
889+ return this._lastCapturedException;
890+ },
891+
892+ /*
893+ * Get the last event id
894+ *
895+ * @return {string}
896+ */
897+ lastEventId: function () {
898+ return this._lastEventId;
899+ },
900+
901+ /*
902+ * Determine if Raven is setup and ready to go.
903+ *
904+ * @return {boolean}
905+ */
906+ isSetup: function () {
907+ if (!this._hasJSON) return false; // needs JSON support
908+ if (!this._globalServer) {
909+ if (!this.ravenNotConfiguredError) {
910+ this.ravenNotConfiguredError = true;
911+ this._logDebug(
912+ "error",
913+ "Error: Raven has not been configured.",
914+ );
915+ }
916+ return false;
917+ }
918+ return true;
919+ },
841920
842− var script = _document.createElement('script');
843− script.async = true;
844− script.src = globalServer + '/api/embed/error-page/' + qs;
845− (_document.head || _document.body).appendChild(script);
846− },
921+ afterLoad: function () {
922+ // TODO: remove window dependence?
847923
848− /**** Private functions ****/
849− _ignoreNextOnError: function() {
850− var self = this;
851− this._ignoreOnError += 1;
852− setTimeout(function() {
853− // onerror should trigger before setTimeout
854− self._ignoreOnError -= 1;
855− });
856− },
924+ // Attempt to initialize Raven on load
925+ var RavenConfig = _window.RavenConfig;
926+ if (RavenConfig) {
927+ this.config(RavenConfig.dsn, RavenConfig.config).install();
928+ }
929+ },
857930
858− _triggerEvent: function(eventType, options) {
859− // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
860− var evt, key;
931+ showReportDialog: function (options) {
932+ if (
933+ !_document // doesn't work without a document (React native)
934+ )
935+ return;
861936
862− if (!this._hasDocument) return;
937+ options = options || {};
863938
864− options = options || {};
939+ var lastEventId = options.eventId || this.lastEventId();
940+ if (!lastEventId) {
941+ throw new RavenConfigError("Missing eventId");
942+ }
865943
866− eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1);
944+ var dsn = options.dsn || this._dsn;
945+ if (!dsn) {
946+ throw new RavenConfigError("Missing DSN");
947+ }
867948
868− if (_document.createEvent) {
869− evt = _document.createEvent('HTMLEvents');
870− evt.initEvent(eventType, true, true);
871− } else {
872− evt = _document.createEventObject();
873− evt.eventType = eventType;
874− }
949+ var encode = encodeURIComponent;
950+ var qs = "";
951+ qs += "?eventId=" + encode(lastEventId);
952+ qs += "&dsn=" + encode(dsn);
875953
876− for (key in options)
877− if (hasKey(options, key)) {
878− evt[key] = options[key];
879− }
954+ var user = options.user || this._globalContext.user;
955+ if (user) {
956+ if (user.name) qs += "&name=" + encode(user.name);
957+ if (user.email) qs += "&email=" + encode(user.email);
958+ }
880959
881− if (_document.createEvent) {
882− // IE9 if standards
883− _document.dispatchEvent(evt);
884− } else {
885− // IE8 regardless of Quirks or Standards
886− // IE9 if quirks
887− try {
888− _document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
889− } catch (e) {
890− // Do nothing
891− }
892− }
893− },
894−
895− /**
896− * Wraps addEventListener to capture UI breadcrumbs
897− * @param evtName the event name (e.g. "click")
898− * @returns {Function}
899− * @private
900− */
901− _breadcrumbEventHandler: function(evtName) {
902− var self = this;
903− return function(evt) {
904− // reset keypress timeout; e.g. triggering a 'click' after
905− // a 'keypress' will reset the keypress debounce so that a new
906− // set of keypresses can be recorded
907− self._keypressTimeout = null;
908−
909− // It's possible this handler might trigger multiple times for the same
910− // event (e.g. event propagation through node ancestors). Ignore if we've
911− // already captured the event.
912− if (self._lastCapturedEvent === evt) return;
913−
914− self._lastCapturedEvent = evt;
915−
916− // try/catch both:
917− // - accessing evt.target (see getsentry/raven-js#838, #768)
918− // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
919− // can throw an exception in some circumstances.
920− var target;
921− try {
922− target = htmlTreeAsString(evt.target);
923− } catch (e) {
924− target = '<unknown>';
925− }
960+ var globalServer = this._getGlobalServer(this._parseDSN(dsn));
961+
962+ var script = _document.createElement("script");
963+ script.async = true;
964+ script.src = globalServer + "/api/embed/error-page/" + qs;
965+ (_document.head || _document.body).appendChild(script);
966+ },
967+
968+ /**** Private functions ****/
969+ _ignoreNextOnError: function () {
970+ var self = this;
971+ this._ignoreOnError += 1;
972+ setTimeout(function () {
973+ // onerror should trigger before setTimeout
974+ self._ignoreOnError -= 1;
975+ });
976+ },
926977
927− self.captureBreadcrumb({
928− category: 'ui.' + evtName, // e.g. ui.click, ui.input
929− message: target
930− });
931− };
932− },
933−
934− /**
935− * Wraps addEventListener to capture keypress UI events
936− * @returns {Function}
937− * @private
938− */
939− _keypressEventHandler: function() {
940− var self = this,
941− debounceDuration = 1000; // milliseconds
942−
943− // TODO: if somehow user switches keypress target before
944− // debounce timeout is triggered, we will only capture
945− // a single breadcrumb from the FIRST target (acceptable?)
946− return function(evt) {
947− var target;
948− try {
949− target = evt.target;
950− } catch (e) {
951− // just accessing event properties can throw an exception in some rare circumstances
952− // see: https://github.com/getsentry/raven-js/issues/838
953− return;
954− }
955− var tagName = target && target.tagName;
956−
957− // only consider keypress events on actual input elements
958− // this will disregard keypresses targeting body (e.g. tabbing
959− // through elements, hotkeys, etc)
960− if (
961− !tagName ||
962− (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !target.isContentEditable)
963− )
964− return;
965−
966− // record first keypress in a series, but ignore subsequent
967− // keypresses until debounce clears
968− var timeout = self._keypressTimeout;
969− if (!timeout) {
970− self._breadcrumbEventHandler('input')(evt);
971− }
972− clearTimeout(timeout);
973− self._keypressTimeout = setTimeout(function() {
974− self._keypressTimeout = null;
975− }, debounceDuration);
976− };
977− },
978−
979− /**
980− * Captures a breadcrumb of type "navigation", normalizing input URLs
981− * @param to the originating URL
982− * @param from the target URL
983− * @private
984− */
985− _captureUrlChange: function(from, to) {
986− var parsedLoc = parseUrl(this._location.href);
987− var parsedTo = parseUrl(to);
988− var parsedFrom = parseUrl(from);
989−
990− // because onpopstate only tells you the "new" (to) value of location.href, and
991− // not the previous (from) value, we need to track the value of the current URL
992− // state ourselves
993− this._lastHref = to;
994−
995− // Use only the path component of the URL if the URL matches the current
996− // document (almost all the time when using pushState)
997− if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host)
998− to = parsedTo.relative;
999− if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host)
1000− from = parsedFrom.relative;
1001−
1002− this.captureBreadcrumb({
1003− category: 'navigation',
1004− data: {
1005− to: to,
1006− from: from
1007− }
1008− });
1009− },
1010−
1011− _patchFunctionToString: function() {
1012− var self = this;
1013− self._originalFunctionToString = Function.prototype.toString;
1014− // eslint-disable-next-line no-extend-native
1015− Function.prototype.toString = function() {
1016− if (typeof this === 'function' && this.__raven__) {
1017− return self._originalFunctionToString.apply(this.__orig__, arguments);
1018− }
1019− return self._originalFunctionToString.apply(this, arguments);
1020− };
1021− },
1022−
1023− _unpatchFunctionToString: function() {
1024− if (this._originalFunctionToString) {
1025− // eslint-disable-next-line no-extend-native
1026− Function.prototype.toString = this._originalFunctionToString;
1027− }
1028− },
1029−
1030− /**
1031− * Wrap timer functions and event targets to catch errors and provide
1032− * better metadata.
1033− */
1034− _instrumentTryCatch: function() {
1035− var self = this;
1036−
1037− var wrappedBuiltIns = self._wrappedBuiltIns;
1038−
1039− function wrapTimeFn(orig) {
1040− return function(fn, t) {
1041− // preserve arity
1042− // Make a copy of the arguments to prevent deoptimization
1043− // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1044− var args = new Array(arguments.length);
1045− for (var i = 0; i < args.length; ++i) {
1046− args[i] = arguments[i];
1047− }
1048− var originalCallback = args[0];
1049− if (isFunction(originalCallback)) {
1050− args[0] = self.wrap(originalCallback);
1051− }
978+ _triggerEvent: function (eventType, options) {
979+ // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
980+ var evt, key;
1052981
1053− // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
1054− // also supports only two arguments and doesn't care what this is, so we
1055− // can just call the original function directly.
1056− if (orig.apply) {
1057− return orig.apply(this, args);
1058− } else {
1059− return orig(args[0], args[1]);
1060− }
1061− };
1062− }
982+ if (!this._hasDocument) return;
1063983
1064− var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1065−
1066− function wrapEventTarget(global) {
1067− var proto = _window[global] && _window[global].prototype;
1068− if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) {
1069− fill(
1070− proto,
1071− 'addEventListener',
1072− function(orig) {
1073− return function(evtName, fn, capture, secure) {
1074− // preserve arity
1075− try {
1076− if (fn && fn.handleEvent) {
1077− fn.handleEvent = self.wrap(fn.handleEvent);
984+ options = options || {};
985+
986+ eventType =
987+ "raven" +
988+ eventType.substr(0, 1).toUpperCase() +
989+ eventType.substr(1);
990+
991+ if (_document.createEvent) {
992+ evt = _document.createEvent("HTMLEvents");
993+ evt.initEvent(eventType, true, true);
994+ } else {
995+ evt = _document.createEventObject();
996+ evt.eventType = eventType;
1078997 }
1079− } catch (err) {
1080− // can sometimes get 'Permission denied to access property "handle Event'
1081− }
1082998
1083− // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`
1084− // so that we don't have more than one wrapper function
1085− var before, clickHandler, keypressHandler;
999+ for (key in options)
1000+ if (hasKey(options, key)) {
1001+ evt[key] = options[key];
1002+ }
10861003
1087− if (
1088− autoBreadcrumbs &&
1089− autoBreadcrumbs.dom &&
1090− (global === 'EventTarget' || global === 'Node')
1091− ) {
1092− // NOTE: generating multiple handlers per addEventListener invocation, should
1093− // revisit and verify we can just use one (almost certainly)
1094− clickHandler = self._breadcrumbEventHandler('click');
1095− keypressHandler = self._keypressEventHandler();
1096− before = function(evt) {
1097− // need to intercept every DOM event in `before` argument, in case that
1098− // same wrapped method is re-used for different events (e.g. mousemove THEN click)
1099− // see #724
1100− if (!evt) return;
1101−
1102− var eventType;
1004+ if (_document.createEvent) {
1005+ // IE9 if standards
1006+ _document.dispatchEvent(evt);
1007+ } else {
1008+ // IE8 regardless of Quirks or Standards
1009+ // IE9 if quirks
1010+ try {
1011+ _document.fireEvent(
1012+ "on" + evt.eventType.toLowerCase(),
1013+ evt,
1014+ );
1015+ } catch (e) {
1016+ // Do nothing
1017+ }
1018+ }
1019+ },
1020+
1021+ /**
1022+ * Wraps addEventListener to capture UI breadcrumbs
1023+ * @param evtName the event name (e.g. "click")
1024+ * @returns {Function}
1025+ * @private
1026+ */
1027+ _breadcrumbEventHandler: function (evtName) {
1028+ var self = this;
1029+ return function (evt) {
1030+ // reset keypress timeout; e.g. triggering a 'click' after
1031+ // a 'keypress' will reset the keypress debounce so that a new
1032+ // set of keypresses can be recorded
1033+ self._keypressTimeout = null;
1034+
1035+ // It's possible this handler might trigger multiple times for the same
1036+ // event (e.g. event propagation through node ancestors). Ignore if we've
1037+ // already captured the event.
1038+ if (self._lastCapturedEvent === evt) return;
1039+
1040+ self._lastCapturedEvent = evt;
1041+
1042+ // try/catch both:
1043+ // - accessing evt.target (see getsentry/raven-js#838, #768)
1044+ // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
1045+ // can throw an exception in some circumstances.
1046+ var target;
1047+ try {
1048+ target = htmlTreeAsString(evt.target);
1049+ } catch (e) {
1050+ target = "<unknown>";
1051+ }
1052+
1053+ self.captureBreadcrumb({
1054+ category: "ui." + evtName, // e.g. ui.click, ui.input
1055+ message: target,
1056+ });
1057+ };
1058+ },
1059+
1060+ /**
1061+ * Wraps addEventListener to capture keypress UI events
1062+ * @returns {Function}
1063+ * @private
1064+ */
1065+ _keypressEventHandler: function () {
1066+ var self = this,
1067+ debounceDuration = 1000; // milliseconds
1068+
1069+ // TODO: if somehow user switches keypress target before
1070+ // debounce timeout is triggered, we will only capture
1071+ // a single breadcrumb from the FIRST target (acceptable?)
1072+ return function (evt) {
1073+ var target;
11031074 try {
1104− eventType = evt.type;
1075+ target = evt.target;
11051076 } catch (e) {
11061077 // just accessing event properties can throw an exception in some rare circumstances
11071078 // see: https://github.com/getsentry/raven-js/issues/838
11081079 return;
11091080 }
1110− if (eventType === 'click') return clickHandler(evt);
1111− else if (eventType === 'keypress') return keypressHandler(evt);
1081+ var tagName = target && target.tagName;
1082+
1083+ // only consider keypress events on actual input elements
1084+ // this will disregard keypresses targeting body (e.g. tabbing
1085+ // through elements, hotkeys, etc)
1086+ if (
1087+ !tagName ||
1088+ (tagName !== "INPUT" &&
1089+ tagName !== "TEXTAREA" &&
1090+ !target.isContentEditable)
1091+ )
1092+ return;
1093+
1094+ // record first keypress in a series, but ignore subsequent
1095+ // keypresses until debounce clears
1096+ var timeout = self._keypressTimeout;
1097+ if (!timeout) {
1098+ self._breadcrumbEventHandler("input")(evt);
1099+ }
1100+ clearTimeout(timeout);
1101+ self._keypressTimeout = setTimeout(function () {
1102+ self._keypressTimeout = null;
1103+ }, debounceDuration);
11121104 };
1113− }
1114− return orig.call(
1115− this,
1116− evtName,
1117− self.wrap(fn, undefined, before),
1118− capture,
1119− secure
1120− );
1121− };
1122− },
1123− wrappedBuiltIns
1124− );
1125− fill(
1126− proto,
1127− 'removeEventListener',
1128− function(orig) {
1129− return function(evt, fn, capture, secure) {
1130− try {
1131− fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn);
1132− } catch (e) {
1133− // ignore, accessing __raven_wrapper__ will throw in some Selenium environments
1134− }
1135− return orig.call(this, evt, fn, capture, secure);
1136− };
1137− },
1138− wrappedBuiltIns
1139− );
1140− }
1141− }
1105+ },
1106+
1107+ /**
1108+ * Captures a breadcrumb of type "navigation", normalizing input URLs
1109+ * @param to the originating URL
1110+ * @param from the target URL
1111+ * @private
1112+ */
1113+ _captureUrlChange: function (from, to) {
1114+ var parsedLoc = parseUrl(this._location.href);
1115+ var parsedTo = parseUrl(to);
1116+ var parsedFrom = parseUrl(from);
1117+
1118+ // because onpopstate only tells you the "new" (to) value of location.href, and
1119+ // not the previous (from) value, we need to track the value of the current URL
1120+ // state ourselves
1121+ this._lastHref = to;
1122+
1123+ // Use only the path component of the URL if the URL matches the current
1124+ // document (almost all the time when using pushState)
1125+ if (
1126+ parsedLoc.protocol === parsedTo.protocol &&
1127+ parsedLoc.host === parsedTo.host
1128+ )
1129+ to = parsedTo.relative;
1130+ if (
1131+ parsedLoc.protocol === parsedFrom.protocol &&
1132+ parsedLoc.host === parsedFrom.host
1133+ )
1134+ from = parsedFrom.relative;
1135+
1136+ this.captureBreadcrumb({
1137+ category: "navigation",
1138+ data: {
1139+ to: to,
1140+ from: from,
1141+ },
1142+ });
1143+ },
1144+
1145+ _patchFunctionToString: function () {
1146+ var self = this;
1147+ self._originalFunctionToString = Function.prototype.toString;
1148+ // eslint-disable-next-line no-extend-native
1149+ Function.prototype.toString = function () {
1150+ if (typeof this === "function" && this.__raven__) {
1151+ return self._originalFunctionToString.apply(
1152+ this.__orig__,
1153+ arguments,
1154+ );
1155+ }
1156+ return self._originalFunctionToString.apply(this, arguments);
1157+ };
1158+ },
11421159
1143− fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns);
1144− fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns);
1145− if (_window.requestAnimationFrame) {
1146− fill(
1147− _window,
1148− 'requestAnimationFrame',
1149− function(orig) {
1150− return function(cb) {
1151− return orig(self.wrap(cb));
1152− };
1153− },
1154− wrappedBuiltIns
1155− );
1156− }
1160+ _unpatchFunctionToString: function () {
1161+ if (this._originalFunctionToString) {
1162+ // eslint-disable-next-line no-extend-native
1163+ Function.prototype.toString = this._originalFunctionToString;
1164+ }
1165+ },
1166+
1167+ /**
1168+ * Wrap timer functions and event targets to catch errors and provide
1169+ * better metadata.
1170+ */
1171+ _instrumentTryCatch: function () {
1172+ var self = this;
1173+
1174+ var wrappedBuiltIns = self._wrappedBuiltIns;
1175+
1176+ function wrapTimeFn(orig) {
1177+ return function (fn, t) {
1178+ // preserve arity
1179+ // Make a copy of the arguments to prevent deoptimization
1180+ // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1181+ var args = new Array(arguments.length);
1182+ for (var i = 0; i < args.length; ++i) {
1183+ args[i] = arguments[i];
1184+ }
1185+ var originalCallback = args[0];
1186+ if (isFunction(originalCallback)) {
1187+ args[0] = self.wrap(originalCallback);
1188+ }
1189+
1190+ // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
1191+ // also supports only two arguments and doesn't care what this is, so we
1192+ // can just call the original function directly.
1193+ if (orig.apply) {
1194+ return orig.apply(this, args);
1195+ } else {
1196+ return orig(args[0], args[1]);
1197+ }
1198+ };
1199+ }
11571200
1158− // event targets borrowed from bugsnag-js:
1159− // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
1160− var eventTargets = [
1161− 'EventTarget',
1162− 'Window',
1163− 'Node',
1164− 'ApplicationCache',
1165− 'AudioTrackList',
1166− 'ChannelMergerNode',
1167− 'CryptoOperation',
1168− 'EventSource',
1169− 'FileReader',
1170− 'HTMLUnknownElement',
1171− 'IDBDatabase',
1172− 'IDBRequest',
1173− 'IDBTransaction',
1174− 'KeyOperation',
1175− 'MediaController',
1176− 'MessagePort',
1177− 'ModalWindow',
1178− 'Notification',
1179− 'SVGElementInstance',
1180− 'Screen',
1181− 'TextTrack',
1182− 'TextTrackCue',
1183− 'TextTrackList',
1184− 'WebSocket',
1185− 'WebSocketWorker',
1186− 'Worker',
1187− 'XMLHttpRequest',
1188− 'XMLHttpRequestEventTarget',
1189− 'XMLHttpRequestUpload'
1190− ];
1191− for (var i = 0; i < eventTargets.length; i++) {
1192− wrapEventTarget(eventTargets[i]);
1193− }
1194− },
1195−
1196− /**
1197− * Instrument browser built-ins w/ breadcrumb capturing
1198− * - XMLHttpRequests
1199− * - DOM interactions (click/typing)
1200− * - window.location changes
1201− * - console
1202− *
1203− * Can be disabled or individually configured via the `autoBreadcrumbs` config option
1204− */
1205− _instrumentBreadcrumbs: function() {
1206− var self = this;
1207− var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1208−
1209− var wrappedBuiltIns = self._wrappedBuiltIns;
1210−
1211− function wrapProp(prop, xhr) {
1212− if (prop in xhr && isFunction(xhr[prop])) {
1213− fill(xhr, prop, function(orig) {
1214− return self.wrap(orig);
1215− }); // intentionally don't track filled methods on XHR instances
1216− }
1217− }
1201+ var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1202+
1203+ function wrapEventTarget(global) {
1204+ var proto = _window[global] && _window[global].prototype;
1205+ if (
1206+ proto &&
1207+ proto.hasOwnProperty &&
1208+ proto.hasOwnProperty("addEventListener")
1209+ ) {
1210+ fill(
1211+ proto,
1212+ "addEventListener",
1213+ function (orig) {
1214+ return function (evtName, fn, capture, secure) {
1215+ // preserve arity
1216+ try {
1217+ if (fn && fn.handleEvent) {
1218+ fn.handleEvent = self.wrap(fn.handleEvent);
1219+ }
1220+ } catch (err) {
1221+ // can sometimes get 'Permission denied to access property "handle Event'
1222+ }
1223+
1224+ // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`
1225+ // so that we don't have more than one wrapper function
1226+ var before, clickHandler, keypressHandler;
1227+
1228+ if (
1229+ autoBreadcrumbs &&
1230+ autoBreadcrumbs.dom &&
1231+ (global === "EventTarget" || global === "Node")
1232+ ) {
1233+ // NOTE: generating multiple handlers per addEventListener invocation, should
1234+ // revisit and verify we can just use one (almost certainly)
1235+ clickHandler =
1236+ self._breadcrumbEventHandler("click");
1237+ keypressHandler = self._keypressEventHandler();
1238+ before = function (evt) {
1239+ // need to intercept every DOM event in `before` argument, in case that
1240+ // same wrapped method is re-used for different events (e.g. mousemove THEN click)
1241+ // see #724
1242+ if (!evt) return;
1243+
1244+ var eventType;
1245+ try {
1246+ eventType = evt.type;
1247+ } catch (e) {
1248+ // just accessing event properties can throw an exception in some rare circumstances
1249+ // see: https://github.com/getsentry/raven-js/issues/838
1250+ return;
1251+ }
1252+ if (eventType === "click")
1253+ return clickHandler(evt);
1254+ else if (eventType === "keypress")
1255+ return keypressHandler(evt);
1256+ };
1257+ }
1258+ return orig.call(
1259+ this,
1260+ evtName,
1261+ self.wrap(fn, undefined, before),
1262+ capture,
1263+ secure,
1264+ );
1265+ };
1266+ },
1267+ wrappedBuiltIns,
1268+ );
1269+ fill(
1270+ proto,
1271+ "removeEventListener",
1272+ function (orig) {
1273+ return function (evt, fn, capture, secure) {
1274+ try {
1275+ fn =
1276+ fn &&
1277+ (fn.__raven_wrapper__
1278+ ? fn.__raven_wrapper__
1279+ : fn);
1280+ } catch (e) {
1281+ // ignore, accessing __raven_wrapper__ will throw in some Selenium environments
1282+ }
1283+ return orig.call(this, evt, fn, capture, secure);
1284+ };
1285+ },
1286+ wrappedBuiltIns,
1287+ );
1288+ }
1289+ }
12181290
1219− if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) {
1220− var xhrproto = XMLHttpRequest.prototype;
1221− fill(
1222− xhrproto,
1223− 'open',
1224− function(origOpen) {
1225− return function(method, url) {
1226− // preserve arity
1227−
1228− // if Sentry key appears in URL, don't capture
1229− if (isString(url) && url.indexOf(self._globalKey) === -1) {
1230− this.__raven_xhr = {
1231− method: method,
1232− url: url,
1233− status_code: null
1234− };
1235− }
1291+ fill(_window, "setTimeout", wrapTimeFn, wrappedBuiltIns);
1292+ fill(_window, "setInterval", wrapTimeFn, wrappedBuiltIns);
1293+ if (_window.requestAnimationFrame) {
1294+ fill(
1295+ _window,
1296+ "requestAnimationFrame",
1297+ function (orig) {
1298+ return function (cb) {
1299+ return orig(self.wrap(cb));
1300+ };
1301+ },
1302+ wrappedBuiltIns,
1303+ );
1304+ }
12361305
1237− return origOpen.apply(this, arguments);
1238− };
1239− },
1240− wrappedBuiltIns
1241− );
1242−
1243− fill(
1244− xhrproto,
1245− 'send',
1246− function(origSend) {
1247− return function(data) {
1248− // preserve arity
1249− var xhr = this;
1250−
1251− function onreadystatechangeHandler() {
1252− if (xhr.__raven_xhr && xhr.readyState === 4) {
1253− try {
1254− // touching statusCode in some platforms throws
1255− // an exception
1256− xhr.__raven_xhr.status_code = xhr.status;
1257− } catch (e) {
1258− /* do nothing */
1306+ // event targets borrowed from bugsnag-js:
1307+ // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
1308+ var eventTargets = [
1309+ "EventTarget",
1310+ "Window",
1311+ "Node",
1312+ "ApplicationCache",
1313+ "AudioTrackList",
1314+ "ChannelMergerNode",
1315+ "CryptoOperation",
1316+ "EventSource",
1317+ "FileReader",
1318+ "HTMLUnknownElement",
1319+ "IDBDatabase",
1320+ "IDBRequest",
1321+ "IDBTransaction",
1322+ "KeyOperation",
1323+ "MediaController",
1324+ "MessagePort",
1325+ "ModalWindow",
1326+ "Notification",
1327+ "SVGElementInstance",
1328+ "Screen",
1329+ "TextTrack",
1330+ "TextTrackCue",
1331+ "TextTrackList",
1332+ "WebSocket",
1333+ "WebSocketWorker",
1334+ "Worker",
1335+ "XMLHttpRequest",
1336+ "XMLHttpRequestEventTarget",
1337+ "XMLHttpRequestUpload",
1338+ ];
1339+ for (var i = 0; i < eventTargets.length; i++) {
1340+ wrapEventTarget(eventTargets[i]);
1341+ }
1342+ },
1343+
1344+ /**
1345+ * Instrument browser built-ins w/ breadcrumb capturing
1346+ * - XMLHttpRequests
1347+ * - DOM interactions (click/typing)
1348+ * - window.location changes
1349+ * - console
1350+ *
1351+ * Can be disabled or individually configured via the `autoBreadcrumbs` config option
1352+ */
1353+ _instrumentBreadcrumbs: function () {
1354+ var self = this;
1355+ var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1356+
1357+ var wrappedBuiltIns = self._wrappedBuiltIns;
1358+
1359+ function wrapProp(prop, xhr) {
1360+ if (prop in xhr && isFunction(xhr[prop])) {
1361+ fill(xhr, prop, function (orig) {
1362+ return self.wrap(orig);
1363+ }); // intentionally don't track filled methods on XHR instances
1364+ }
12591365 }
12601366
1261− self.captureBreadcrumb({
1262− type: 'http',
1263− category: 'xhr',
1264− data: xhr.__raven_xhr
1265− });
1266− }
1267− }
1367+ if (autoBreadcrumbs.xhr && "XMLHttpRequest" in _window) {
1368+ var xhrproto = XMLHttpRequest.prototype;
1369+ fill(
1370+ xhrproto,
1371+ "open",
1372+ function (origOpen) {
1373+ return function (method, url) {
1374+ // preserve arity
1375+
1376+ // if Sentry key appears in URL, don't capture
1377+ if (
1378+ isString(url) &&
1379+ url.indexOf(self._globalKey) === -1
1380+ ) {
1381+ this.__raven_xhr = {
1382+ method: method,
1383+ url: url,
1384+ status_code: null,
1385+ };
1386+ }
1387+
1388+ return origOpen.apply(this, arguments);
1389+ };
1390+ },
1391+ wrappedBuiltIns,
1392+ );
1393+
1394+ fill(
1395+ xhrproto,
1396+ "send",
1397+ function (origSend) {
1398+ return function (data) {
1399+ // preserve arity
1400+ var xhr = this;
1401+
1402+ function onreadystatechangeHandler() {
1403+ if (xhr.__raven_xhr && xhr.readyState === 4) {
1404+ try {
1405+ // touching statusCode in some platforms throws
1406+ // an exception
1407+ xhr.__raven_xhr.status_code = xhr.status;
1408+ } catch (e) {
1409+ /* do nothing */
1410+ }
1411+
1412+ self.captureBreadcrumb({
1413+ type: "http",
1414+ category: "xhr",
1415+ data: xhr.__raven_xhr,
1416+ });
1417+ }
1418+ }
1419+
1420+ var props = ["onload", "onerror", "onprogress"];
1421+ for (var j = 0; j < props.length; j++) {
1422+ wrapProp(props[j], xhr);
1423+ }
1424+
1425+ if (
1426+ "onreadystatechange" in xhr &&
1427+ isFunction(xhr.onreadystatechange)
1428+ ) {
1429+ fill(
1430+ xhr,
1431+ "onreadystatechange",
1432+ function (orig) {
1433+ return self.wrap(
1434+ orig,
1435+ undefined,
1436+ onreadystatechangeHandler,
1437+ );
1438+ } /* intentionally don't track this instrumentation */,
1439+ );
1440+ } else {
1441+ // if onreadystatechange wasn't actually set by the page on this xhr, we
1442+ // are free to set our own and capture the breadcrumb
1443+ xhr.onreadystatechange = onreadystatechangeHandler;
1444+ }
1445+
1446+ return origSend.apply(this, arguments);
1447+ };
1448+ },
1449+ wrappedBuiltIns,
1450+ );
1451+ }
12681452
1269− var props = ['onload', 'onerror', 'onprogress'];
1270− for (var j = 0; j < props.length; j++) {
1271− wrapProp(props[j], xhr);
1272− }
1453+ if (autoBreadcrumbs.xhr && "fetch" in _window) {
1454+ fill(
1455+ _window,
1456+ "fetch",
1457+ function (origFetch) {
1458+ return function (fn, t) {
1459+ // preserve arity
1460+ // Make a copy of the arguments to prevent deoptimization
1461+ // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1462+ var args = new Array(arguments.length);
1463+ for (var i = 0; i < args.length; ++i) {
1464+ args[i] = arguments[i];
1465+ }
1466+
1467+ var fetchInput = args[0];
1468+ var method = "GET";
1469+ var url;
1470+
1471+ if (typeof fetchInput === "string") {
1472+ url = fetchInput;
1473+ } else if (
1474+ "Request" in _window &&
1475+ fetchInput instanceof _window.Request
1476+ ) {
1477+ url = fetchInput.url;
1478+ if (fetchInput.method) {
1479+ method = fetchInput.method;
1480+ }
1481+ } else {
1482+ url = "" + fetchInput;
1483+ }
1484+
1485+ if (args[1] && args[1].method) {
1486+ method = args[1].method;
1487+ }
1488+
1489+ var fetchData = {
1490+ method: method,
1491+ url: url,
1492+ status_code: null,
1493+ };
1494+
1495+ self.captureBreadcrumb({
1496+ type: "http",
1497+ category: "fetch",
1498+ data: fetchData,
1499+ });
1500+
1501+ return origFetch
1502+ .apply(this, args)
1503+ .then(function (response) {
1504+ fetchData.status_code = response.status;
1505+
1506+ return response;
1507+ });
1508+ };
1509+ },
1510+ wrappedBuiltIns,
1511+ );
1512+ }
12731513
1274− if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) {
1275− fill(
1276− xhr,
1277− 'onreadystatechange',
1278− function(orig) {
1279− return self.wrap(orig, undefined, onreadystatechangeHandler);
1280− } /* intentionally don't track this instrumentation */
1281− );
1282− } else {
1283− // if onreadystatechange wasn't actually set by the page on this xhr, we
1284− // are free to set our own and capture the breadcrumb
1285− xhr.onreadystatechange = onreadystatechangeHandler;
1286− }
1514+ // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
1515+ // to the document. Do this before we instrument addEventListener.
1516+ if (autoBreadcrumbs.dom && this._hasDocument) {
1517+ if (_document.addEventListener) {
1518+ _document.addEventListener(
1519+ "click",
1520+ self._breadcrumbEventHandler("click"),
1521+ false,
1522+ );
1523+ _document.addEventListener(
1524+ "keypress",
1525+ self._keypressEventHandler(),
1526+ false,
1527+ );
1528+ } else {
1529+ // IE8 Compatibility
1530+ _document.attachEvent(
1531+ "onclick",
1532+ self._breadcrumbEventHandler("click"),
1533+ );
1534+ _document.attachEvent(
1535+ "onkeypress",
1536+ self._keypressEventHandler(),
1537+ );
1538+ }
1539+ }
12871540
1288− return origSend.apply(this, arguments);
1289− };
1290− },
1291− wrappedBuiltIns
1292− );
1293− }
1541+ // record navigation (URL) changes
1542+ // NOTE: in Chrome App environment, touching history.pushState, *even inside
1543+ // a try/catch block*, will cause Chrome to output an error to console.error
1544+ // borrowed from: https://github.com/angular/angular.js/pull/13945/files
1545+ var chrome = _window.chrome;
1546+ var isChromePackagedApp =
1547+ chrome && chrome.app && chrome.app.runtime;
1548+ var hasPushAndReplaceState =
1549+ !isChromePackagedApp &&
1550+ _window.history &&
1551+ history.pushState &&
1552+ history.replaceState;
1553+ if (autoBreadcrumbs.location && hasPushAndReplaceState) {
1554+ // TODO: remove onpopstate handler on uninstall()
1555+ var oldOnPopState = _window.onpopstate;
1556+ _window.onpopstate = function () {
1557+ var currentHref = self._location.href;
1558+ self._captureUrlChange(self._lastHref, currentHref);
1559+
1560+ if (oldOnPopState) {
1561+ return oldOnPopState.apply(this, arguments);
1562+ }
1563+ };
1564+
1565+ var historyReplacementFunction = function (origHistFunction) {
1566+ // note history.pushState.length is 0; intentionally not declaring
1567+ // params to preserve 0 arity
1568+ return function (/* state, title, url */) {
1569+ var url = arguments.length > 2 ? arguments[2] : undefined;
1570+
1571+ // url argument is optional
1572+ if (url) {
1573+ // coerce to string (this is what pushState does)
1574+ self._captureUrlChange(self._lastHref, url + "");
1575+ }
1576+
1577+ return origHistFunction.apply(this, arguments);
1578+ };
1579+ };
1580+
1581+ fill(
1582+ history,
1583+ "pushState",
1584+ historyReplacementFunction,
1585+ wrappedBuiltIns,
1586+ );
1587+ fill(
1588+ history,
1589+ "replaceState",
1590+ historyReplacementFunction,
1591+ wrappedBuiltIns,
1592+ );
1593+ }
12941594
1295− if (autoBreadcrumbs.xhr && 'fetch' in _window) {
1296− fill(
1297− _window,
1298− 'fetch',
1299− function(origFetch) {
1300− return function(fn, t) {
1301− // preserve arity
1302− // Make a copy of the arguments to prevent deoptimization
1303− // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1304− var args = new Array(arguments.length);
1305− for (var i = 0; i < args.length; ++i) {
1306− args[i] = arguments[i];
1307− }
1595+ if (
1596+ autoBreadcrumbs.console &&
1597+ "console" in _window &&
1598+ console.log
1599+ ) {
1600+ // console
1601+ var consoleMethodCallback = function (msg, data) {
1602+ self.captureBreadcrumb({
1603+ message: msg,
1604+ level: data.level,
1605+ category: "console",
1606+ });
1607+ };
1608+
1609+ each(
1610+ ["debug", "info", "warn", "error", "log"],
1611+ function (_, level) {
1612+ wrapConsoleMethod(console, level, consoleMethodCallback);
1613+ },
1614+ );
1615+ }
1616+ },
13081617
1309− var fetchInput = args[0];
1310− var method = 'GET';
1311− var url;
1618+ _restoreBuiltIns: function () {
1619+ // restore any wrapped builtins
1620+ var builtin;
1621+ while (this._wrappedBuiltIns.length) {
1622+ builtin = this._wrappedBuiltIns.shift();
13121623
1313− if (typeof fetchInput === 'string') {
1314− url = fetchInput;
1315− } else if ('Request' in _window && fetchInput instanceof _window.Request) {
1316− url = fetchInput.url;
1317− if (fetchInput.method) {
1318− method = fetchInput.method;
1319− }
1320− } else {
1321− url = '' + fetchInput;
1322− }
1624+ var obj = builtin[0],
1625+ name = builtin[1],
1626+ orig = builtin[2];
13231627
1324− if (args[1] && args[1].method) {
1325− method = args[1].method;
1326− }
1628+ obj[name] = orig;
1629+ }
1630+ },
13271631
1328− var fetchData = {
1329− method: method,
1330− url: url,
1331− status_code: null
1332− };
1632+ _drainPlugins: function () {
1633+ var self = this;
13331634
1334− self.captureBreadcrumb({
1335− type: 'http',
1336− category: 'fetch',
1337− data: fetchData
1338− });
1635+ // FIX ME TODO
1636+ each(this._plugins, function (_, plugin) {
1637+ var installer = plugin[0];
1638+ var args = plugin[1];
1639+ installer.apply(self, [self].concat(args));
1640+ });
1641+ },
13391642
1340− return origFetch.apply(this, args).then(function(response) {
1341− fetchData.status_code = response.status;
1643+ _parseDSN: function (str) {
1644+ var m = dsnPattern.exec(str),
1645+ dsn = {},
1646+ i = 7;
13421647
1343− return response;
1344− });
1345− };
1346− },
1347− wrappedBuiltIns
1348− );
1349− }
1648+ try {
1649+ while (i--) dsn[dsnKeys[i]] = m[i] || "";
1650+ } catch (e) {
1651+ throw new RavenConfigError("Invalid DSN: " + str);
1652+ }
13501653
1351− // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
1352− // to the document. Do this before we instrument addEventListener.
1353− if (autoBreadcrumbs.dom && this._hasDocument) {
1354− if (_document.addEventListener) {
1355− _document.addEventListener('click', self._breadcrumbEventHandler('click'), false);
1356− _document.addEventListener('keypress', self._keypressEventHandler(), false);
1357− } else {
1358− // IE8 Compatibility
1359− _document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
1360− _document.attachEvent('onkeypress', self._keypressEventHandler());
1361− }
1362− }
1654+ if (dsn.pass && !this._globalOptions.allowSecretKey) {
1655+ throw new RavenConfigError(
1656+ "Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key",
1657+ );
1658+ }
13631659
1364− // record navigation (URL) changes
1365− // NOTE: in Chrome App environment, touching history.pushState, *even inside
1366− // a try/catch block*, will cause Chrome to output an error to console.error
1367− // borrowed from: https://github.com/angular/angular.js/pull/13945/files
1368− var chrome = _window.chrome;
1369− var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
1370− var hasPushAndReplaceState =
1371− !isChromePackagedApp &&
1372− _window.history &&
1373− history.pushState &&
1374− history.replaceState;
1375− if (autoBreadcrumbs.location && hasPushAndReplaceState) {
1376− // TODO: remove onpopstate handler on uninstall()
1377− var oldOnPopState = _window.onpopstate;
1378− _window.onpopstate = function() {
1379− var currentHref = self._location.href;
1380− self._captureUrlChange(self._lastHref, currentHref);
1381−
1382− if (oldOnPopState) {
1383− return oldOnPopState.apply(this, arguments);
1384− }
1385− };
1386−
1387− var historyReplacementFunction = function(origHistFunction) {
1388− // note history.pushState.length is 0; intentionally not declaring
1389− // params to preserve 0 arity
1390− return function(/* state, title, url */) {
1391− var url = arguments.length > 2 ? arguments[2] : undefined;
1392−
1393− // url argument is optional
1394− if (url) {
1395− // coerce to string (this is what pushState does)
1396− self._captureUrlChange(self._lastHref, url + '');
1397− }
1660+ return dsn;
1661+ },
13981662
1399− return origHistFunction.apply(this, arguments);
1400− };
1401− };
1663+ _getGlobalServer: function (uri) {
1664+ // assemble the endpoint from the uri pieces
1665+ var globalServer =
1666+ "//" + uri.host + (uri.port ? ":" + uri.port : "");
14021667
1403− fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns);
1404− fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns);
1405− }
1668+ if (uri.protocol) {
1669+ globalServer = uri.protocol + ":" + globalServer;
1670+ }
1671+ return globalServer;
1672+ },
14061673
1407− if (autoBreadcrumbs.console && 'console' in _window && console.log) {
1408− // console
1409− var consoleMethodCallback = function(msg, data) {
1410− self.captureBreadcrumb({
1411− message: msg,
1412− level: data.level,
1413− category: 'console'
1414− });
1415− };
1416−
1417− each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) {
1418− wrapConsoleMethod(console, level, consoleMethodCallback);
1419− });
1420− }
1421− },
1674+ _handleOnErrorStackInfo: function () {
1675+ // if we are intentionally ignoring errors via onerror, bail out
1676+ if (!this._ignoreOnError) {
1677+ this._handleStackInfo.apply(this, arguments);
1678+ }
1679+ },
14221680
1423− _restoreBuiltIns: function() {
1424− // restore any wrapped builtins
1425− var builtin;
1426− while (this._wrappedBuiltIns.length) {
1427− builtin = this._wrappedBuiltIns.shift();
1681+ _handleStackInfo: function (stackInfo, options) {
1682+ var frames = this._prepareFrames(stackInfo, options);
14281683
1429− var obj = builtin[0],
1430− name = builtin[1],
1431− orig = builtin[2];
1684+ this._triggerEvent("handle", {
1685+ stackInfo: stackInfo,
1686+ options: options,
1687+ });
14321688
1433− obj[name] = orig;
1434− }
1435− },
1436−
1437− _drainPlugins: function() {
1438− var self = this;
1439−
1440− // FIX ME TODO
1441− each(this._plugins, function(_, plugin) {
1442− var installer = plugin[0];
1443− var args = plugin[1];
1444− installer.apply(self, [self].concat(args));
1445− });
1446− },
1447−
1448− _parseDSN: function(str) {
1449− var m = dsnPattern.exec(str),
1450− dsn = {},
1451− i = 7;
1452−
1453− try {
1454− while (i--) dsn[dsnKeys[i]] = m[i] || '';
1455− } catch (e) {
1456− throw new RavenConfigError('Invalid DSN: ' + str);
1457− }
1689+ this._processException(
1690+ stackInfo.name,
1691+ stackInfo.message,
1692+ stackInfo.url,
1693+ stackInfo.lineno,
1694+ frames,
1695+ options,
1696+ );
1697+ },
1698+
1699+ _prepareFrames: function (stackInfo, options) {
1700+ var self = this;
1701+ var frames = [];
1702+ if (stackInfo.stack && stackInfo.stack.length) {
1703+ each(stackInfo.stack, function (i, stack) {
1704+ var frame = self._normalizeFrame(stack, stackInfo.url);
1705+ if (frame) {
1706+ frames.push(frame);
1707+ }
1708+ });
1709+
1710+ // e.g. frames captured via captureMessage throw
1711+ if (options && options.trimHeadFrames) {
1712+ for (
1713+ var j = 0;
1714+ j < options.trimHeadFrames && j < frames.length;
1715+ j++
1716+ ) {
1717+ frames[j].in_app = false;
1718+ }
1719+ }
1720+ }
1721+ frames = frames.slice(0, this._globalOptions.stackTraceLimit);
1722+ return frames;
1723+ },
1724+
1725+ _normalizeFrame: function (frame, stackInfoUrl) {
1726+ // normalize the frames data
1727+ var normalized = {
1728+ filename: frame.url,
1729+ lineno: frame.line,
1730+ colno: frame.column,
1731+ function: frame.func || "?",
1732+ };
14581733
1459− if (dsn.pass && !this._globalOptions.allowSecretKey) {
1460− throw new RavenConfigError(
1461− 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key'
1462− );
1463− }
1734+ // Case when we don't have any information about the error
1735+ // E.g. throwing a string or raw object, instead of an `Error` in Firefox
1736+ // Generating synthetic error doesn't add any value here
1737+ //
1738+ // We should probably somehow let a user know that they should fix their code
1739+ if (!frame.url) {
1740+ normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler
1741+ }
14641742
1465− return dsn;
1466− },
1743+ normalized.in_app = !(
1744+ // determine if an exception came from outside of our app
1745+ // first we check the global includePaths list.
1746+ (
1747+ (!!this._globalOptions.includePaths.test &&
1748+ !this._globalOptions.includePaths.test(
1749+ normalized.filename,
1750+ )) ||
1751+ // Now we check for fun, if the function name is Raven or TraceKit
1752+ /(Raven|TraceKit)\./.test(normalized["function"]) ||
1753+ // finally, we do a last ditch effort and check for raven.min.js
1754+ /raven\.(min\.)?js$/.test(normalized.filename)
1755+ )
1756+ );
1757+
1758+ return normalized;
1759+ },
1760+
1761+ _processException: function (
1762+ type,
1763+ message,
1764+ fileurl,
1765+ lineno,
1766+ frames,
1767+ options,
1768+ ) {
1769+ var prefixedMessage =
1770+ (type ? type + ": " : "") + (message || "");
1771+ if (
1772+ !!this._globalOptions.ignoreErrors.test &&
1773+ (this._globalOptions.ignoreErrors.test(message) ||
1774+ this._globalOptions.ignoreErrors.test(prefixedMessage))
1775+ ) {
1776+ return;
1777+ }
14671778
1468− _getGlobalServer: function(uri) {
1469− // assemble the endpoint from the uri pieces
1470− var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : '');
1779+ var stacktrace;
1780+
1781+ if (frames && frames.length) {
1782+ fileurl = frames[0].filename || fileurl;
1783+ // Sentry expects frames oldest to newest
1784+ // and JS sends them as newest to oldest
1785+ frames.reverse();
1786+ stacktrace = { frames: frames };
1787+ } else if (fileurl) {
1788+ stacktrace = {
1789+ frames: [
1790+ {
1791+ filename: fileurl,
1792+ lineno: lineno,
1793+ in_app: true,
1794+ },
1795+ ],
1796+ };
1797+ }
14711798
1472− if (uri.protocol) {
1473− globalServer = uri.protocol + ':' + globalServer;
1474− }
1475− return globalServer;
1476− },
1799+ if (
1800+ !!this._globalOptions.ignoreUrls.test &&
1801+ this._globalOptions.ignoreUrls.test(fileurl)
1802+ ) {
1803+ return;
1804+ }
14771805
1478− _handleOnErrorStackInfo: function() {
1479− // if we are intentionally ignoring errors via onerror, bail out
1480− if (!this._ignoreOnError) {
1481− this._handleStackInfo.apply(this, arguments);
1482− }
1483− },
1484−
1485− _handleStackInfo: function(stackInfo, options) {
1486− var frames = this._prepareFrames(stackInfo, options);
1487−
1488− this._triggerEvent('handle', {
1489− stackInfo: stackInfo,
1490− options: options
1491− });
1492−
1493− this._processException(
1494− stackInfo.name,
1495− stackInfo.message,
1496− stackInfo.url,
1497− stackInfo.lineno,
1498− frames,
1499− options
1500− );
1501− },
1502−
1503− _prepareFrames: function(stackInfo, options) {
1504− var self = this;
1505− var frames = [];
1506− if (stackInfo.stack && stackInfo.stack.length) {
1507− each(stackInfo.stack, function(i, stack) {
1508− var frame = self._normalizeFrame(stack, stackInfo.url);
1509− if (frame) {
1510− frames.push(frame);
1511− }
1512− });
1806+ if (
1807+ !!this._globalOptions.whitelistUrls.test &&
1808+ !this._globalOptions.whitelistUrls.test(fileurl)
1809+ ) {
1810+ return;
1811+ }
15131812
1514− // e.g. frames captured via captureMessage throw
1515− if (options && options.trimHeadFrames) {
1516− for (var j = 0; j < options.trimHeadFrames && j < frames.length; j++) {
1517− frames[j].in_app = false;
1518− }
1519− }
1520− }
1521− frames = frames.slice(0, this._globalOptions.stackTraceLimit);
1522− return frames;
1523− },
1524−
1525− _normalizeFrame: function(frame, stackInfoUrl) {
1526− // normalize the frames data
1527− var normalized = {
1528− filename: frame.url,
1529− lineno: frame.line,
1530− colno: frame.column,
1531− function: frame.func || '?'
1532− };
1533−
1534− // Case when we don't have any information about the error
1535− // E.g. throwing a string or raw object, instead of an `Error` in Firefox
1536− // Generating synthetic error doesn't add any value here
1537− //
1538− // We should probably somehow let a user know that they should fix their code
1539− if (!frame.url) {
1540− normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler
1541− }
1813+ var data = objectMerge(
1814+ {
1815+ // sentry.interfaces.Exception
1816+ exception: {
1817+ values: [
1818+ {
1819+ type: type,
1820+ value: message,
1821+ stacktrace: stacktrace,
1822+ },
1823+ ],
1824+ },
1825+ culprit: fileurl,
1826+ },
1827+ options,
1828+ );
1829+
1830+ // Fire away!
1831+ this._send(data);
1832+ },
1833+
1834+ _trimPacket: function (data) {
1835+ // For now, we only want to truncate the two different messages
1836+ // but this could/should be expanded to just trim everything
1837+ var max = this._globalOptions.maxMessageLength;
1838+ if (data.message) {
1839+ data.message = truncate(data.message, max);
1840+ }
1841+ if (data.exception) {
1842+ var exception = data.exception.values[0];
1843+ exception.value = truncate(exception.value, max);
1844+ }
15421845
1543− normalized.in_app = !// determine if an exception came from outside of our app
1544− // first we check the global includePaths list.
1545− (
1546− (!!this._globalOptions.includePaths.test &&
1547− !this._globalOptions.includePaths.test(normalized.filename)) ||
1548− // Now we check for fun, if the function name is Raven or TraceKit
1549− /(Raven|TraceKit)\./.test(normalized['function']) ||
1550− // finally, we do a last ditch effort and check for raven.min.js
1551− /raven\.(min\.)?js$/.test(normalized.filename)
1552− );
1553−
1554− return normalized;
1555− },
1556−
1557− _processException: function(type, message, fileurl, lineno, frames, options) {
1558− var prefixedMessage = (type ? type + ': ' : '') + (message || '');
1559− if (
1560− !!this._globalOptions.ignoreErrors.test &&
1561− (this._globalOptions.ignoreErrors.test(message) ||
1562− this._globalOptions.ignoreErrors.test(prefixedMessage))
1563− ) {
1564− return;
1565− }
1846+ var request = data.request;
1847+ if (request) {
1848+ if (request.url) {
1849+ request.url = truncate(
1850+ request.url,
1851+ this._globalOptions.maxUrlLength,
1852+ );
1853+ }
1854+ if (request.Referer) {
1855+ request.Referer = truncate(
1856+ request.Referer,
1857+ this._globalOptions.maxUrlLength,
1858+ );
1859+ }
1860+ }
15661861
1567− var stacktrace;
1568−
1569− if (frames && frames.length) {
1570− fileurl = frames[0].filename || fileurl;
1571− // Sentry expects frames oldest to newest
1572− // and JS sends them as newest to oldest
1573− frames.reverse();
1574− stacktrace = {frames: frames};
1575− } else if (fileurl) {
1576− stacktrace = {
1577− frames: [
1578− {
1579− filename: fileurl,
1580− lineno: lineno,
1581− in_app: true
1582− }
1583− ]
1584− };
1585− }
1862+ if (data.breadcrumbs && data.breadcrumbs.values)
1863+ this._trimBreadcrumbs(data.breadcrumbs);
1864+
1865+ return data;
1866+ },
1867+
1868+ /**
1869+ * Truncate breadcrumb values (right now just URLs)
1870+ */
1871+ _trimBreadcrumbs: function (breadcrumbs) {
1872+ // known breadcrumb properties with urls
1873+ // TODO: also consider arbitrary prop values that start with (https?)?://
1874+ var urlProps = ["to", "from", "url"],
1875+ urlProp,
1876+ crumb,
1877+ data;
1878+
1879+ for (var i = 0; i < breadcrumbs.values.length; ++i) {
1880+ crumb = breadcrumbs.values[i];
1881+ if (
1882+ !crumb.hasOwnProperty("data") ||
1883+ !isObject(crumb.data) ||
1884+ objectFrozen(crumb.data)
1885+ )
1886+ continue;
1887+
1888+ data = objectMerge({}, crumb.data);
1889+ for (var j = 0; j < urlProps.length; ++j) {
1890+ urlProp = urlProps[j];
1891+ if (data.hasOwnProperty(urlProp) && data[urlProp]) {
1892+ data[urlProp] = truncate(
1893+ data[urlProp],
1894+ this._globalOptions.maxUrlLength,
1895+ );
1896+ }
1897+ }
1898+ breadcrumbs.values[i].data = data;
1899+ }
1900+ },
15861901
1587− if (
1588− !!this._globalOptions.ignoreUrls.test &&
1589− this._globalOptions.ignoreUrls.test(fileurl)
1590− ) {
1591− return;
1592− }
1902+ _getHttpData: function () {
1903+ if (!this._hasNavigator && !this._hasDocument) return;
1904+ var httpData = {};
15931905
1594− if (
1595− !!this._globalOptions.whitelistUrls.test &&
1596− !this._globalOptions.whitelistUrls.test(fileurl)
1597− ) {
1598− return;
1599− }
1906+ if (this._hasNavigator && _navigator.userAgent) {
1907+ httpData.headers = {
1908+ "User-Agent": navigator.userAgent,
1909+ };
1910+ }
16001911
1601− var data = objectMerge(
1602− {
1603− // sentry.interfaces.Exception
1604− exception: {
1605− values: [
1606− {
1607− type: type,
1608− value: message,
1609− stacktrace: stacktrace
1610− }
1611− ]
1612− },
1613− culprit: fileurl
1614− },
1615− options
1616− );
1617−
1618− // Fire away!
1619− this._send(data);
1620− },
1621−
1622− _trimPacket: function(data) {
1623− // For now, we only want to truncate the two different messages
1624− // but this could/should be expanded to just trim everything
1625− var max = this._globalOptions.maxMessageLength;
1626− if (data.message) {
1627− data.message = truncate(data.message, max);
1628− }
1629− if (data.exception) {
1630− var exception = data.exception.values[0];
1631− exception.value = truncate(exception.value, max);
1632− }
1633−
1634− var request = data.request;
1635− if (request) {
1636− if (request.url) {
1637− request.url = truncate(request.url, this._globalOptions.maxUrlLength);
1638− }
1639− if (request.Referer) {
1640− request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength);
1641− }
1642− }
1643−
1644− if (data.breadcrumbs && data.breadcrumbs.values)
1645− this._trimBreadcrumbs(data.breadcrumbs);
1646−
1647− return data;
1648− },
1649−
1650− /**
1651− * Truncate breadcrumb values (right now just URLs)
1652− */
1653− _trimBreadcrumbs: function(breadcrumbs) {
1654− // known breadcrumb properties with urls
1655− // TODO: also consider arbitrary prop values that start with (https?)?://
1656− var urlProps = ['to', 'from', 'url'],
1657− urlProp,
1658− crumb,
1659− data;
1660−
1661− for (var i = 0; i < breadcrumbs.values.length; ++i) {
1662− crumb = breadcrumbs.values[i];
1663− if (
1664− !crumb.hasOwnProperty('data') ||
1665− !isObject(crumb.data) ||
1666− objectFrozen(crumb.data)
1667− )
1668− continue;
1669−
1670− data = objectMerge({}, crumb.data);
1671− for (var j = 0; j < urlProps.length; ++j) {
1672− urlProp = urlProps[j];
1673− if (data.hasOwnProperty(urlProp) && data[urlProp]) {
1674− data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength);
1675− }
1676− }
1677− breadcrumbs.values[i].data = data;
1678− }
1679− },
1680−
1681− _getHttpData: function() {
1682− if (!this._hasNavigator && !this._hasDocument) return;
1683− var httpData = {};
1684−
1685− if (this._hasNavigator && _navigator.userAgent) {
1686− httpData.headers = {
1687− 'User-Agent': navigator.userAgent
1688− };
1689− }
1690−
1691− if (this._hasDocument) {
1692− if (_document.location && _document.location.href) {
1693− httpData.url = _document.location.href;
1694− }
1695− if (_document.referrer) {
1696− if (!httpData.headers) httpData.headers = {};
1697− httpData.headers.Referer = _document.referrer;
1698− }
1699− }
1700−
1701− return httpData;
1702− },
1703−
1704− _resetBackoff: function() {
1705− this._backoffDuration = 0;
1706− this._backoffStart = null;
1707− },
1708−
1709− _shouldBackoff: function() {
1710− return this._backoffDuration && now() - this._backoffStart < this._backoffDuration;
1711− },
1712−
1713− /**
1714− * Returns true if the in-process data payload matches the signature
1715− * of the previously-sent data
1716− *
1717− * NOTE: This has to be done at this level because TraceKit can generate
1718− * data from window.onerror WITHOUT an exception object (IE8, IE9,
1719− * other old browsers). This can take the form of an "exception"
1720− * data object with a single frame (derived from the onerror args).
1721− */
1722− _isRepeatData: function(current) {
1723− var last = this._lastData;
1724−
1725− if (
1726− !last ||
1727− current.message !== last.message || // defined for captureMessage
1728− current.culprit !== last.culprit // defined for captureException/onerror
1729− )
1730− return false;
1731−
1732− // Stacktrace interface (i.e. from captureMessage)
1733− if (current.stacktrace || last.stacktrace) {
1734− return isSameStacktrace(current.stacktrace, last.stacktrace);
1735− } else if (current.exception || last.exception) {
1736− // Exception interface (i.e. from captureException/onerror)
1737− return isSameException(current.exception, last.exception);
1738− }
1739−
1740− return true;
1741− },
1742−
1743− _setBackoffState: function(request) {
1744− // If we are already in a backoff state, don't change anything
1745− if (this._shouldBackoff()) {
1746− return;
1747− }
1748−
1749− var status = request.status;
1750−
1751− // 400 - project_id doesn't exist or some other fatal
1752− // 401 - invalid/revoked dsn
1753− // 429 - too many requests
1754− if (!(status === 400 || status === 401 || status === 429)) return;
1755−
1756− var retry;
1757− try {
1758− // If Retry-After is not in Access-Control-Expose-Headers, most
1759− // browsers will throw an exception trying to access it
1760− retry = request.getResponseHeader('Retry-After');
1761− retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds
1762− } catch (e) {
1763− /* eslint no-empty:0 */
1764− }
1765−
1766− this._backoffDuration = retry
1767− ? // If Sentry server returned a Retry-After value, use it
1768− retry
1769− : // Otherwise, double the last backoff duration (starts at 1 sec)
1770− this._backoffDuration * 2 || 1000;
1912+ if (this._hasDocument) {
1913+ if (_document.location && _document.location.href) {
1914+ httpData.url = _document.location.href;
1915+ }
1916+ if (_document.referrer) {
1917+ if (!httpData.headers) httpData.headers = {};
1918+ httpData.headers.Referer = _document.referrer;
1919+ }
1920+ }
17711921
1772− this._backoffStart = now();
1773− },
1922+ return httpData;
1923+ },
1924+
1925+ _resetBackoff: function () {
1926+ this._backoffDuration = 0;
1927+ this._backoffStart = null;
1928+ },
1929+
1930+ _shouldBackoff: function () {
1931+ return (
1932+ this._backoffDuration &&
1933+ now() - this._backoffStart < this._backoffDuration
1934+ );
1935+ },
1936+
1937+ /**
1938+ * Returns true if the in-process data payload matches the signature
1939+ * of the previously-sent data
1940+ *
1941+ * NOTE: This has to be done at this level because TraceKit can generate
1942+ * data from window.onerror WITHOUT an exception object (IE8, IE9,
1943+ * other old browsers). This can take the form of an "exception"
1944+ * data object with a single frame (derived from the onerror args).
1945+ */
1946+ _isRepeatData: function (current) {
1947+ var last = this._lastData;
1948+
1949+ if (
1950+ !last ||
1951+ current.message !== last.message || // defined for captureMessage
1952+ current.culprit !== last.culprit // defined for captureException/onerror
1953+ )
1954+ return false;
1955+
1956+ // Stacktrace interface (i.e. from captureMessage)
1957+ if (current.stacktrace || last.stacktrace) {
1958+ return isSameStacktrace(current.stacktrace, last.stacktrace);
1959+ } else if (current.exception || last.exception) {
1960+ // Exception interface (i.e. from captureException/onerror)
1961+ return isSameException(current.exception, last.exception);
1962+ }
17741963
1775− _send: function(data) {
1776− var globalOptions = this._globalOptions;
1964+ return true;
1965+ },
17771966
1778− var baseData = {
1779− project: this._globalProject,
1780− logger: globalOptions.logger,
1781− platform: 'javascript'
1782− },
1783− httpData = this._getHttpData();
1967+ _setBackoffState: function (request) {
1968+ // If we are already in a backoff state, don't change anything
1969+ if (this._shouldBackoff()) {
1970+ return;
1971+ }
17841972
1785− if (httpData) {
1786− baseData.request = httpData;
1787− }
1973+ var status = request.status;
17881974
1789− // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload
1790− if (data.trimHeadFrames) delete data.trimHeadFrames;
1975+ // 400 - project_id doesn't exist or some other fatal
1976+ // 401 - invalid/revoked dsn
1977+ // 429 - too many requests
1978+ if (!(status === 400 || status === 401 || status === 429))
1979+ return;
17911980
1792− data = objectMerge(baseData, data);
1981+ var retry;
1982+ try {
1983+ // If Retry-After is not in Access-Control-Expose-Headers, most
1984+ // browsers will throw an exception trying to access it
1985+ retry = request.getResponseHeader("Retry-After");
1986+ retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds
1987+ } catch (e) {
1988+ /* eslint no-empty:0 */
1989+ }
17931990
1794− // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
1795− data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags);
1796− data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra);
1991+ this._backoffDuration = retry
1992+ ? // If Sentry server returned a Retry-After value, use it
1993+ retry
1994+ : // Otherwise, double the last backoff duration (starts at 1 sec)
1995+ this._backoffDuration * 2 || 1000;
17971996
1798− // Send along our own collected metadata with extra
1799− data.extra['session:duration'] = now() - this._startTime;
1997+ this._backoffStart = now();
1998+ },
18001999
1801− if (this._breadcrumbs && this._breadcrumbs.length > 0) {
1802− // intentionally make shallow copy so that additions
1803− // to breadcrumbs aren't accidentally sent in this request
1804− data.breadcrumbs = {
1805− values: [].slice.call(this._breadcrumbs, 0)
1806− };
1807− }
2000+ _send: function (data) {
2001+ var globalOptions = this._globalOptions;
18082002
1809− // If there are no tags/extra, strip the key from the payload alltogther.
1810− if (isEmptyObject(data.tags)) delete data.tags;
2003+ var baseData = {
2004+ project: this._globalProject,
2005+ logger: globalOptions.logger,
2006+ platform: "javascript",
2007+ },
2008+ httpData = this._getHttpData();
18112009
1812− if (this._globalContext.user) {
1813− // sentry.interfaces.User
1814− data.user = this._globalContext.user;
1815− }
2010+ if (httpData) {
2011+ baseData.request = httpData;
2012+ }
18162013
1817− // Include the environment if it's defined in globalOptions
1818− if (globalOptions.environment) data.environment = globalOptions.environment;
2014+ // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload
2015+ if (data.trimHeadFrames) delete data.trimHeadFrames;
2016+
2017+ data = objectMerge(baseData, data);
2018+
2019+ // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
2020+ data.tags = objectMerge(
2021+ objectMerge({}, this._globalContext.tags),
2022+ data.tags,
2023+ );
2024+ data.extra = objectMerge(
2025+ objectMerge({}, this._globalContext.extra),
2026+ data.extra,
2027+ );
2028+
2029+ // Send along our own collected metadata with extra
2030+ data.extra["session:duration"] = now() - this._startTime;
2031+
2032+ if (this._breadcrumbs && this._breadcrumbs.length > 0) {
2033+ // intentionally make shallow copy so that additions
2034+ // to breadcrumbs aren't accidentally sent in this request
2035+ data.breadcrumbs = {
2036+ values: [].slice.call(this._breadcrumbs, 0),
2037+ };
2038+ }
18192039
1820− // Include the release if it's defined in globalOptions
1821− if (globalOptions.release) data.release = globalOptions.release;
2040+ // If there are no tags/extra, strip the key from the payload alltogther.
2041+ if (isEmptyObject(data.tags)) delete data.tags;
18222042
1823− // Include server_name if it's defined in globalOptions
1824− if (globalOptions.serverName) data.server_name = globalOptions.serverName;
2043+ if (this._globalContext.user) {
2044+ // sentry.interfaces.User
2045+ data.user = this._globalContext.user;
2046+ }
18252047
1826− if (isFunction(globalOptions.dataCallback)) {
1827− data = globalOptions.dataCallback(data) || data;
1828− }
2048+ // Include the environment if it's defined in globalOptions
2049+ if (globalOptions.environment)
2050+ data.environment = globalOptions.environment;
18292051
1830− // Why??????????
1831− if (!data || isEmptyObject(data)) {
1832− return;
1833− }
2052+ // Include the release if it's defined in globalOptions
2053+ if (globalOptions.release) data.release = globalOptions.release;
18342054
1835− // Check if the request should be filtered or not
1836− if (
1837− isFunction(globalOptions.shouldSendCallback) &&
1838− !globalOptions.shouldSendCallback(data)
1839− ) {
1840− return;
1841− }
2055+ // Include server_name if it's defined in globalOptions
2056+ if (globalOptions.serverName)
2057+ data.server_name = globalOptions.serverName;
18422058
1843− // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests),
1844− // so drop requests until "cool-off" period has elapsed.
1845− if (this._shouldBackoff()) {
1846− this._logDebug('warn', 'Raven dropped error due to backoff: ', data);
1847− return;
1848− }
2059+ if (isFunction(globalOptions.dataCallback)) {
2060+ data = globalOptions.dataCallback(data) || data;
2061+ }
18492062
1850− if (typeof globalOptions.sampleRate === 'number') {
1851− if (Math.random() < globalOptions.sampleRate) {
1852− this._sendProcessedPayload(data);
1853− }
1854− } else {
1855− this._sendProcessedPayload(data);
1856− }
1857− },
2063+ // Why??????????
2064+ if (!data || isEmptyObject(data)) {
2065+ return;
2066+ }
18582067
1859− _getUuid: function() {
1860− return uuid4();
1861− },
2068+ // Check if the request should be filtered or not
2069+ if (
2070+ isFunction(globalOptions.shouldSendCallback) &&
2071+ !globalOptions.shouldSendCallback(data)
2072+ ) {
2073+ return;
2074+ }
18622075
1863− _sendProcessedPayload: function(data, callback) {
1864− var self = this;
1865− var globalOptions = this._globalOptions;
2076+ // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests),
2077+ // so drop requests until "cool-off" period has elapsed.
2078+ if (this._shouldBackoff()) {
2079+ this._logDebug(
2080+ "warn",
2081+ "Raven dropped error due to backoff: ",
2082+ data,
2083+ );
2084+ return;
2085+ }
18662086
1867− if (!this.isSetup()) return;
2087+ if (typeof globalOptions.sampleRate === "number") {
2088+ if (Math.random() < globalOptions.sampleRate) {
2089+ this._sendProcessedPayload(data);
2090+ }
2091+ } else {
2092+ this._sendProcessedPayload(data);
2093+ }
2094+ },
2095+
2096+ _getUuid: function () {
2097+ return uuid4();
2098+ },
2099+
2100+ _sendProcessedPayload: function (data, callback) {
2101+ var self = this;
2102+ var globalOptions = this._globalOptions;
2103+
2104+ if (!this.isSetup()) return;
2105+
2106+ // Try and clean up the packet before sending by truncating long values
2107+ data = this._trimPacket(data);
2108+
2109+ // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback,
2110+ // but this would require copying an un-truncated copy of the data packet, which can be
2111+ // arbitrarily deep (extra_data) -- could be worthwhile? will revisit
2112+ if (
2113+ !this._globalOptions.allowDuplicates &&
2114+ this._isRepeatData(data)
2115+ ) {
2116+ this._logDebug("warn", "Raven dropped repeat event: ", data);
2117+ return;
2118+ }
18682119
1869− // Try and clean up the packet before sending by truncating long values
1870− data = this._trimPacket(data);
2120+ // Send along an event_id if not explicitly passed.
2121+ // This event_id can be used to reference the error within Sentry itself.
2122+ // Set lastEventId after we know the error should actually be sent
2123+ this._lastEventId =
2124+ data.event_id || (data.event_id = this._getUuid());
18712125
1872− // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback,
1873− // but this would require copying an un-truncated copy of the data packet, which can be
1874− // arbitrarily deep (extra_data) -- could be worthwhile? will revisit
1875− if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) {
1876− this._logDebug('warn', 'Raven dropped repeat event: ', data);
1877− return;
1878− }
2126+ // Store outbound payload after trim
2127+ this._lastData = data;
18792128
1880− // Send along an event_id if not explicitly passed.
1881− // This event_id can be used to reference the error within Sentry itself.
1882− // Set lastEventId after we know the error should actually be sent
1883− this._lastEventId = data.event_id || (data.event_id = this._getUuid());
2129+ this._logDebug("debug", "Raven about to send:", data);
18842130
1885− // Store outbound payload after trim
1886− this._lastData = data;
2131+ var auth = {
2132+ sentry_version: "7",
2133+ sentry_client: "raven-js/" + this.VERSION,
2134+ sentry_key: this._globalKey,
2135+ };
18872136
1888− this._logDebug('debug', 'Raven about to send:', data);
2137+ if (this._globalSecret) {
2138+ auth.sentry_secret = this._globalSecret;
2139+ }
18892140
1890− var auth = {
1891− sentry_version: '7',
1892− sentry_client: 'raven-js/' + this.VERSION,
1893− sentry_key: this._globalKey
1894− };
2141+ var exception = data.exception && data.exception.values[0];
2142+
2143+ // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy
2144+ if (
2145+ this._globalOptions.autoBreadcrumbs &&
2146+ this._globalOptions.autoBreadcrumbs.sentry
2147+ ) {
2148+ this.captureBreadcrumb({
2149+ category: "sentry",
2150+ message: exception
2151+ ? (exception.type ? exception.type + ": " : "") +
2152+ exception.value
2153+ : data.message,
2154+ event_id: data.event_id,
2155+ level: data.level || "error", // presume error unless specified
2156+ });
2157+ }
18952158
1896− if (this._globalSecret) {
1897− auth.sentry_secret = this._globalSecret;
1898− }
2159+ var url = this._globalEndpoint;
2160+ (globalOptions.transport || this._makeRequest).call(this, {
2161+ url: url,
2162+ auth: auth,
2163+ data: data,
2164+ options: globalOptions,
2165+ onSuccess: function success() {
2166+ self._resetBackoff();
2167+
2168+ self._triggerEvent("success", {
2169+ data: data,
2170+ src: url,
2171+ });
2172+ callback && callback();
2173+ },
2174+ onError: function failure(error) {
2175+ self._logDebug(
2176+ "error",
2177+ "Raven transport failed to send: ",
2178+ error,
2179+ );
2180+
2181+ if (error.request) {
2182+ self._setBackoffState(error.request);
2183+ }
2184+
2185+ self._triggerEvent("failure", {
2186+ data: data,
2187+ src: url,
2188+ });
2189+ error =
2190+ error ||
2191+ new Error(
2192+ "Raven send failed (no additional details provided)",
2193+ );
2194+ callback && callback(error);
2195+ },
2196+ });
2197+ },
2198+
2199+ _makeRequest: function (opts) {
2200+ var request =
2201+ _window.XMLHttpRequest && new _window.XMLHttpRequest();
2202+ if (!request) return;
2203+
2204+ // if browser doesn't support CORS (e.g. IE7), we are out of luck
2205+ var hasCORS =
2206+ "withCredentials" in request ||
2207+ typeof XDomainRequest !== "undefined";
2208+
2209+ if (!hasCORS) return;
2210+
2211+ var url = opts.url;
2212+
2213+ if ("withCredentials" in request) {
2214+ request.onreadystatechange = function () {
2215+ if (request.readyState !== 4) {
2216+ return;
2217+ } else if (request.status === 200) {
2218+ opts.onSuccess && opts.onSuccess();
2219+ } else if (opts.onError) {
2220+ var err = new Error(
2221+ "Sentry error code: " + request.status,
2222+ );
2223+ err.request = request;
2224+ opts.onError(err);
2225+ }
2226+ };
2227+ } else {
2228+ request = new XDomainRequest();
2229+ // xdomainrequest cannot go http -> https (or vice versa),
2230+ // so always use protocol relative
2231+ url = url.replace(/^https?:/, "");
2232+
2233+ // onreadystatechange not supported by XDomainRequest
2234+ if (opts.onSuccess) {
2235+ request.onload = opts.onSuccess;
2236+ }
2237+ if (opts.onError) {
2238+ request.onerror = function () {
2239+ var err = new Error("Sentry error code: XDomainRequest");
2240+ err.request = request;
2241+ opts.onError(err);
2242+ };
2243+ }
2244+ }
18992245
1900− var exception = data.exception && data.exception.values[0];
1901−
1902− // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy
1903− if (
1904− this._globalOptions.autoBreadcrumbs &&
1905− this._globalOptions.autoBreadcrumbs.sentry
1906− ) {
1907− this.captureBreadcrumb({
1908− category: 'sentry',
1909− message: exception
1910− ? (exception.type ? exception.type + ': ' : '') + exception.value
1911− : data.message,
1912− event_id: data.event_id,
1913− level: data.level || 'error' // presume error unless specified
1914− });
1915− }
2246+ // NOTE: auth is intentionally sent as part of query string (NOT as custom
2247+ // HTTP header) so as to avoid preflight CORS requests
2248+ request.open("POST", url + "?" + urlencode(opts.auth));
2249+ request.send(stringify(opts.data));
2250+ },
2251+
2252+ _logDebug: function (level) {
2253+ if (this._originalConsoleMethods[level] && this.debug) {
2254+ // In IE<10 console methods do not have their own 'apply' method
2255+ Function.prototype.apply.call(
2256+ this._originalConsoleMethods[level],
2257+ this._originalConsole,
2258+ [].slice.call(arguments, 1),
2259+ );
2260+ }
2261+ },
2262+
2263+ _mergeContext: function (key, context) {
2264+ if (isUndefined(context)) {
2265+ delete this._globalContext[key];
2266+ } else {
2267+ this._globalContext[key] = objectMerge(
2268+ this._globalContext[key] || {},
2269+ context,
2270+ );
2271+ }
2272+ },
2273+ };
19162274
1917− var url = this._globalEndpoint;
1918− (globalOptions.transport || this._makeRequest).call(this, {
1919− url: url,
1920− auth: auth,
1921− data: data,
1922− options: globalOptions,
1923− onSuccess: function success() {
1924− self._resetBackoff();
1925−
1926− self._triggerEvent('success', {
1927− data: data,
1928− src: url
1929− });
1930− callback && callback();
1931− },
1932− onError: function failure(error) {
1933− self._logDebug('error', 'Raven transport failed to send: ', error);
1934−
1935− if (error.request) {
1936− self._setBackoffState(error.request);
1937− }
2275+ // Deprecations
2276+ Raven.prototype.setUser = Raven.prototype.setUserContext;
2277+ Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
2278+
2279+ module.exports = Raven;
2280+ }).call(
2281+ this,
2282+ typeof global !== "undefined"
2283+ ? global
2284+ : typeof self !== "undefined"
2285+ ? self
2286+ : typeof window !== "undefined"
2287+ ? window
2288+ : {},
2289+ );
2290+ },
2291+ { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7 },
2292+ ],
2293+ 4: [
2294+ function (_dereq_, module, exports) {
2295+ (function (global) {
2296+ /**
2297+ * Enforces a single instance of the Raven client, and the
2298+ * main entry point for Raven. If you are a consumer of the
2299+ * Raven library, you SHOULD load this file (vs raven.js).
2300+ **/
2301+
2302+ var RavenConstructor = _dereq_(3);
2303+
2304+ // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
2305+ var _window =
2306+ typeof window !== "undefined"
2307+ ? window
2308+ : typeof global !== "undefined"
2309+ ? global
2310+ : typeof self !== "undefined"
2311+ ? self
2312+ : {};
2313+ var _Raven = _window.Raven;
2314+
2315+ var Raven = new RavenConstructor();
2316+
2317+ /*
2318+ * Allow multiple versions of Raven to be installed.
2319+ * Strip Raven from the global context and returns the instance.
2320+ *
2321+ * @return {Raven}
2322+ */
2323+ Raven.noConflict = function () {
2324+ _window.Raven = _Raven;
2325+ return Raven;
2326+ };
19382327
1939− self._triggerEvent('failure', {
1940− data: data,
1941− src: url
1942− });
1943− error = error || new Error('Raven send failed (no additional details provided)');
1944− callback && callback(error);
1945− }
1946− });
1947− },
1948−
1949− _makeRequest: function(opts) {
1950− var request = _window.XMLHttpRequest && new _window.XMLHttpRequest();
1951− if (!request) return;
1952−
1953− // if browser doesn't support CORS (e.g. IE7), we are out of luck
1954− var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined';
1955−
1956− if (!hasCORS) return;
1957−
1958− var url = opts.url;
1959−
1960− if ('withCredentials' in request) {
1961− request.onreadystatechange = function() {
1962− if (request.readyState !== 4) {
1963− return;
1964− } else if (request.status === 200) {
1965− opts.onSuccess && opts.onSuccess();
1966− } else if (opts.onError) {
1967− var err = new Error('Sentry error code: ' + request.status);
1968− err.request = request;
1969− opts.onError(err);
1970− }
1971− };
1972− } else {
1973− request = new XDomainRequest();
1974− // xdomainrequest cannot go http -> https (or vice versa),
1975− // so always use protocol relative
1976− url = url.replace(/^https?:/, '');
1977−
1978− // onreadystatechange not supported by XDomainRequest
1979− if (opts.onSuccess) {
1980− request.onload = opts.onSuccess;
1981− }
1982− if (opts.onError) {
1983− request.onerror = function() {
1984− var err = new Error('Sentry error code: XDomainRequest');
1985− err.request = request;
1986− opts.onError(err);
1987− };
1988− }
1989− }
2328+ Raven.afterLoad();
2329+
2330+ module.exports = Raven;
2331+ }).call(
2332+ this,
2333+ typeof global !== "undefined"
2334+ ? global
2335+ : typeof self !== "undefined"
2336+ ? self
2337+ : typeof window !== "undefined"
2338+ ? window
2339+ : {},
2340+ );
2341+ },
2342+ { 3: 3 },
2343+ ],
2344+ 5: [
2345+ function (_dereq_, module, exports) {
2346+ (function (global) {
2347+ var _window =
2348+ typeof window !== "undefined"
2349+ ? window
2350+ : typeof global !== "undefined"
2351+ ? global
2352+ : typeof self !== "undefined"
2353+ ? self
2354+ : {};
2355+
2356+ function isObject(what) {
2357+ return typeof what === "object" && what !== null;
2358+ }
19902359
1991− // NOTE: auth is intentionally sent as part of query string (NOT as custom
1992− // HTTP header) so as to avoid preflight CORS requests
1993− request.open('POST', url + '?' + urlencode(opts.auth));
1994− request.send(stringify(opts.data));
1995− },
1996−
1997− _logDebug: function(level) {
1998− if (this._originalConsoleMethods[level] && this.debug) {
1999− // In IE<10 console methods do not have their own 'apply' method
2000− Function.prototype.apply.call(
2001− this._originalConsoleMethods[level],
2002− this._originalConsole,
2003− [].slice.call(arguments, 1)
2004− );
2005− }
2006− },
2360+ // Yanked from https://git.io/vS8DV re-used under CC0
2361+ // with some tiny modifications
2362+ function isError(value) {
2363+ switch ({}.toString.call(value)) {
2364+ case "[object Error]":
2365+ return true;
2366+ case "[object Exception]":
2367+ return true;
2368+ case "[object DOMException]":
2369+ return true;
2370+ default:
2371+ return value instanceof Error;
2372+ }
2373+ }
20072374
2008− _mergeContext: function(key, context) {
2009− if (isUndefined(context)) {
2010− delete this._globalContext[key];
2011− } else {
2012− this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context);
2013− }
2014− }
2015−};
2375+ function isErrorEvent(value) {
2376+ return (
2377+ supportsErrorEvent() &&
2378+ {}.toString.call(value) === "[object ErrorEvent]"
2379+ );
2380+ }
20162381
2017−// Deprecations
2018−Raven.prototype.setUser = Raven.prototype.setUserContext;
2019−Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
2382+ function isUndefined(what) {
2383+ return what === void 0;
2384+ }
20202385
2021−module.exports = Raven;
2386+ function isFunction(what) {
2387+ return typeof what === "function";
2388+ }
20222389
2023−}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2024−},{"1":1,"2":2,"5":5,"6":6,"7":7}],4:[function(_dereq_,module,exports){
2025−(function (global){
2026−/**
2027− * Enforces a single instance of the Raven client, and the
2028− * main entry point for Raven. If you are a consumer of the
2029− * Raven library, you SHOULD load this file (vs raven.js).
2030− **/
2390+ function isString(what) {
2391+ return Object.prototype.toString.call(what) === "[object String]";
2392+ }
20312393
2032−var RavenConstructor = _dereq_(3);
2394+ function isArray(what) {
2395+ return Object.prototype.toString.call(what) === "[object Array]";
2396+ }
20332397
2034−// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
2035−var _window =
2036− typeof window !== 'undefined'
2037− ? window
2038− : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2039−var _Raven = _window.Raven;
2398+ function isEmptyObject(what) {
2399+ for (var _ in what) {
2400+ if (what.hasOwnProperty(_)) {
2401+ return false;
2402+ }
2403+ }
2404+ return true;
2405+ }
20402406
2041−var Raven = new RavenConstructor();
2407+ function supportsErrorEvent() {
2408+ try {
2409+ new ErrorEvent(""); // eslint-disable-line no-new
2410+ return true;
2411+ } catch (e) {
2412+ return false;
2413+ }
2414+ }
20422415
2043−/*
2044− * Allow multiple versions of Raven to be installed.
2045− * Strip Raven from the global context and returns the instance.
2046− *
2047− * @return {Raven}
2048− */
2049−Raven.noConflict = function() {
2050− _window.Raven = _Raven;
2051− return Raven;
2052−};
2053−
2054−Raven.afterLoad();
2055−
2056−module.exports = Raven;
2057−
2058−}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2059−},{"3":3}],5:[function(_dereq_,module,exports){
2060−(function (global){
2061−var _window =
2062− typeof window !== 'undefined'
2063− ? window
2064− : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2065−
2066−function isObject(what) {
2067− return typeof what === 'object' && what !== null;
2068−}
2069−
2070−// Yanked from https://git.io/vS8DV re-used under CC0
2071−// with some tiny modifications
2072−function isError(value) {
2073− switch ({}.toString.call(value)) {
2074− case '[object Error]':
2075− return true;
2076− case '[object Exception]':
2077− return true;
2078− case '[object DOMException]':
2079− return true;
2080− default:
2081− return value instanceof Error;
2082− }
2083−}
2416+ function wrappedCallback(callback) {
2417+ function dataCallback(data, original) {
2418+ var normalizedData = callback(data) || data;
2419+ if (original) {
2420+ return original(normalizedData) || normalizedData;
2421+ }
2422+ return normalizedData;
2423+ }
20842424
2085−function isErrorEvent(value) {
2086− return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]';
2087−}
2425+ return dataCallback;
2426+ }
20882427
2089−function isUndefined(what) {
2090− return what === void 0;
2091−}
2428+ function each(obj, callback) {
2429+ var i, j;
20922430
2093−function isFunction(what) {
2094− return typeof what === 'function';
2095−}
2431+ if (isUndefined(obj.length)) {
2432+ for (i in obj) {
2433+ if (hasKey(obj, i)) {
2434+ callback.call(null, i, obj[i]);
2435+ }
2436+ }
2437+ } else {
2438+ j = obj.length;
2439+ if (j) {
2440+ for (i = 0; i < j; i++) {
2441+ callback.call(null, i, obj[i]);
2442+ }
2443+ }
2444+ }
2445+ }
20962446
2097−function isString(what) {
2098− return Object.prototype.toString.call(what) === '[object String]';
2099−}
2447+ function objectMerge(obj1, obj2) {
2448+ if (!obj2) {
2449+ return obj1;
2450+ }
2451+ each(obj2, function (key, value) {
2452+ obj1[key] = value;
2453+ });
2454+ return obj1;
2455+ }
21002456
2101−function isArray(what) {
2102− return Object.prototype.toString.call(what) === '[object Array]';
2103−}
2457+ /**
2458+ * This function is only used for react-native.
2459+ * react-native freezes object that have already been sent over the
2460+ * js bridge. We need this function in order to check if the object is frozen.
2461+ * So it's ok that objectFrozen returns false if Object.isFrozen is not
2462+ * supported because it's not relevant for other "platforms". See related issue:
2463+ * https://github.com/getsentry/react-native-sentry/issues/57
2464+ */
2465+ function objectFrozen(obj) {
2466+ if (!Object.isFrozen) {
2467+ return false;
2468+ }
2469+ return Object.isFrozen(obj);
2470+ }
21042471
2105−function isEmptyObject(what) {
2106− for (var _ in what) {
2107− if (what.hasOwnProperty(_)) {
2108− return false;
2109− }
2110− }
2111− return true;
2112−}
2113−
2114−function supportsErrorEvent() {
2115− try {
2116− new ErrorEvent(''); // eslint-disable-line no-new
2117− return true;
2118− } catch (e) {
2119− return false;
2120− }
2121−}
2472+ function truncate(str, max) {
2473+ return !max || str.length <= max
2474+ ? str
2475+ : str.substr(0, max) + "\u2026";
2476+ }
21222477
2123−function wrappedCallback(callback) {
2124− function dataCallback(data, original) {
2125− var normalizedData = callback(data) || data;
2126− if (original) {
2127− return original(normalizedData) || normalizedData;
2128− }
2129− return normalizedData;
2130− }
2478+ /**
2479+ * hasKey, a better form of hasOwnProperty
2480+ * Example: hasKey(MainHostObject, property) === true/false
2481+ *
2482+ * @param {Object} host object to check property
2483+ * @param {string} key to check
2484+ */
2485+ function hasKey(object, key) {
2486+ return Object.prototype.hasOwnProperty.call(object, key);
2487+ }
21312488
2132− return dataCallback;
2133−}
2489+ function joinRegExp(patterns) {
2490+ // Combine an array of regular expressions and strings into one large regexp
2491+ // Be mad.
2492+ var sources = [],
2493+ i = 0,
2494+ len = patterns.length,
2495+ pattern;
2496+
2497+ for (; i < len; i++) {
2498+ pattern = patterns[i];
2499+ if (isString(pattern)) {
2500+ // If it's a string, we need to escape it
2501+ // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
2502+ sources.push(
2503+ pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"),
2504+ );
2505+ } else if (pattern && pattern.source) {
2506+ // If it's a regexp already, we want to extract the source
2507+ sources.push(pattern.source);
2508+ }
2509+ // Intentionally skip other cases
2510+ }
2511+ return new RegExp(sources.join("|"), "i");
2512+ }
21342513
2135−function each(obj, callback) {
2136− var i, j;
2514+ function urlencode(o) {
2515+ var pairs = [];
2516+ each(o, function (key, value) {
2517+ pairs.push(
2518+ encodeURIComponent(key) + "=" + encodeURIComponent(value),
2519+ );
2520+ });
2521+ return pairs.join("&");
2522+ }
21372523
2138− if (isUndefined(obj.length)) {
2139− for (i in obj) {
2140− if (hasKey(obj, i)) {
2141− callback.call(null, i, obj[i]);
2142− }
2143− }
2144− } else {
2145− j = obj.length;
2146− if (j) {
2147− for (i = 0; i < j; i++) {
2148− callback.call(null, i, obj[i]);
2149− }
2150− }
2151− }
2152−}
2524+ // borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B
2525+ // intentionally using regex and not <a/> href parsing trick because React Native and other
2526+ // environments where DOM might not be available
2527+ function parseUrl(url) {
2528+ var match = url.match(
2529+ /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/,
2530+ );
2531+ if (!match) return {};
2532+
2533+ // coerce to undefined values to empty string so we don't get 'undefined'
2534+ var query = match[6] || "";
2535+ var fragment = match[8] || "";
2536+ return {
2537+ protocol: match[2],
2538+ host: match[4],
2539+ path: match[5],
2540+ relative: match[5] + query + fragment, // everything minus origin
2541+ };
2542+ }
2543+ function uuid4() {
2544+ var crypto = _window.crypto || _window.msCrypto;
2545+
2546+ if (!isUndefined(crypto) && crypto.getRandomValues) {
2547+ // Use window.crypto API if available
2548+ // eslint-disable-next-line no-undef
2549+ var arr = new Uint16Array(8);
2550+ crypto.getRandomValues(arr);
2551+
2552+ // set 4 in byte 7
2553+ arr[3] = (arr[3] & 0xfff) | 0x4000;
2554+ // set 2 most significant bits of byte 9 to '10'
2555+ arr[4] = (arr[4] & 0x3fff) | 0x8000;
2556+
2557+ var pad = function (num) {
2558+ var v = num.toString(16);
2559+ while (v.length < 4) {
2560+ v = "0" + v;
2561+ }
2562+ return v;
2563+ };
21532564
2154−function objectMerge(obj1, obj2) {
2155− if (!obj2) {
2156− return obj1;
2157− }
2158− each(obj2, function(key, value) {
2159− obj1[key] = value;
2160− });
2161− return obj1;
2162−}
2163−
2164−/**
2165− * This function is only used for react-native.
2166− * react-native freezes object that have already been sent over the
2167− * js bridge. We need this function in order to check if the object is frozen.
2168− * So it's ok that objectFrozen returns false if Object.isFrozen is not
2169− * supported because it's not relevant for other "platforms". See related issue:
2170− * https://github.com/getsentry/react-native-sentry/issues/57
2171− */
2172−function objectFrozen(obj) {
2173− if (!Object.isFrozen) {
2174− return false;
2175− }
2176− return Object.isFrozen(obj);
2177−}
2565+ return (
2566+ pad(arr[0]) +
2567+ pad(arr[1]) +
2568+ pad(arr[2]) +
2569+ pad(arr[3]) +
2570+ pad(arr[4]) +
2571+ pad(arr[5]) +
2572+ pad(arr[6]) +
2573+ pad(arr[7])
2574+ );
2575+ } else {
2576+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
2577+ return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(
2578+ /[xy]/g,
2579+ function (c) {
2580+ var r = (Math.random() * 16) | 0,
2581+ v = c === "x" ? r : (r & 0x3) | 0x8;
2582+ return v.toString(16);
2583+ },
2584+ );
2585+ }
2586+ }
21782587
2179−function truncate(str, max) {
2180− return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
2181−}
2588+ /**
2589+ * Given a child DOM element, returns a query-selector statement describing that
2590+ * and its ancestors
2591+ * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
2592+ * @param elem
2593+ * @returns {string}
2594+ */
2595+ function htmlTreeAsString(elem) {
2596+ /* eslint no-extra-parens:0*/
2597+ var MAX_TRAVERSE_HEIGHT = 5,
2598+ MAX_OUTPUT_LEN = 80,
2599+ out = [],
2600+ height = 0,
2601+ len = 0,
2602+ separator = " > ",
2603+ sepLength = separator.length,
2604+ nextStr;
2605+
2606+ while (elem && height++ < MAX_TRAVERSE_HEIGHT) {
2607+ nextStr = htmlElementAsString(elem);
2608+ // bail out if
2609+ // - nextStr is the 'html' element
2610+ // - the length of the string that would be created exceeds MAX_OUTPUT_LEN
2611+ // (ignore this limit if we are on the first iteration)
2612+ if (
2613+ nextStr === "html" ||
2614+ (height > 1 &&
2615+ len + out.length * sepLength + nextStr.length >=
2616+ MAX_OUTPUT_LEN)
2617+ ) {
2618+ break;
2619+ }
21822620
2183−/**
2184− * hasKey, a better form of hasOwnProperty
2185− * Example: hasKey(MainHostObject, property) === true/false
2186− *
2187− * @param {Object} host object to check property
2188− * @param {string} key to check
2189− */
2190−function hasKey(object, key) {
2191− return Object.prototype.hasOwnProperty.call(object, key);
2192−}
2193−
2194−function joinRegExp(patterns) {
2195− // Combine an array of regular expressions and strings into one large regexp
2196− // Be mad.
2197− var sources = [],
2198− i = 0,
2199− len = patterns.length,
2200− pattern;
2201−
2202− for (; i < len; i++) {
2203− pattern = patterns[i];
2204− if (isString(pattern)) {
2205− // If it's a string, we need to escape it
2206− // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
2207− sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));
2208− } else if (pattern && pattern.source) {
2209− // If it's a regexp already, we want to extract the source
2210− sources.push(pattern.source);
2211− }
2212− // Intentionally skip other cases
2213− }
2214− return new RegExp(sources.join('|'), 'i');
2215−}
2216−
2217−function urlencode(o) {
2218− var pairs = [];
2219− each(o, function(key, value) {
2220− pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
2221− });
2222− return pairs.join('&');
2223−}
2224−
2225−// borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B
2226−// intentionally using regex and not <a/> href parsing trick because React Native and other
2227−// environments where DOM might not be available
2228−function parseUrl(url) {
2229− var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
2230− if (!match) return {};
2231−
2232− // coerce to undefined values to empty string so we don't get 'undefined'
2233− var query = match[6] || '';
2234− var fragment = match[8] || '';
2235− return {
2236− protocol: match[2],
2237− host: match[4],
2238− path: match[5],
2239− relative: match[5] + query + fragment // everything minus origin
2240− };
2241−}
2242−function uuid4() {
2243− var crypto = _window.crypto || _window.msCrypto;
2244−
2245− if (!isUndefined(crypto) && crypto.getRandomValues) {
2246− // Use window.crypto API if available
2247− // eslint-disable-next-line no-undef
2248− var arr = new Uint16Array(8);
2249− crypto.getRandomValues(arr);
2250−
2251− // set 4 in byte 7
2252− arr[3] = (arr[3] & 0xfff) | 0x4000;
2253− // set 2 most significant bits of byte 9 to '10'
2254− arr[4] = (arr[4] & 0x3fff) | 0x8000;
2255−
2256− var pad = function(num) {
2257− var v = num.toString(16);
2258− while (v.length < 4) {
2259− v = '0' + v;
2260− }
2261− return v;
2262− };
2263−
2264− return (
2265− pad(arr[0]) +
2266− pad(arr[1]) +
2267− pad(arr[2]) +
2268− pad(arr[3]) +
2269− pad(arr[4]) +
2270− pad(arr[5]) +
2271− pad(arr[6]) +
2272− pad(arr[7])
2273− );
2274− } else {
2275− // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
2276− return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
2277− var r = (Math.random() * 16) | 0,
2278− v = c === 'x' ? r : (r & 0x3) | 0x8;
2279− return v.toString(16);
2280− });
2281− }
2282−}
2283−
2284−/**
2285− * Given a child DOM element, returns a query-selector statement describing that
2286− * and its ancestors
2287− * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
2288− * @param elem
2289− * @returns {string}
2290− */
2291−function htmlTreeAsString(elem) {
2292− /* eslint no-extra-parens:0*/
2293− var MAX_TRAVERSE_HEIGHT = 5,
2294− MAX_OUTPUT_LEN = 80,
2295− out = [],
2296− height = 0,
2297− len = 0,
2298− separator = ' > ',
2299− sepLength = separator.length,
2300− nextStr;
2301−
2302− while (elem && height++ < MAX_TRAVERSE_HEIGHT) {
2303− nextStr = htmlElementAsString(elem);
2304− // bail out if
2305− // - nextStr is the 'html' element
2306− // - the length of the string that would be created exceeds MAX_OUTPUT_LEN
2307− // (ignore this limit if we are on the first iteration)
2308− if (
2309− nextStr === 'html' ||
2310− (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)
2311− ) {
2312− break;
2313− }
2621+ out.push(nextStr);
23142622
2315− out.push(nextStr);
2623+ len += nextStr.length;
2624+ elem = elem.parentNode;
2625+ }
23162626
2317− len += nextStr.length;
2318− elem = elem.parentNode;
2319− }
2627+ return out.reverse().join(separator);
2628+ }
23202629
2321− return out.reverse().join(separator);
2322−}
2630+ /**
2631+ * Returns a simple, query-selector representation of a DOM element
2632+ * e.g. [HTMLElement] => input#foo.btn[name=baz]
2633+ * @param HTMLElement
2634+ * @returns {string}
2635+ */
2636+ function htmlElementAsString(elem) {
2637+ var out = [],
2638+ className,
2639+ classes,
2640+ key,
2641+ attr,
2642+ i;
2643+
2644+ if (!elem || !elem.tagName) {
2645+ return "";
2646+ }
23232647
2324−/**
2325− * Returns a simple, query-selector representation of a DOM element
2326− * e.g. [HTMLElement] => input#foo.btn[name=baz]
2327− * @param HTMLElement
2328− * @returns {string}
2329− */
2330−function htmlElementAsString(elem) {
2331− var out = [],
2332− className,
2333− classes,
2334− key,
2335− attr,
2336− i;
2337−
2338− if (!elem || !elem.tagName) {
2339− return '';
2340− }
2648+ out.push(elem.tagName.toLowerCase());
2649+ if (elem.id) {
2650+ out.push("#" + elem.id);
2651+ }
23412652
2342− out.push(elem.tagName.toLowerCase());
2343− if (elem.id) {
2344− out.push('#' + elem.id);
2345− }
2653+ className = elem.className;
2654+ if (className && isString(className)) {
2655+ classes = className.split(/\s+/);
2656+ for (i = 0; i < classes.length; i++) {
2657+ out.push("." + classes[i]);
2658+ }
2659+ }
2660+ var attrWhitelist = ["type", "name", "title", "alt"];
2661+ for (i = 0; i < attrWhitelist.length; i++) {
2662+ key = attrWhitelist[i];
2663+ attr = elem.getAttribute(key);
2664+ if (attr) {
2665+ out.push("[" + key + '="' + attr + '"]');
2666+ }
2667+ }
2668+ return out.join("");
2669+ }
23462670
2347− className = elem.className;
2348− if (className && isString(className)) {
2349− classes = className.split(/\s+/);
2350− for (i = 0; i < classes.length; i++) {
2351− out.push('.' + classes[i]);
2352− }
2353− }
2354− var attrWhitelist = ['type', 'name', 'title', 'alt'];
2355− for (i = 0; i < attrWhitelist.length; i++) {
2356− key = attrWhitelist[i];
2357− attr = elem.getAttribute(key);
2358− if (attr) {
2359− out.push('[' + key + '="' + attr + '"]');
2360− }
2361− }
2362− return out.join('');
2363−}
2671+ /**
2672+ * Returns true if either a OR b is truthy, but not both
2673+ */
2674+ function isOnlyOneTruthy(a, b) {
2675+ return !!(!!a ^ !!b);
2676+ }
23642677
2365−/**
2366− * Returns true if either a OR b is truthy, but not both
2367− */
2368−function isOnlyOneTruthy(a, b) {
2369− return !!(!!a ^ !!b);
2370−}
2678+ /**
2679+ * Returns true if the two input exception interfaces have the same content
2680+ */
2681+ function isSameException(ex1, ex2) {
2682+ if (isOnlyOneTruthy(ex1, ex2)) return false;
23712683
2372−/**
2373− * Returns true if the two input exception interfaces have the same content
2374− */
2375−function isSameException(ex1, ex2) {
2376− if (isOnlyOneTruthy(ex1, ex2)) return false;
2684+ ex1 = ex1.values[0];
2685+ ex2 = ex2.values[0];
23772686
2378− ex1 = ex1.values[0];
2379− ex2 = ex2.values[0];
2687+ if (ex1.type !== ex2.type || ex1.value !== ex2.value)
2688+ return false;
23802689
2381− if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;
2690+ return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
2691+ }
23822692
2383− return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
2384−}
2693+ /**
2694+ * Returns true if the two input stack trace interfaces have the same content
2695+ */
2696+ function isSameStacktrace(stack1, stack2) {
2697+ if (isOnlyOneTruthy(stack1, stack2)) return false;
2698+
2699+ var frames1 = stack1.frames;
2700+ var frames2 = stack2.frames;
2701+
2702+ // Exit early if frame count differs
2703+ if (frames1.length !== frames2.length) return false;
2704+
2705+ // Iterate through every frame; bail out if anything differs
2706+ var a, b;
2707+ for (var i = 0; i < frames1.length; i++) {
2708+ a = frames1[i];
2709+ b = frames2[i];
2710+ if (
2711+ a.filename !== b.filename ||
2712+ a.lineno !== b.lineno ||
2713+ a.colno !== b.colno ||
2714+ a["function"] !== b["function"]
2715+ )
2716+ return false;
2717+ }
2718+ return true;
2719+ }
23852720
2386−/**
2387− * Returns true if the two input stack trace interfaces have the same content
2388− */
2389−function isSameStacktrace(stack1, stack2) {
2390− if (isOnlyOneTruthy(stack1, stack2)) return false;
2391−
2392− var frames1 = stack1.frames;
2393− var frames2 = stack2.frames;
2394−
2395− // Exit early if frame count differs
2396− if (frames1.length !== frames2.length) return false;
2397−
2398− // Iterate through every frame; bail out if anything differs
2399− var a, b;
2400− for (var i = 0; i < frames1.length; i++) {
2401− a = frames1[i];
2402− b = frames2[i];
2403− if (
2404− a.filename !== b.filename ||
2405− a.lineno !== b.lineno ||
2406− a.colno !== b.colno ||
2407− a['function'] !== b['function']
2408− )
2409− return false;
2410− }
2411− return true;
2412−}
2413−
2414−/**
2415− * Polyfill a method
2416− * @param obj object e.g. `document`
2417− * @param name method name present on object e.g. `addEventListener`
2418− * @param replacement replacement function
2419− * @param track {optional} record instrumentation to an array
2420− */
2421−function fill(obj, name, replacement, track) {
2422− var orig = obj[name];
2423− obj[name] = replacement(orig);
2424− obj[name].__raven__ = true;
2425− obj[name].__orig__ = orig;
2426− if (track) {
2427− track.push([obj, name, orig]);
2428− }
2429−}
2430−
2431−module.exports = {
2432− isObject: isObject,
2433− isError: isError,
2434− isErrorEvent: isErrorEvent,
2435− isUndefined: isUndefined,
2436− isFunction: isFunction,
2437− isString: isString,
2438− isArray: isArray,
2439− isEmptyObject: isEmptyObject,
2440− supportsErrorEvent: supportsErrorEvent,
2441− wrappedCallback: wrappedCallback,
2442− each: each,
2443− objectMerge: objectMerge,
2444− truncate: truncate,
2445− objectFrozen: objectFrozen,
2446− hasKey: hasKey,
2447− joinRegExp: joinRegExp,
2448− urlencode: urlencode,
2449− uuid4: uuid4,
2450− htmlTreeAsString: htmlTreeAsString,
2451− htmlElementAsString: htmlElementAsString,
2452− isSameException: isSameException,
2453− isSameStacktrace: isSameStacktrace,
2454− parseUrl: parseUrl,
2455− fill: fill
2456−};
2457−
2458−}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2459−},{}],6:[function(_dereq_,module,exports){
2460−(function (global){
2461−var utils = _dereq_(5);
2721+ /**
2722+ * Polyfill a method
2723+ * @param obj object e.g. `document`
2724+ * @param name method name present on object e.g. `addEventListener`
2725+ * @param replacement replacement function
2726+ * @param track {optional} record instrumentation to an array
2727+ */
2728+ function fill(obj, name, replacement, track) {
2729+ var orig = obj[name];
2730+ obj[name] = replacement(orig);
2731+ obj[name].__raven__ = true;
2732+ obj[name].__orig__ = orig;
2733+ if (track) {

1440 more lines aren’t shown here.

assets/javascripts/views/content/content.js+81−56
@@ -32,51 +32,46 @@ const Cls = (app.views.Content = class Content extends app.View { · 31 unmodified lines
3232 }
3333
3434 static initClass() {
35− this.el = '._content';
36− this.loadingClass = '_content-loading';
37−
38− this.events =
39− {click: 'onClick'};
40−
35+ this.el = "._content";
36+ this.loadingClass = "_content-loading";
37+
38+ this.events = { click: "onClick" };
39+
4140 this.shortcuts = {
42− altUp: 'scrollStepUp',
43− altDown: 'scrollStepDown',
44− pageUp: 'scrollPageUp',
45− pageDown: 'scrollPageDown',
46− pageTop: 'scrollToTop',
47− pageBottom: 'scrollToBottom',
48− altF: 'onAltF'
41+ altUp: "scrollStepUp",
42+ altDown: "scrollStepDown",
43+ pageUp: "scrollPageUp",
44+ pageDown: "scrollPageDown",
45+ pageTop: "scrollToTop",
46+ pageBottom: "scrollToBottom",
47+ altF: "onAltF",
4948 };
50−
49+
5150 this.routes = {
52− before: 'beforeRoute',
53− after: 'afterRoute'
51+ before: "beforeRoute",
52+ after: "afterRoute",
5453 };
5554 }
5655
5756 init() {
58− this.scrollEl = app.isMobile() ?
59− (document.scrollingElement || document.body)
60− :
61− this.el;
57+ this.scrollEl = app.isMobile()
58+ ? document.scrollingElement || document.body
59+ : this.el;
6260 this.scrollMap = {};
6361 this.scrollStack = [];
6462
65− this.rootPage = new app.views.RootPage;
66− this.staticPage = new app.views.StaticPage;
67− this.settingsPage = new app.views.SettingsPage;
68− this.offlinePage = new app.views.OfflinePage;
69− this.typePage = new app.views.TypePage;
70− this.entryPage = new app.views.EntryPage;
63+ this.rootPage = new app.views.RootPage();
64+ this.staticPage = new app.views.StaticPage();
65+ this.settingsPage = new app.views.SettingsPage();
66+ this.offlinePage = new app.views.OfflinePage();
67+ this.typePage = new app.views.TypePage();
68+ this.entryPage = new app.views.EntryPage();
7169
7270 this.entryPage
73− .on('loading', this.onEntryLoading)
74− .on('loaded', this.onEntryLoaded);
75−
76− app
77− .on('ready', this.onReady)
78− .on('bootError', this.onBootError);
71+ .on("loading", this.onEntryLoading)
72+ .on("loaded", this.onEntryLoaded);
7973
74+ app.on("ready", this.onReady).on("bootError", this.onBootError);
8075 }
8176
8277 show(view) {
@@ -85,7 +80,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 2 unmodified lines
8580 if (this.view != null) {
8681 this.view.deactivate();
8782 }
88− this.html(this.view = view);
83+ this.html((this.view = view));
8984 this.view.activate();
9085 }
9186 }
@@ -107,7 +102,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 15 unmodified lines
107102 }
108103
109104 smoothScrollTo(value) {
110− if (app.settings.get('fastScroll')) {
105+ if (app.settings.get("fastScroll")) {
111106 this.scrollTo(value);
112107 } else {
113108 $.smoothScroll(this.scrollEl, value || 0);
@@ -144,10 +139,14 @@ const Cls = (app.views.Content = class Content extends app.View { · 30 unmodified lines
144139
145140 scrollToTarget() {
146141 let el;
147− if (this.routeCtx.hash && (el = this.findTargetByHash(this.routeCtx.hash))) {
148− $.scrollToWithImageLock(el, this.scrollEl, 'top',
149− {margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top});
150− $.highlight(el, {className: '_highlight'});
142+ if (
143+ this.routeCtx.hash &&
144+ (el = this.findTargetByHash(this.routeCtx.hash))
145+ ) {
146+ $.scrollToWithImageLock(el, this.scrollEl, "top", {
147+ margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top,
148+ });
149+ $.highlight(el, { className: "_highlight" });
151150 } else {
152151 this.scrollTo(this.scrollMap[this.routeCtx.state.id]);
153152 }
@@ -159,7 +158,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 5 unmodified lines
159158
160159 onBootError() {
161160 this.hideLoading();
162− this.html(this.tmpl('bootError'));
161+ this.html(this.tmpl("bootError"));
163162 }
164163
165164 onEntryLoading() {
@@ -186,8 +185,12 @@ const Cls = (app.views.Content = class Content extends app.View { · 20 unmodified lines
186185 }
187186
188187 cacheScrollPosition() {
189− if (!this.routeCtx || this.routeCtx.hash) { return; }
190− if (this.routeCtx.path === '/') { return; }
188+ if (!this.routeCtx || this.routeCtx.hash) {
189+ return;
190+ }
191+ if (this.routeCtx.path === "/") {
192+ return;
193+ }
191194
192195 if (this.scrollMap[this.routeCtx.state.id] == null) {
193196 this.scrollStack.push(this.routeCtx.state.id);
@@ -200,24 +203,24 @@ const Cls = (app.views.Content = class Content extends app.View { · 6 unmodified lines
200203 }
201204
202205 afterRoute(route, context) {
203− if ((route !== 'entry') && (route !== 'type')) {
206+ if (route !== "entry" && route !== "type") {
204207 resetFavicon();
205208 }
206209
207210 switch (route) {
208− case 'root':
211+ case "root":
209212 this.show(this.rootPage);
210213 break;
211− case 'entry':
214+ case "entry":
212215 this.show(this.entryPage);
213216 break;
214− case 'type':
217+ case "type":
215218 this.show(this.typePage);
216219 break;
217− case 'settings':
220+ case "settings":
218221 this.show(this.settingsPage);
219222 break;
220− case 'offline':
223+ case "offline":
221224 this.show(this.offlinePage);
222225 break;
223226 default:
@@ -225,37 +228,59 @@ const Cls = (app.views.Content = class Content extends app.View { · 1 unmodified line
225228 }
226229
227230 this.view.onRoute(context);
228− app.document.setTitle(typeof this.view.getTitle === 'function' ? this.view.getTitle() : undefined);
231+ app.document.setTitle(
232+ typeof this.view.getTitle === "function"
233+ ? this.view.getTitle()
234+ : undefined,
235+ );
229236 }
230237
231238 onClick(event) {
232239 const link = $.closestLink($.eventTarget(event), this.el);
233− if (link && this.isExternalUrl(link.getAttribute('href'))) {
240+ if (link && this.isExternalUrl(link.getAttribute("href"))) {
234241 $.stopEvent(event);
235242 $.popup(link);
236243 }
237244 }
238245
239246 onAltF(event) {
240− if (!document.activeElement || !$.hasChild(this.el, document.activeElement)) {
241− __guard__(this.find('a:not(:empty)'), x => x.focus());
247+ if (
248+ !document.activeElement ||
249+ !$.hasChild(this.el, document.activeElement)
250+ ) {
251+ __guard__(this.find("a:not(:empty)"), (x) => x.focus());
242252 return $.stopEvent(event);
243253 }
244254 }
245255
246256 findTargetByHash(hash) {
247− let el = (() => { try { return $.id(decodeURIComponent(hash)); } catch (error) {} })();
248− if (!el) { el = (() => { try { return $.id(hash); } catch (error1) {} })(); }
257+ let el = (() => {
258+ try {
259+ return $.id(decodeURIComponent(hash));
260+ } catch (error) {}
261+ })();
262+ if (!el) {
263+ el = (() => {
264+ try {
265+ return $.id(hash);
266+ } catch (error1) {}
267+ })();
268+ }
249269 return el;
250270 }
251271
252272 isExternalUrl(url) {
253273 let needle;
254− return (needle = __guard__(url, x => x.slice(0, 6)), ['http:/', 'https:'].includes(needle));
274+ return (
275+ (needle = __guard__(url, (x) => x.slice(0, 6))),
276+ ["http:/", "https:"].includes(needle)
277+ );
255278 }
256279 });
257280 Cls.initClass();
258281
259282 function __guard__(value, transform) {
260− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
261−}
283+ return typeof value !== "undefined" && value !== null
284+ ? transform(value)
285+ : undefined;
286+}
assets/javascripts/views/content/entry_page.js+94−47
@@ -10,7 +10,7 @@ · 9 unmodified lines
1010 * DS207: Consider shorter variations of null checks
1111 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1212 */
13−(function() {
13+(function () {
1414 let LINKS = undefined;
1515 const Cls = (app.views.EntryPage = class EntryPage extends app.View {
1616 constructor(...args) {
@@ -24,23 +24,21 @@ · 7 unmodified lines
2424 }
2525
2626 static initClass() {
27− this.className = '_page';
28− this.errorClass = '_page-error';
29−
30− this.events =
31− {click: 'onClick'};
32−
27+ this.className = "_page";
28+ this.errorClass = "_page-error";
29+
30+ this.events = { click: "onClick" };
31+
3332 this.shortcuts = {
34− altC: 'onAltC',
35− altO: 'onAltO'
33+ altC: "onAltC",
34+ altO: "onAltO",
3635 };
37−
38− this.routes =
39− {before: 'beforeRoute'};
40−
36+
37+ this.routes = { before: "beforeRoute" };
38+
4139 LINKS = {
42− home: 'Homepage',
43− code: 'Source code'
40+ home: "Homepage",
41+ code: "Source code",
4442 };
4543 }
4644
@@ -58,61 +56,84 @@ · 11 unmodified lines
5856
5957 loading() {
6058 this.empty();
61− this.trigger('loading');
59+ this.trigger("loading");
6260 }
6361
6462 render(content, fromCache) {
65− if (content == null) { content = ''; }
66− if (fromCache == null) { fromCache = false; }
67− if (!this.activated) { return; }
63+ if (content == null) {
64+ content = "";
65+ }
66+ if (fromCache == null) {
67+ fromCache = false;
68+ }
69+ if (!this.activated) {
70+ return;
71+ }
6872 this.empty();
6973 this.subview = new (this.subViewClass())(this.el, this.entry);
7074
7175 $.batchUpdate(this.el, () => {
7276 this.subview.render(content, fromCache);
73− if (!fromCache) { this.addCopyButtons(); }
77+ if (!fromCache) {
78+ this.addCopyButtons();
79+ }
7480 });
7581
76− if (app.disabledDocs.findBy('slug', this.entry.doc.slug)) {
82+ if (app.disabledDocs.findBy("slug", this.entry.doc.slug)) {
7783 this.hiddenView = new app.views.HiddenPage(this.el, this.entry);
7884 }
7985
8086 setFaviconForDoc(this.entry.doc);
8187 this.delay(this.polyfillMathML);
82− this.trigger('loaded');
88+ this.trigger("loaded");
8389 }
8490
8591 addCopyButtons() {
8692 if (!this.copyButton) {
87− this.copyButton = document.createElement('button');
93+ this.copyButton = document.createElement("button");
8894 this.copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>';
89− this.copyButton.type = 'button';
90− this.copyButton.className = '_pre-clip';
91− this.copyButton.title = 'Copy to clipboard';
92− this.copyButton.setAttribute('aria-label', 'Copy to clipboard');
95+ this.copyButton.type = "button";
96+ this.copyButton.className = "_pre-clip";
97+ this.copyButton.title = "Copy to clipboard";
98+ this.copyButton.setAttribute("aria-label", "Copy to clipboard");
99+ }
100+ for (var el of Array.from(this.findAllByTag("pre"))) {
101+ el.appendChild(this.copyButton.cloneNode(true));
93102 }
94− for (var el of Array.from(this.findAllByTag('pre'))) { el.appendChild(this.copyButton.cloneNode(true)); }
95103 }
96104
97105 polyfillMathML() {
98− if ((window.supportsMathML !== false) || !!this.polyfilledMathML || !this.findByTag('math')) { return; }
106+ if (
107+ window.supportsMathML !== false ||
108+ !!this.polyfilledMathML ||
109+ !this.findByTag("math")
110+ ) {
111+ return;
112+ }
99113 this.polyfilledMathML = true;
100− $.append(document.head, `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`);
114+ $.append(
115+ document.head,
116+ `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`,
117+ );
101118 }
102119
103120 prepareContent(content) {
104− if (!this.entry.isIndex() || !this.entry.doc.links) { return content; }
121+ if (!this.entry.isIndex() || !this.entry.doc.links) {
122+ return content;
123+ }
105124
106125 const links = (() => {
107126 const result = [];
108127 for (var link in this.entry.doc.links) {
109128 var url = this.entry.doc.links[link];
110− result.push(`<a href="${url}" class="_links-link">${LINKS[link]}</a>`);
129+ result.push(
130+ `<a href="${url}" class="_links-link">${LINKS[link]}</a>`,
131+ );
111132 }
112133 return result;
113134 })();
114135
115− return `<p class="_links">${links.join('')}</p>${content}`;
136+ return `<p class="_links">${links.join("")}</p>${content}`;
116137 }
117138
118139 empty() {
@@ -131,11 +152,17 @@ · 12 unmodified lines
131152 }
132153
133154 subViewClass() {
134− return app.views[`${$.classify(this.entry.doc.type)}Page`] || app.views.BasePage;
155+ return (
156+ app.views[`${$.classify(this.entry.doc.type)}Page`] ||
157+ app.views.BasePage
158+ );
135159 }
136160
137161 getTitle() {
138− return this.entry.doc.fullName + (this.entry.isIndex() ? ' documentation' : ` / ${this.entry.name}`);
162+ return (
163+ this.entry.doc.fullName +
164+ (this.entry.isIndex() ? " documentation" : ` / ${this.entry.name}`)
165+ );
139166 }
140167
141168 beforeRoute() {
@@ -144,9 +171,13 @@ · 2 unmodified lines
144171 }
145172
146173 onRoute(context) {
147− const isSameFile = context.entry.filePath() === (this.entry != null ? this.entry.filePath() : undefined);
174+ const isSameFile =
175+ context.entry.filePath() ===
176+ (this.entry != null ? this.entry.filePath() : undefined);
148177 this.entry = context.entry;
149− if (!isSameFile) { this.restore() || this.load(); }
178+ if (!isSameFile) {
179+ this.restore() || this.load();
180+ }
150181 }
151182
152183 load() {
@@ -157,19 +188,21 @@ · 4 unmodified lines
157188 abort() {
158189 if (this.xhr) {
159190 this.xhr.abort();
160− this.xhr = (this.entry = null);
191+ this.xhr = this.entry = null;
161192 }
162193 }
163194
164195 onSuccess(response) {
165− if (!this.activated) { return; }
196+ if (!this.activated) {
197+ return;
198+ }
166199 this.xhr = null;
167200 this.render(this.prepareContent(response));
168201 }
169202
170203 onError() {
171204 this.xhr = null;
172− this.render(this.tmpl('pageLoadError'));
205+ this.render(this.tmpl("pageLoadError"));
173206 this.resetClass();
174207 this.addClass(this.constructor.errorClass);
175208 if (app.serviceWorker != null) {
@@ -179,7 +212,13 @@ · 3 unmodified lines
179212
180213 cache() {
181214 let path;
182− if (this.xhr || !this.entry || this.cacheMap[(path = this.entry.filePath())]) { return; }
215+ if (
216+ this.xhr ||
217+ !this.entry ||
218+ this.cacheMap[(path = this.entry.filePath())]
219+ ) {
220+ return;
221+ }
183222
184223 this.cacheMap[path] = this.el.innerHTML;
185224 this.cacheStack.push(path);
@@ -199,26 +238,34 @@ · 13 unmodified lines
199238
200239 onClick(event) {
201240 const target = $.eventTarget(event);
202− if (target.hasAttribute('data-retry')) {
241+ if (target.hasAttribute("data-retry")) {
203242 $.stopEvent(event);
204243 this.load();
205− } else if (target.classList.contains('_pre-clip')) {
244+ } else if (target.classList.contains("_pre-clip")) {
206245 $.stopEvent(event);
207− target.classList.add($.copyToClipboard(target.parentNode.textContent) ? '_pre-clip-success' : '_pre-clip-error');
208− setTimeout((() => target.className = '_pre-clip'), 2000);
246+ target.classList.add(
247+ $.copyToClipboard(target.parentNode.textContent)
248+ ? "_pre-clip-success"
249+ : "_pre-clip-error",
250+ );
251+ setTimeout(() => (target.className = "_pre-clip"), 2000);
209252 }
210253 }
211254
212255 onAltC() {
213256 let link;
214− if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
257+ if (!(link = this.find("._attribution:last-child ._attribution-link"))) {
258+ return;
259+ }
215260 console.log(link.href + location.hash);
216261 navigator.clipboard.writeText(link.href + location.hash);
217262 }
218263
219264 onAltO() {
220265 let link;
221− if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
266+ if (!(link = this.find("._attribution:last-child ._attribution-link"))) {
267+ return;
268+ }
222269 this.delay(() => $.popup(link.href + location.hash));
223270 }
224271 });
assets/javascripts/views/content/offline_page.js+70−37
@@ -15,11 +15,11 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 14 unmodified lines
1515 }
1616
1717 static initClass() {
18− this.className = '_static';
19−
18+ this.className = "_static";
19+
2020 this.events = {
21− click: 'onClick',
22− change: 'onChange'
21+ click: "onClick",
22+ change: "onChange",
2323 };
2424 }
2525
@@ -31,41 +31,49 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 5 unmodified lines
3131
3232 render() {
3333 if (app.cookieBlocked) {
34− this.html(this.tmpl('offlineError', 'cookie_blocked'));
34+ this.html(this.tmpl("offlineError", "cookie_blocked"));
3535 return;
3636 }
3737
38− app.docs.getInstallStatuses(statuses => {
39− if (!this.activated) { return; }
38+ app.docs.getInstallStatuses((statuses) => {
39+ if (!this.activated) {
40+ return;
41+ }
4042 if (statuses === false) {
41− this.html(this.tmpl('offlineError', app.db.reason, app.db.error));
43+ this.html(this.tmpl("offlineError", app.db.reason, app.db.error));
4244 } else {
43− let html = '';
44− for (var doc of Array.from(app.docs.all())) { html += this.renderDoc(doc, statuses[doc.slug]); }
45− this.html(this.tmpl('offlinePage', html));
45+ let html = "";
46+ for (var doc of Array.from(app.docs.all())) {
47+ html += this.renderDoc(doc, statuses[doc.slug]);
48+ }
49+ this.html(this.tmpl("offlinePage", html));
4650 this.refreshLinks();
4751 }
4852 });
4953 }
5054
5155 renderDoc(doc, status) {
52− return app.templates.render('offlineDoc', doc, status);
56+ return app.templates.render("offlineDoc", doc, status);
5357 }
5458
5559 getTitle() {
56− return 'Offline';
60+ return "Offline";
5761 }
5862
5963 refreshLinks() {
60− for (var action of ['install', 'update', 'uninstall']) {
61− this.find(`[data-action-all='${action}']`).classList[this.find(`[data-action='${action}']`) ? 'add' : 'remove']('_show');
64+ for (var action of ["install", "update", "uninstall"]) {
65+ this.find(`[data-action-all='${action}']`).classList[
66+ this.find(`[data-action='${action}']`) ? "add" : "remove"
67+ ]("_show");
6268 }
6369 }
6470
6571 docByEl(el) {
6672 let slug;
67− while (!(slug = el.getAttribute('data-slug'))) { el = el.parentNode; }
68− return app.docs.findBy('slug', slug);
73+ while (!(slug = el.getAttribute("data-slug"))) {
74+ el = el.parentNode;
75+ }
76+ return app.docs.findBy("slug", slug);
6977 }
7078
7179 docEl(doc) {
@@ -79,26 +87,44 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 7 unmodified lines
7987 onClick(event) {
8088 let action;
8189 let el = $.eventTarget(event);
82− if (action = el.getAttribute('data-action')) {
90+ if ((action = el.getAttribute("data-action"))) {
8391 const doc = this.docByEl(el);
84− if (action === 'update') { action = 'install'; }
85− doc[action](this.onInstallSuccess.bind(this, doc), this.onInstallError.bind(this, doc), this.onInstallProgress.bind(this, doc));
86− el.parentNode.innerHTML = `${el.textContent.replace(/e$/, '')}ing…`;
87− } else if (action = el.getAttribute('data-action-all') || el.parentElement.getAttribute('data-action-all')) {
88− if ((action === 'uninstall') && !window.confirm('Uninstall all docs?')) { return; }
92+ if (action === "update") {
93+ action = "install";
94+ }
95+ doc[action](
96+ this.onInstallSuccess.bind(this, doc),
97+ this.onInstallError.bind(this, doc),
98+ this.onInstallProgress.bind(this, doc),
99+ );
100+ el.parentNode.innerHTML = `${el.textContent.replace(/e$/, "")}ing…`;
101+ } else if (
102+ (action =
103+ el.getAttribute("data-action-all") ||
104+ el.parentElement.getAttribute("data-action-all"))
105+ ) {
106+ if (action === "uninstall" && !window.confirm("Uninstall all docs?")) {
107+ return;
108+ }
89109 app.db.migrate();
90− for (el of Array.from(this.findAll(`[data-action='${action}']`))) { $.click(el); }
110+ for (el of Array.from(this.findAll(`[data-action='${action}']`))) {
111+ $.click(el);
112+ }
91113 }
92114 }
93115
94116 onInstallSuccess(doc) {
95− if (!this.activated) { return; }
96− doc.getInstallStatus(status => {
117+ if (!this.activated) {
118+ return;
119+ }
120+ doc.getInstallStatus((status) => {
97121 let el;
98− if (!this.activated) { return; }
99− if (el = this.docEl(doc)) {
122+ if (!this.activated) {
123+ return;
124+ }
125+ if ((el = this.docEl(doc))) {
100126 el.outerHTML = this.renderDoc(doc, status);
101− $.highlight(el, {className: '_highlight'});
127+ $.highlight(el, { className: "_highlight" });
102128 this.refreshLinks();
103129 }
104130 });
@@ -106,24 +132,31 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 1 unmodified line
106132
107133 onInstallError(doc) {
108134 let el;
109− if (!this.activated) { return; }
110− if (el = this.docEl(doc)) {
111− el.lastElementChild.textContent = 'Error';
135+ if (!this.activated) {
136+ return;
137+ }
138+ if ((el = this.docEl(doc))) {
139+ el.lastElementChild.textContent = "Error";
112140 }
113141 }
114142
115143 onInstallProgress(doc, event) {
116144 let el;
117− if (!this.activated || !event.lengthComputable) { return; }
118− if (el = this.docEl(doc)) {
145+ if (!this.activated || !event.lengthComputable) {
146+ return;
147+ }
148+ if ((el = this.docEl(doc))) {
119149 const percentage = Math.round((event.loaded * 100) / event.total);
120− el.lastElementChild.textContent = el.lastElementChild.textContent.replace(/(\s.+)?$/, ` (${percentage}%)`);
150+ el.lastElementChild.textContent = el.lastElementChild.textContent.replace(
151+ /(\s.+)?$/,
152+ ` (${percentage}%)`,
153+ );
121154 }
122155 }
123156
124157 onChange(event) {
125− if (event.target.name === 'autoUpdate') {
126− app.settings.set('manualUpdate', !event.target.checked);
158+ if (event.target.name === "autoUpdate") {
159+ app.settings.set("manualUpdate", !event.target.checked);
127160 }
128161 }
129162 });
assets/javascripts/views/content/root_page.js+14−14
@@ -14,26 +14,26 @@ const Cls = (app.views.RootPage = class RootPage extends app.View { · 13 unmodified lines
1414 }
1515
1616 static initClass() {
17− this.events =
18− {click: 'onClick'};
17+ this.events = { click: "onClick" };
1918 }
2019
2120 init() {
22− if (!this.isHidden()) { this.setHidden(false); } // reserve space in local storage
21+ if (!this.isHidden()) {
22+ this.setHidden(false);
23+ } // reserve space in local storage
2324 this.render();
2425 }
2526
2627 render() {
2728 this.empty();
2829
29− const tmpl = app.isAndroidWebview() ?
30− 'androidWarning'
31− : this.isHidden() ?
32− 'splash'
33− : app.isMobile() ?
34− 'mobileIntro'
35− :
36− 'intro';
30+ const tmpl = app.isAndroidWebview()
31+ ? "androidWarning"
32+ : this.isHidden()
33+ ? "splash"
34+ : app.isMobile()
35+ ? "mobileIntro"
36+ : "intro";
3737
3838 this.append(this.tmpl(tmpl));
3939 }
@@ -44,17 +44,17 @@ const Cls = (app.views.RootPage = class RootPage extends app.View { · 4 unmodified lines
4444 }
4545
4646 setHidden(value) {
47− app.settings.set('hideIntro', value);
47+ app.settings.set("hideIntro", value);
4848 }
4949
5050 isHidden() {
51− return app.isSingleDoc() || app.settings.get('hideIntro');
51+ return app.isSingleDoc() || app.settings.get("hideIntro");
5252 }
5353
5454 onRoute() {}
5555
5656 onClick(event) {
57− if ($.eventTarget(event).hasAttribute('data-hide-intro')) {
57+ if ($.eventTarget(event).hasAttribute("data-hide-intro")) {
5858 $.stopEvent(event);
5959 this.hideIntro();
6060 }
assets/javascripts/views/content/settings_page.js+51−41
@@ -17,39 +17,41 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 16 unmodified lines
1717 }
1818
1919 static initClass() {
20− this.className = '_static';
21−
20+ this.className = "_static";
21+
2222 this.events = {
23− click: 'onClick',
24− change: 'onChange'
23+ click: "onClick",
24+ change: "onChange",
2525 };
2626 }
2727
2828 render() {
29− this.html(this.tmpl('settingsPage', this.currentSettings()));
29+ this.html(this.tmpl("settingsPage", this.currentSettings()));
3030 }
3131
3232 currentSettings() {
3333 const settings = {};
34− settings.theme = app.settings.get('theme');
35− settings.smoothScroll = !app.settings.get('fastScroll');
36− settings.arrowScroll = app.settings.get('arrowScroll');
37− settings.noAutofocus = app.settings.get('noAutofocus');
38− settings.autoInstall = app.settings.get('autoInstall');
39− settings.analyticsConsent = app.settings.get('analyticsConsent');
40− settings.spaceScroll = app.settings.get('spaceScroll');
41− settings.spaceTimeout = app.settings.get('spaceTimeout');
34+ settings.theme = app.settings.get("theme");
35+ settings.smoothScroll = !app.settings.get("fastScroll");
36+ settings.arrowScroll = app.settings.get("arrowScroll");
37+ settings.noAutofocus = app.settings.get("noAutofocus");
38+ settings.autoInstall = app.settings.get("autoInstall");
39+ settings.analyticsConsent = app.settings.get("analyticsConsent");
40+ settings.spaceScroll = app.settings.get("spaceScroll");
41+ settings.spaceTimeout = app.settings.get("spaceTimeout");
4242 settings.autoSupported = app.settings.autoSupported;
43− for (var layout of Array.from(app.settings.LAYOUTS)) { settings[layout] = app.settings.hasLayout(layout); }
43+ for (var layout of Array.from(app.settings.LAYOUTS)) {
44+ settings[layout] = app.settings.hasLayout(layout);
45+ }
4446 return settings;
4547 }
4648
4749 getTitle() {
48− return 'Preferences';
50+ return "Preferences";
4951 }
5052
5153 setTheme(value) {
52− app.settings.set('theme', value);
54+ app.settings.set("theme", value);
5355 }
5456
5557 toggleLayout(layout, enable) {
@@ -57,20 +59,22 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line
5759 }
5860
5961 toggleSmoothScroll(enable) {
60− app.settings.set('fastScroll', !enable);
62+ app.settings.set("fastScroll", !enable);
6163 }
6264
6365 toggleAnalyticsConsent(enable) {
64− app.settings.set('analyticsConsent', enable ? '1' : '0');
65− if (!enable) { resetAnalytics(); }
66+ app.settings.set("analyticsConsent", enable ? "1" : "0");
67+ if (!enable) {
68+ resetAnalytics();
69+ }
6670 }
6771
6872 toggleSpaceScroll(enable) {
69− app.settings.set('spaceScroll', enable ? 1 : 0);
73+ app.settings.set("spaceScroll", enable ? 1 : 0);
7074 }
7175
7276 setScrollTimeout(value) {
73− return app.settings.set('spaceTimeout', value);
77+ return app.settings.set("spaceTimeout", value);
7478 }
7579
7680 toggle(name, enable) {
@@ -78,31 +82,37 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line
7882 }
7983
8084 export() {
81− const data = new Blob([JSON.stringify(app.settings.export())], {type: 'application/json'});
82− const link = document.createElement('a');
85+ const data = new Blob([JSON.stringify(app.settings.export())], {
86+ type: "application/json",
87+ });
88+ const link = document.createElement("a");
8389 link.href = URL.createObjectURL(data);
84− link.download = 'devdocs.json';
85− link.style.display = 'none';
90+ link.download = "devdocs.json";
91+ link.style.display = "none";
8692 document.body.appendChild(link);
8793 link.click();
8894 document.body.removeChild(link);
8995 }
9096
9197 import(file, input) {
92− if (!file || (file.type !== 'application/json')) {
93− new app.views.Notif('ImportInvalid', {autoHide: false});
98+ if (!file || file.type !== "application/json") {
99+ new app.views.Notif("ImportInvalid", { autoHide: false });
94100 return;
95101 }
96102
97103 const reader = new FileReader();
98− reader.onloadend = function() {
99− const data = (() => { try { return JSON.parse(reader.result); } catch (error) {} })();
100− if (!data || (data.constructor !== Object)) {
101− new app.views.Notif('ImportInvalid', {autoHide: false});
104+ reader.onloadend = function () {
105+ const data = (() => {
106+ try {
107+ return JSON.parse(reader.result);
108+ } catch (error) {}
109+ })();
110+ if (!data || data.constructor !== Object) {
111+ new app.views.Notif("ImportInvalid", { autoHide: false });
102112 return;
103113 }
104114 app.settings.import(data);
105− $.trigger(input.form, 'import');
115+ $.trigger(input.form, "import");
106116 };
107117 reader.readAsText(file);
108118 }
@@ -110,25 +120,25 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line
110120 onChange(event) {
111121 const input = event.target;
112122 switch (input.name) {
113− case 'theme':
123+ case "theme":
114124 this.setTheme(input.value);
115125 break;
116− case 'layout':
126+ case "layout":
117127 this.toggleLayout(input.value, input.checked);
118128 break;
119− case 'smoothScroll':
129+ case "smoothScroll":
120130 this.toggleSmoothScroll(input.checked);
121131 break;
122− case 'import':
132+ case "import":
123133 this.import(input.files[0], input);
124134 break;
125− case 'analyticsConsent':
135+ case "analyticsConsent":
126136 this.toggleAnalyticsConsent(input.checked);
127137 break;
128− case 'spaceScroll':
138+ case "spaceScroll":
129139 this.toggleSpaceScroll(input.checked);
130140 break;
131− case 'spaceTimeout':
141+ case "spaceTimeout":
132142 this.setScrollTimeout(input.value);
133143 break;
134144 default:
@@ -138,8 +148,8 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 3 unmodified lines
138148
139149 onClick(event) {
140150 const target = $.eventTarget(event);
141− switch (target.getAttribute('data-action')) {
142− case 'export':
151+ switch (target.getAttribute("data-action")) {
152+ case "export":
143153 $.stopEvent(event);
144154 this.export();
145155 break;
assets/javascripts/views/content/static_page.js+7−7
@@ -8,13 +8,13 @@ · 7 unmodified lines
88 */
99 const Cls = (app.views.StaticPage = class StaticPage extends app.View {
1010 static initClass() {
11− this.className = '_static';
12−
11+ this.className = "_static";
12+
1313 this.titles = {
14− about: 'About',
15− news: 'News',
16− help: 'User Guide',
17− notFound: '404'
14+ about: "About",
15+ news: "News",
16+ help: "User Guide",
17+ notFound: "404",
1818 };
1919 }
2020
@@ -35,7 +35,7 @@ const Cls = (app.views.StaticPage = class StaticPage extends app.View { · 14 unmodified lines
3535 }
3636
3737 onRoute(context) {
38− this.render(context.page || 'notFound');
38+ this.render(context.page || "notFound");
3939 }
4040 });
4141 Cls.initClass();
assets/javascripts/views/content/type_page.js+2−2
@@ -8,7 +8,7 @@ · 7 unmodified lines
88 */
99 const Cls = (app.views.TypePage = class TypePage extends app.View {
1010 static initClass() {
11− this.className = '_page';
11+ this.className = "_page";
1212 }
1313
1414 deactivate() {
@@ -20,7 +20,7 @@ const Cls = (app.views.TypePage = class TypePage extends app.View { · 5 unmodified lines
2020
2121 render(type) {
2222 this.type = type;
23− this.html(this.tmpl('typePage', this.type));
23+ this.html(this.tmpl("typePage", this.type));
2424 setFaviconForDoc(this.type.doc);
2525 }
2626
assets/javascripts/views/layout/document.js+69−40
@@ -17,41 +17,49 @@ const Cls = (app.views.Document = class Document extends app.View { · 16 unmodified lines
1717
1818 static initClass() {
1919 this.el = document;
20−
21− this.events =
22− {visibilitychange: 'onVisibilityChange'};
23−
20+
21+ this.events = { visibilitychange: "onVisibilityChange" };
22+
2423 this.shortcuts = {
25− help: 'onHelp',
26− preferences: 'onPreferences',
27− escape: 'onEscape',
28− superLeft: 'onBack',
29− superRight: 'onForward'
24+ help: "onHelp",
25+ preferences: "onPreferences",
26+ escape: "onEscape",
27+ superLeft: "onBack",
28+ superRight: "onForward",
3029 };
31−
32− this.routes =
33− {after: 'afterRoute'};
30+
31+ this.routes = { after: "afterRoute" };
3432 }
3533
3634 init() {
37− this.addSubview((this.menu = new app.views.Menu),
38− this.addSubview(this.sidebar = new app.views.Sidebar));
39− if (app.views.Resizer.isSupported()) { this.addSubview(this.resizer = new app.views.Resizer); }
40− this.addSubview(this.content = new app.views.Content);
41− if (!app.isSingleDoc() && !app.isMobile()) { this.addSubview(this.path = new app.views.Path); }
42− if (!app.isSingleDoc()) { this.settings = new app.views.Settings; }
35+ this.addSubview(
36+ (this.menu = new app.views.Menu()),
37+ this.addSubview((this.sidebar = new app.views.Sidebar())),
38+ );
39+ if (app.views.Resizer.isSupported()) {
40+ this.addSubview((this.resizer = new app.views.Resizer()));
41+ }
42+ this.addSubview((this.content = new app.views.Content()));
43+ if (!app.isSingleDoc() && !app.isMobile()) {
44+ this.addSubview((this.path = new app.views.Path()));
45+ }
46+ if (!app.isSingleDoc()) {
47+ this.settings = new app.views.Settings();
48+ }
4349
44− $.on(document.body, 'click', this.onClick);
50+ $.on(document.body, "click", this.onClick);
4551
4652 this.activate();
4753 }
4854
4955 setTitle(title) {
50− return this.el.title = title ? `${title} — DevDocs` : 'DevDocs API Documentation';
56+ return (this.el.title = title
57+ ? `${title} — DevDocs`
58+ : "DevDocs API Documentation");
5159 }
5260
5361 afterRoute(route) {
54− if (route === 'settings') {
62+ if (route === "settings") {
5563 if (this.settings != null) {
5664 this.settings.activate();
5765 }
@@ -63,26 +71,29 @@ const Cls = (app.views.Document = class Document extends app.View { · 5 unmodified lines
6371 }
6472
6573 onVisibilityChange() {
66− if (this.el.visibilityState !== 'visible') { return; }
67− this.delay(function() {
68− if (app.isMobile() !== app.views.Mobile.detect()) { location.reload(); }
74+ if (this.el.visibilityState !== "visible") {
75+ return;
6976 }
70− , 300);
77+ this.delay(function () {
78+ if (app.isMobile() !== app.views.Mobile.detect()) {
79+ location.reload();
80+ }
81+ }, 300);
7182 }
7283
7384 onHelp() {
74− app.router.show('/help#shortcuts');
85+ app.router.show("/help#shortcuts");
7586 }
7687
7788 onPreferences() {
78− app.router.show('/settings');
89+ app.router.show("/settings");
7990 }
8091
8192 onEscape() {
82− const path = !app.isSingleDoc() || (location.pathname === app.doc.fullPath()) ?
83− '/'
84− :
85− app.doc.fullPath();
93+ const path =
94+ !app.isSingleDoc() || location.pathname === app.doc.fullPath()
95+ ? "/"
96+ : app.doc.fullPath();
8697
8798 app.router.show(path);
8899 }
@@ -97,16 +108,34 @@ const Cls = (app.views.Document = class Document extends app.View { · 8 unmodified lines
97108
98109 onClick(event) {
99110 const target = $.eventTarget(event);
100− if (!target.hasAttribute('data-behavior')) { return; }
111+ if (!target.hasAttribute("data-behavior")) {
112+ return;
113+ }
101114 $.stopEvent(event);
102− switch (target.getAttribute('data-behavior')) {
103− case 'back': history.back(); break;
104− case 'reload': window.location.reload(); break;
105− case 'reboot': app.reboot(); break;
106− case 'hard-reload': app.reload(); break;
107− case 'reset': if (confirm('Are you sure you want to reset DevDocs?')) { app.reset(); } break;
108− case 'accept-analytics': Cookies.set('analyticsConsent', '1', {expires: 1e8}) && app.reboot(); break;
109− case 'decline-analytics': Cookies.set('analyticsConsent', '0', {expires: 1e8}) && app.reboot(); break;
115+ switch (target.getAttribute("data-behavior")) {
116+ case "back":
117+ history.back();
118+ break;
119+ case "reload":
120+ window.location.reload();
121+ break;
122+ case "reboot":
123+ app.reboot();
124+ break;
125+ case "hard-reload":
126+ app.reload();
127+ break;
128+ case "reset":
129+ if (confirm("Are you sure you want to reset DevDocs?")) {
130+ app.reset();
131+ }
132+ break;
133+ case "accept-analytics":
134+ Cookies.set("analyticsConsent", "1", { expires: 1e8 }) && app.reboot();
135+ break;
136+ case "decline-analytics":
137+ Cookies.set("analyticsConsent", "0", { expires: 1e8 }) && app.reboot();
138+ break;
110139 }
111140 }
112141 });
assets/javascripts/views/layout/menu.js+16−9
@@ -13,25 +13,32 @@ const Cls = (app.views.Menu = class Menu extends app.View { · 12 unmodified lines
1313 }
1414
1515 static initClass() {
16− this.el = '._menu';
17− this.activeClass = 'active';
18−
19− this.events =
20− {click: 'onClick'};
16+ this.el = "._menu";
17+ this.activeClass = "active";
18+
19+ this.events = { click: "onClick" };
2120 }
2221
2322 init() {
24− $.on(document.body, 'click', this.onGlobalClick);
23+ $.on(document.body, "click", this.onGlobalClick);
2524 }
2625
2726 onClick(event) {
2827 const target = $.eventTarget(event);
29− if (target.tagName === 'A') { target.blur(); }
28+ if (target.tagName === "A") {
29+ target.blur();
30+ }
3031 }
3132
3233 onGlobalClick(event) {
33− if (event.which !== 1) { return; }
34− if (typeof event.target.hasAttribute === 'function' ? event.target.hasAttribute('data-toggle-menu') : undefined) {
34+ if (event.which !== 1) {
35+ return;
36+ }
37+ if (
38+ typeof event.target.hasAttribute === "function"
39+ ? event.target.hasAttribute("data-toggle-menu")
40+ : undefined
41+ ) {
3542 this.toggleClass(this.constructor.activeClass);
3643 } else if (this.hasClass(this.constructor.activeClass)) {
3744 this.removeClass(this.constructor.activeClass);
assets/javascripts/views/layout/mobile.js+77−63
@@ -10,34 +10,36 @@ · 9 unmodified lines
1010 */
1111 const Cls = (app.views.Mobile = class Mobile extends app.View {
1212 static initClass() {
13− this.className = '_mobile';
14−
13+ this.className = "_mobile";
14+
1515 this.elements = {
16− body: 'body',
17− content: '._container',
18− sidebar: '._sidebar',
19− docPicker: '._settings ._sidebar'
16+ body: "body",
17+ content: "._container",
18+ sidebar: "._sidebar",
19+ docPicker: "._settings ._sidebar",
2020 };
21−
22− this.shortcuts =
23− {escape: 'onEscape'};
24−
25− this.routes =
26− {after: 'afterRoute'};
21+
22+ this.shortcuts = { escape: "onEscape" };
23+
24+ this.routes = { after: "afterRoute" };
2725 }
2826
2927 static detect() {
30− if (Cookies.get('override-mobile-detect') != null) {
31− return JSON.parse(Cookies.get('override-mobile-detect'));
28+ if (Cookies.get("override-mobile-detect") != null) {
29+ return JSON.parse(Cookies.get("override-mobile-detect"));
3230 }
3331 try {
34− return (window.matchMedia('(max-width: 480px)').matches) ||
35− (window.matchMedia('(max-width: 767px)').matches) ||
36− (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) ||
37− // Need to sniff the user agent because some Android and Windows Phone devices don't take
38− // resolution (dpi) into account when reporting device width/height.
39− ((navigator.userAgent.indexOf('Android') !== -1) && (navigator.userAgent.indexOf('Mobile') !== -1)) ||
40− (navigator.userAgent.indexOf('IEMobile') !== -1);
32+ return (
33+ window.matchMedia("(max-width: 480px)").matches ||
34+ window.matchMedia("(max-width: 767px)").matches ||
35+ window.matchMedia("(max-height: 767px) and (max-width: 1024px)")
36+ .matches ||
37+ // Need to sniff the user agent because some Android and Windows Phone devices don't take
38+ // resolution (dpi) into account when reporting device width/height.
39+ (navigator.userAgent.indexOf("Android") !== -1 &&
40+ navigator.userAgent.indexOf("Mobile") !== -1) ||
41+ navigator.userAgent.indexOf("IEMobile") !== -1
42+ );
4143 } catch (error) {
4244 return false;
4345 }
@@ -67,30 +69,29 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 23 unmodified lines
6769 }
6870
6971 init() {
70− $.on($('._search'), 'touchend', this.onTapSearch);
72+ $.on($("._search"), "touchend", this.onTapSearch);
7173
72− this.toggleSidebar = $('button[data-toggle-sidebar]');
73− this.toggleSidebar.removeAttribute('hidden');
74− $.on(this.toggleSidebar, 'click', this.onClickToggleSidebar);
74+ this.toggleSidebar = $("button[data-toggle-sidebar]");
75+ this.toggleSidebar.removeAttribute("hidden");
76+ $.on(this.toggleSidebar, "click", this.onClickToggleSidebar);
7577
76− this.back = $('button[data-back]');
77− this.back.removeAttribute('hidden');
78− $.on(this.back, 'click', this.onClickBack);
78+ this.back = $("button[data-back]");
79+ this.back.removeAttribute("hidden");
80+ $.on(this.back, "click", this.onClickBack);
7981
80− this.forward = $('button[data-forward]');
81− this.forward.removeAttribute('hidden');
82− $.on(this.forward, 'click', this.onClickForward);
82+ this.forward = $("button[data-forward]");
83+ this.forward.removeAttribute("hidden");
84+ $.on(this.forward, "click", this.onClickForward);
8385
8486 this.docPickerTab = $('button[data-tab="doc-picker"]');
85− this.docPickerTab.removeAttribute('hidden');
86− $.on(this.docPickerTab, 'click', this.onClickDocPickerTab);
87+ this.docPickerTab.removeAttribute("hidden");
88+ $.on(this.docPickerTab, "click", this.onClickDocPickerTab);
8789
8890 this.settingsTab = $('button[data-tab="settings"]');
89− this.settingsTab.removeAttribute('hidden');
90− $.on(this.settingsTab, 'click', this.onClickSettingsTab);
91+ this.settingsTab.removeAttribute("hidden");
92+ $.on(this.settingsTab, "click", this.onClickSettingsTab);
9193
92− app.document.sidebar.search
93− .on('searching', this.showSidebar);
94+ app.document.sidebar.search.on("searching", this.showSidebar);
9495
9596 this.activate();
9697 }
@@ -103,27 +104,36 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 6 unmodified lines
103104 }
104105
105106 this.contentTop = window.scrollY;
106− this.content.style.display = 'none';
107− this.sidebar.style.display = 'block';
108−
109− if (selection = this.findByClass(app.views.ListSelect.activeClass)) {
110− const scrollContainer = window.scrollY === this.body.scrollTop ? this.body : document.documentElement;
111− $.scrollTo(selection, scrollContainer, 'center');
107+ this.content.style.display = "none";
108+ this.sidebar.style.display = "block";
109+
110+ if ((selection = this.findByClass(app.views.ListSelect.activeClass))) {
111+ const scrollContainer =
112+ window.scrollY === this.body.scrollTop
113+ ? this.body
114+ : document.documentElement;
115+ $.scrollTo(selection, scrollContainer, "center");
112116 } else {
113− window.scrollTo(0, (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || 0);
117+ window.scrollTo(
118+ 0,
119+ (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) ||
120+ 0,
121+ );
114122 }
115123 }
116124
117125 hideSidebar() {
118− if (!this.isSidebarShown()) { return; }
126+ if (!this.isSidebarShown()) {
127+ return;
128+ }
119129 this.sidebarTop = window.scrollY;
120− this.sidebar.style.display = 'none';
121− this.content.style.display = 'block';
130+ this.sidebar.style.display = "none";
131+ this.content.style.display = "block";
122132 window.scrollTo(0, this.contentTop || 0);
123133 }
124134
125135 isSidebarShown() {
126− return this.sidebar.style.display !== 'none';
136+ return this.sidebar.style.display !== "none";
127137 }
128138
129139 onClickBack() {
@@ -135,7 +145,11 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 5 unmodified lines
135145 }
136146
137147 onClickToggleSidebar() {
138− if (this.isSidebarShown()) { this.hideSidebar(); } else { this.showSidebar(); }
148+ if (this.isSidebarShown()) {
149+ this.hideSidebar();
150+ } else {
151+ this.showSidebar();
152+ }
139153 }
140154
141155 onClickDocPickerTab(event) {
@@ -150,18 +164,18 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 8 unmodified lines
150164
151165 showDocPicker() {
152166 window.scrollTo(0, 0);
153− this.docPickerTab.classList.add('active');
154− this.settingsTab.classList.remove('active');
155− this.docPicker.style.display = 'block';
156− this.content.style.display = 'none';
167+ this.docPickerTab.classList.add("active");
168+ this.settingsTab.classList.remove("active");
169+ this.docPicker.style.display = "block";
170+ this.content.style.display = "none";
157171 }
158172
159173 showSettings() {
160174 window.scrollTo(0, 0);
161− this.docPickerTab.classList.remove('active');
162− this.settingsTab.classList.add('active');
163− this.docPicker.style.display = 'none';
164− this.content.style.display = 'block';
175+ this.docPickerTab.classList.remove("active");
176+ this.settingsTab.classList.add("active");
177+ this.docPicker.style.display = "none";
178+ this.content.style.display = "block";
165179 }
166180
167181 onTapSearch() {
@@ -175,22 +189,22 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 7 unmodified lines
175189 afterRoute(route) {
176190 this.hideSidebar();
177191
178− if (route === 'settings') {
192+ if (route === "settings") {
179193 this.showDocPicker();
180194 } else {
181− this.content.style.display = 'block';
195+ this.content.style.display = "block";
182196 }
183197
184198 if (page.canGoBack()) {
185− this.back.removeAttribute('disabled');
199+ this.back.removeAttribute("disabled");
186200 } else {
187− this.back.setAttribute('disabled', 'disabled');
201+ this.back.setAttribute("disabled", "disabled");
188202 }
189203
190204 if (page.canGoForward()) {
191− this.forward.removeAttribute('disabled');
205+ this.forward.removeAttribute("disabled");
192206 } else {
193− this.forward.setAttribute('disabled', 'disabled');
207+ this.forward.setAttribute("disabled", "disabled");
194208 }
195209 }
196210 });
assets/javascripts/views/layout/path.js+16−13
@@ -15,33 +15,36 @@ const Cls = (app.views.Path = class Path extends app.View { · 14 unmodified lines
1515 }
1616
1717 static initClass() {
18− this.className = '_path';
19− this.attributes =
20− {role: 'complementary'};
21−
22− this.events =
23− {click: 'onClick'};
24−
25− this.routes =
26− {after: 'afterRoute'};
18+ this.className = "_path";
19+ this.attributes = { role: "complementary" };
20+
21+ this.events = { click: "onClick" };
22+
23+ this.routes = { after: "afterRoute" };
2724 }
2825
2926 render(...args) {
30− this.html(this.tmpl('path', ...Array.from(args)));
27+ this.html(this.tmpl("path", ...Array.from(args)));
3128 this.show();
3229 }
3330
3431 show() {
35− if (!this.el.parentNode) { this.prependTo(app.el); }
32+ if (!this.el.parentNode) {
33+ this.prependTo(app.el);
34+ }
3635 }
3736
3837 hide() {
39− if (this.el.parentNode) { $.remove(this.el); }
38+ if (this.el.parentNode) {
39+ $.remove(this.el);
40+ }
4041 }
4142
4243 onClick(event) {
4344 let link;
44− if (link = $.closestLink(event.target, this.el)) { this.clicked = true; }
45+ if ((link = $.closestLink(event.target, this.el))) {
46+ this.clicked = true;
47+ }
4548 }
4649
4750 afterRoute(route, context) {
assets/javascripts/views/layout/resizer.js+32−20
@@ -7,7 +7,7 @@ · 6 unmodified lines
77 * DS206: Consider reworking classes to avoid initClass
88 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
99 */
10−(function() {
10+(function () {
1111 let MIN = undefined;
1212 let MAX = undefined;
1313 const Cls = (app.views.Resizer = class Resizer extends app.View {
@@ -19,54 +19,66 @@ · 5 unmodified lines
1919 }
2020
2121 static initClass() {
22− this.className = '_resizer';
23−
22+ this.className = "_resizer";
23+
2424 this.events = {
25− dragstart: 'onDragStart',
26− dragend: 'onDragEnd'
25+ dragstart: "onDragStart",
26+ dragend: "onDragEnd",
2727 };
28−
28+
2929 MIN = 260;
3030 MAX = 600;
3131 }
3232
3333 static isSupported() {
34− return 'ondragstart' in document.createElement('div') && !app.isMobile();
34+ return "ondragstart" in document.createElement("div") && !app.isMobile();
3535 }
3636
3737 init() {
38− this.el.setAttribute('draggable', 'true');
39− this.appendTo($('._app'));
38+ this.el.setAttribute("draggable", "true");
39+ this.appendTo($("._app"));
4040 }
4141
4242 resize(value, save) {
4343 value -= app.el.offsetLeft;
44− if (!(value > 0)) { return; }
44+ if (!(value > 0)) {
45+ return;
46+ }
4547 value = Math.min(Math.max(Math.round(value), MIN), MAX);
4648 const newSize = `${value}px`;
47− document.documentElement.style.setProperty('--sidebarWidth', newSize);
48− if (save) { app.settings.setSize(value); }
49+ document.documentElement.style.setProperty("--sidebarWidth", newSize);
50+ if (save) {
51+ app.settings.setSize(value);
52+ }
4953 }
5054
5155 onDragStart(event) {
52− event.dataTransfer.effectAllowed = 'link';
53− event.dataTransfer.setData('Text', '');
54− $.on(window, 'dragover', this.onDrag);
56+ event.dataTransfer.effectAllowed = "link";
57+ event.dataTransfer.setData("Text", "");
58+ $.on(window, "dragover", this.onDrag);
5559 }
5660
5761 onDrag(event) {
5862 const value = event.pageX;
59− if (!(value > 0)) { return; }
63+ if (!(value > 0)) {
64+ return;
65+ }
6066 this.lastDragValue = value;
61− if (this.lastDrag && (this.lastDrag > (Date.now() - 50))) { return; }
67+ if (this.lastDrag && this.lastDrag > Date.now() - 50) {
68+ return;
69+ }
6270 this.lastDrag = Date.now();
6371 this.resize(value, false);
6472 }
6573
6674 onDragEnd(event) {
67− $.off(window, 'dragover', this.onDrag);
68− let value = event.pageX || (event.screenX - window.screenX);
69− if (this.lastDragValue && !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)) { // https://github.com/freeCodeCamp/devdocs/issues/265
75+ $.off(window, "dragover", this.onDrag);
76+ let value = event.pageX || event.screenX - window.screenX;
77+ if (
78+ this.lastDragValue &&
79+ !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)
80+ ) {
81+ // https://github.com/freeCodeCamp/devdocs/issues/265
7082 value = this.lastDragValue;
7183 }
7284 this.resize(value, true);
assets/javascripts/views/layout/settings.js+41−33
@@ -10,7 +10,7 @@ · 9 unmodified lines
1010 * DS207: Consider shorter variations of null checks
1111 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1212 */
13−(function() {
13+(function () {
1414 let SIDEBAR_HIDDEN_LAYOUT = undefined;
1515 const Cls = (app.views.Settings = class Settings extends app.View {
1616 constructor(...args) {
@@ -23,29 +23,28 @@ · 6 unmodified lines
2323 }
2424
2525 static initClass() {
26− SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden';
27−
28− this.el = '._settings';
29−
26+ SIDEBAR_HIDDEN_LAYOUT = "_sidebar-hidden";
27+
28+ this.el = "._settings";
29+
3030 this.elements = {
31− sidebar: '._sidebar',
31+ sidebar: "._sidebar",
3232 saveBtn: 'button[type="submit"]',
33− backBtn: 'button[data-back]'
33+ backBtn: "button[data-back]",
3434 };
35−
35+
3636 this.events = {
37− import: 'onImport',
38− change: 'onChange',
39− submit: 'onSubmit',
40− click: 'onClick'
37+ import: "onImport",
38+ change: "onChange",
39+ submit: "onSubmit",
40+ click: "onClick",
4141 };
42−
43− this.shortcuts =
44− {enter: 'onEnter'};
42+
43+ this.shortcuts = { enter: "onEnter" };
4544 }
4645
4746 init() {
48− this.addSubview(this.docPicker = new app.views.DocPicker);
47+ this.addSubview((this.docPicker = new app.views.DocPicker()));
4948 }
5049
5150 activate() {
@@ -59,18 +58,22 @@ · 7 unmodified lines
5958 if (super.deactivate(...arguments)) {
6059 this.resetClass();
6160 this.docPicker.detach();
62− if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) { document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT); }
61+ if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) {
62+ document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT);
63+ }
6364 }
6465 }
6566
6667 render() {
6768 this.docPicker.appendTo(this.sidebar);
6869 this.refreshElements();
69− this.addClass('_in');
70+ this.addClass("_in");
7071 }
7172
7273 save(options) {
73− if (options == null) { options = {}; }
74+ if (options == null) {
75+ options = {};
76+ }
7477 if (!this.saving) {
7578 let docs;
7679 this.saving = true;
@@ -82,16 +85,19 @@ · 5 unmodified lines
8285 app.settings.setDocs(docs);
8386 }
8487
85− this.saveBtn.textContent = 'Saving\u2026';
86− const disabledDocs = new app.collections.Docs((() => {
87− const result = [];
88− for (var doc of Array.from(app.docs.all())) { if (docs.indexOf(doc.slug) === -1) {
89− result.push(doc);
88+ this.saveBtn.textContent = "Saving\u2026";
89+ const disabledDocs = new app.collections.Docs(
90+ (() => {
91+ const result = [];
92+ for (var doc of Array.from(app.docs.all())) {
93+ if (docs.indexOf(doc.slug) === -1) {
94+ result.push(doc);
95+ }
9096 }
91− }
92− return result;
93− })());
94− disabledDocs.uninstall(function() {
97+ return result;
98+ })(),
99+ );
100+ disabledDocs.uninstall(function () {
95101 app.db.migrate();
96102 return app.reload();
97103 });
@@ -99,7 +105,7 @@ · 1 unmodified line
99105 }
100106
101107 onChange() {
102− this.addClass('_dirty');
108+ this.addClass("_dirty");
103109 }
104110
105111 onEnter() {
@@ -112,15 +118,17 @@ · 6 unmodified lines
112118 }
113119
114120 onImport() {
115− this.addClass('_dirty');
116− this.save({import: true});
121+ this.addClass("_dirty");
122+ this.save({ import: true });
117123 }
118124
119125 onClick(event) {
120− if (event.which !== 1) { return; }
126+ if (event.which !== 1) {
127+ return;
128+ }
121129 if (event.target === this.backBtn) {
122130 $.stopEvent(event);
123− app.router.show('/');
131+ app.router.show("/");
124132 }
125133 }
126134 });
assets/javascripts/views/list/list_focus.js+71−44
@@ -10,18 +10,17 @@ · 9 unmodified lines
1010 */
1111 const Cls = (app.views.ListFocus = class ListFocus extends app.View {
1212 static initClass() {
13− this.activeClass = 'focus';
14−
15− this.events =
16− {click: 'onClick'};
17−
13+ this.activeClass = "focus";
14+
15+ this.events = { click: "onClick" };
16+
1817 this.shortcuts = {
19− up: 'onUp',
20− down: 'onDown',
21− left: 'onLeft',
22− enter: 'onEnter',
23− superEnter: 'onSuperEnter',
24− escape: 'blur'
18+ up: "onUp",
19+ down: "onDown",
20+ left: "onLeft",
21+ enter: "onEnter",
22+ superEnter: "onSuperEnter",
23+ escape: "blur",
2524 };
2625 }
2726
@@ -39,41 +38,51 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 11 unmodified lines
3938 }
4039
4140 focus(el, options) {
42− if (options == null) { options = {}; }
41+ if (options == null) {
42+ options = {};
43+ }
4344 if (el && !el.classList.contains(this.constructor.activeClass)) {
4445 this.blur();
4546 el.classList.add(this.constructor.activeClass);
46− if (options.silent !== true) { $.trigger(el, 'focus'); }
47+ if (options.silent !== true) {
48+ $.trigger(el, "focus");
49+ }
4750 }
4851 }
4952
5053 blur() {
5154 let cursor;
52− if (cursor = this.getCursor()) {
55+ if ((cursor = this.getCursor())) {
5356 cursor.classList.remove(this.constructor.activeClass);
54− $.trigger(cursor, 'blur');
57+ $.trigger(cursor, "blur");
5558 }
5659 }
5760
5861 getCursor() {
59− return this.findByClass(this.constructor.activeClass) || this.findByClass(app.views.ListSelect.activeClass);
62+ return (
63+ this.findByClass(this.constructor.activeClass) ||
64+ this.findByClass(app.views.ListSelect.activeClass)
65+ );
6066 }
6167
6268 findNext(cursor) {
6369 let next;
64− if (next = cursor.nextSibling) {
65− if (next.tagName === 'A') {
70+ if ((next = cursor.nextSibling)) {
71+ if (next.tagName === "A") {
6672 return next;
67− } else if (next.tagName === 'SPAN') { // pagination link
73+ } else if (next.tagName === "SPAN") {
74+ // pagination link
6875 $.click(next);
6976 return this.findNext(cursor);
70− } else if (next.tagName === 'DIV') { // sub-list
71− if (cursor.className.indexOf(' open') >= 0) {
77+ } else if (next.tagName === "DIV") {
78+ // sub-list
79+ if (cursor.className.indexOf(" open") >= 0) {
7280 return this.findFirst(next) || this.findNext(next);
7381 } else {
7482 return this.findNext(next);
7583 }
76− } else if (next.tagName === 'H6') { // title
84+ } else if (next.tagName === "H6") {
85+ // title
7786 return this.findNext(next);
7887 }
7988 } else if (cursor.parentNode !== this.el) {
@@ -83,11 +92,14 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines
8392
8493 findFirst(cursor) {
8594 let first;
86− if (!(first = cursor.firstChild)) { return; }
95+ if (!(first = cursor.firstChild)) {
96+ return;
97+ }
8798
88− if (first.tagName === 'A') {
99+ if (first.tagName === "A") {
89100 return first;
90− } else if (first.tagName === 'SPAN') { // pagination link
101+ } else if (first.tagName === "SPAN") {
102+ // pagination link
91103 $.click(first);
92104 return this.findFirst(cursor);
93105 }
@@ -95,19 +107,22 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 1 unmodified line
95107
96108 findPrev(cursor) {
97109 let prev;
98− if (prev = cursor.previousSibling) {
99− if (prev.tagName === 'A') {
110+ if ((prev = cursor.previousSibling)) {
111+ if (prev.tagName === "A") {
100112 return prev;
101− } else if (prev.tagName === 'SPAN') { // pagination link
113+ } else if (prev.tagName === "SPAN") {
114+ // pagination link
102115 $.click(prev);
103116 return this.findPrev(cursor);
104− } else if (prev.tagName === 'DIV') { // sub-list
105− if (prev.previousSibling.className.indexOf('open') >= 0) {
117+ } else if (prev.tagName === "DIV") {
118+ // sub-list
119+ if (prev.previousSibling.className.indexOf("open") >= 0) {
106120 return this.findLast(prev) || this.findPrev(prev);
107121 } else {
108122 return this.findPrev(prev);
109123 }
110− } else if (prev.tagName === 'H6') { // title
124+ } else if (prev.tagName === "H6") {
125+ // title
111126 return this.findPrev(prev);
112127 }
113128 } else if (cursor.parentNode !== this.el) {
@@ -117,13 +132,17 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines
117132
118133 findLast(cursor) {
119134 let last;
120− if (!(last = cursor.lastChild)) { return; }
135+ if (!(last = cursor.lastChild)) {
136+ return;
137+ }
121138
122− if (last.tagName === 'A') {
139+ if (last.tagName === "A") {
123140 return last;
124− } else if ((last.tagName === 'SPAN') || (last.tagName === 'H6')) { // pagination link or title
141+ } else if (last.tagName === "SPAN" || last.tagName === "H6") {
142+ // pagination link or title
125143 return this.findPrev(last);
126− } else if (last.tagName === 'DIV') { // sub-list
144+ } else if (last.tagName === "DIV") {
145+ // sub-list
127146 return this.findLast(last);
128147 }
129148 }
@@ -133,7 +152,7 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines
133152 if ((cursor = this.getCursor())) {
134153 this.focusOnNextFrame(this.findNext(cursor));
135154 } else {
136− this.focusOnNextFrame(this.findByTag('a'));
155+ this.focusOnNextFrame(this.findByTag("a"));
137156 }
138157 }
139158
@@ -142,37 +161,45 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 2 unmodified lines
142161 if ((cursor = this.getCursor())) {
143162 this.focusOnNextFrame(this.findPrev(cursor));
144163 } else {
145− this.focusOnNextFrame(this.findLastByTag('a'));
164+ this.focusOnNextFrame(this.findLastByTag("a"));
146165 }
147166 }
148167
149168 onLeft() {
150169 const cursor = this.getCursor();
151− if (cursor && !cursor.classList.contains(app.views.ListFold.activeClass) && (cursor.parentNode !== this.el)) {
170+ if (
171+ cursor &&
172+ !cursor.classList.contains(app.views.ListFold.activeClass) &&
173+ cursor.parentNode !== this.el
174+ ) {
152175 const prev = cursor.parentNode.previousSibling;
153− if (prev && prev.classList.contains(app.views.ListFold.targetClass)) { this.focusOnNextFrame(cursor.parentNode.previousSibling); }
176+ if (prev && prev.classList.contains(app.views.ListFold.targetClass)) {
177+ this.focusOnNextFrame(cursor.parentNode.previousSibling);
178+ }
154179 }
155180 }
156181
157182 onEnter() {
158183 let cursor;
159− if (cursor = this.getCursor()) {
184+ if ((cursor = this.getCursor())) {
160185 $.click(cursor);
161186 }
162187 }
163188
164189 onSuperEnter() {
165190 let cursor;
166− if (cursor = this.getCursor()) {
191+ if ((cursor = this.getCursor())) {
167192 $.popup(cursor);
168193 }
169194 }
170195
171196 onClick(event) {
172− if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
197+ if (event.which !== 1 || event.metaKey || event.ctrlKey) {
198+ return;
199+ }
173200 const target = $.eventTarget(event);
174− if (target.tagName === 'A') {
175− this.focus(target, {silent: true});
201+ if (target.tagName === "A") {
202+ this.focus(target, { silent: true });
176203 }
177204 }
178205 });
assets/javascripts/views/list/list_fold.js+44−20
@@ -10,32 +10,37 @@ · 9 unmodified lines
1010 */
1111 const Cls = (app.views.ListFold = class ListFold extends app.View {
1212 static initClass() {
13− this.targetClass = '_list-dir';
14− this.handleClass = '_list-arrow';
15− this.activeClass = 'open';
16−
17− this.events =
18− {click: 'onClick'};
19−
13+ this.targetClass = "_list-dir";
14+ this.handleClass = "_list-arrow";
15+ this.activeClass = "open";
16+
17+ this.events = { click: "onClick" };
18+
2019 this.shortcuts = {
21− left: 'onLeft',
22− right: 'onRight'
20+ left: "onLeft",
21+ right: "onRight",
2322 };
2423 }
2524
26− constructor(el) { this.onLeft = this.onLeft.bind(this); this.onRight = this.onRight.bind(this); this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); }
25+ constructor(el) {
26+ this.onLeft = this.onLeft.bind(this);
27+ this.onRight = this.onRight.bind(this);
28+ this.onClick = this.onClick.bind(this);
29+ this.el = el;
30+ super(...arguments);
31+ }
2732
2833 open(el) {
2934 if (el && !el.classList.contains(this.constructor.activeClass)) {
3035 el.classList.add(this.constructor.activeClass);
31− $.trigger(el, 'open');
36+ $.trigger(el, "open");
3237 }
3338 }
3439
3540 close(el) {
3641 if (el && el.classList.contains(this.constructor.activeClass)) {
3742 el.classList.remove(this.constructor.activeClass);
38− $.trigger(el, 'close');
43+ $.trigger(el, "close");
3944 }
4045 }
4146
@@ -55,36 +60,55 @@ const Cls = (app.views.ListFold = class ListFold extends app.View { · 13 unmodified lines
5560 }
5661
5762 getCursor() {
58− return this.findByClass(app.views.ListFocus.activeClass) || this.findByClass(app.views.ListSelect.activeClass);
63+ return (
64+ this.findByClass(app.views.ListFocus.activeClass) ||
65+ this.findByClass(app.views.ListSelect.activeClass)
66+ );
5967 }
6068
6169 onLeft() {
6270 const cursor = this.getCursor();
63− if (cursor != null ? cursor.classList.contains(this.constructor.activeClass) : undefined) {
71+ if (
72+ cursor != null
73+ ? cursor.classList.contains(this.constructor.activeClass)
74+ : undefined
75+ ) {
6476 this.close(cursor);
6577 }
6678 }
6779
6880 onRight() {
6981 const cursor = this.getCursor();
70− if (cursor != null ? cursor.classList.contains(this.constructor.targetClass) : undefined) {
82+ if (
83+ cursor != null
84+ ? cursor.classList.contains(this.constructor.targetClass)
85+ : undefined
86+ ) {
7187 this.open(cursor);
7288 }
7389 }
7490
7591 onClick(event) {
76− if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
77− if (!event.pageY) { return; } // ignore fabricated clicks
92+ if (event.which !== 1 || event.metaKey || event.ctrlKey) {
93+ return;
94+ }
95+ if (!event.pageY) {
96+ return;
97+ } // ignore fabricated clicks
7898 let el = $.eventTarget(event);
79− if (el.parentNode.tagName.toUpperCase() === 'SVG') { el = el.parentNode; }
99+ if (el.parentNode.tagName.toUpperCase() === "SVG") {
100+ el = el.parentNode;
101+ }
80102
81103 if (el.classList.contains(this.constructor.handleClass)) {
82104 $.stopEvent(event);
83105 this.toggle(el.parentNode);
84106 } else if (el.classList.contains(this.constructor.targetClass)) {
85− if (el.hasAttribute('href')) {
107+ if (el.hasAttribute("href")) {
86108 if (el.classList.contains(this.constructor.activeClass)) {
87− if (el.classList.contains(app.views.ListSelect.activeClass)) { this.close(el); }
109+ if (el.classList.contains(app.views.ListSelect.activeClass)) {
110+ this.close(el);
111+ }
88112 } else {
89113 this.open(el);
90114 }
assets/javascripts/views/list/list_select.js+25−14
@@ -10,36 +10,43 @@ · 9 unmodified lines
1010 */
1111 const Cls = (app.views.ListSelect = class ListSelect extends app.View {
1212 static initClass() {
13− this.activeClass = 'active';
14−
15− this.events =
16− {click: 'onClick'};
13+ this.activeClass = "active";
14+
15+ this.events = { click: "onClick" };
1716 }
1817
19− constructor(el) { this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); }
18+ constructor(el) {
19+ this.onClick = this.onClick.bind(this);
20+ this.el = el;
21+ super(...arguments);
22+ }
2023
2124 deactivate() {
22− if (super.deactivate(...arguments)) { this.deselect(); }
25+ if (super.deactivate(...arguments)) {
26+ this.deselect();
27+ }
2328 }
2429
2530 select(el) {
2631 this.deselect();
2732 if (el) {
2833 el.classList.add(this.constructor.activeClass);
29− $.trigger(el, 'select');
34+ $.trigger(el, "select");
3035 }
3136 }
3237
3338 deselect() {
3439 let selection;
35− if (selection = this.getSelection()) {
40+ if ((selection = this.getSelection())) {
3641 selection.classList.remove(this.constructor.activeClass);
37− $.trigger(selection, 'deselect');
42+ $.trigger(selection, "deselect");
3843 }
3944 }
4045
4146 selectByHref(href) {
42− if (__guard__(this.getSelection(), x => x.getAttribute('href')) !== href) {
47+ if (
48+ __guard__(this.getSelection(), (x) => x.getAttribute("href")) !== href
49+ ) {
4350 this.select(this.find(`a[href='${href}']`));
4451 }
4552 }
@@ -53,9 +60,11 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View { · 7 unmodified lines
5360 }
5461
5562 onClick(event) {
56− if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
63+ if (event.which !== 1 || event.metaKey || event.ctrlKey) {
64+ return;
65+ }
5766 const target = $.eventTarget(event);
58− if (target.tagName === 'A') {
67+ if (target.tagName === "A") {
5968 this.select(target);
6069 }
6170 }
@@ -63,5 +72,7 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View { · 1 unmodified line
6372 Cls.initClass();
6473
6574 function __guard__(value, transform) {
66− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
67−}
75+ return typeof value !== "undefined" && value !== null
76+ ? transform(value)
77+ : undefined;
78+}
assets/javascripts/views/list/paginated_list.js+57−22
@@ -10,7 +10,7 @@ · 9 unmodified lines
1010 * DS207: Consider shorter variations of null checks
1111 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1212 */
13−(function() {
13+(function () {
1414 let PER_PAGE = undefined;
1515 const Cls = (app.views.PaginatedList = class PaginatedList extends app.View {
1616 static initClass() {
@@ -21,7 +21,12 @@ · 4 unmodified lines
2121 let base;
2222 this.onClick = this.onClick.bind(this);
2323 this.data = data;
24− if (((base = this.constructor.events || (this.constructor.events = {}))).click == null) { base.click = 'onClick'; }
24+ if (
25+ (base = this.constructor.events || (this.constructor.events = {}))
26+ .click == null
27+ ) {
28+ base.click = "onClick";
29+ }
2530 super(...arguments);
2631 }
2732
@@ -42,11 +47,13 @@ · 14 unmodified lines
4247 }
4348
4449 renderPage(page) {
45− return this.render(this.data.slice(((page - 1) * PER_PAGE), (page * PER_PAGE)));
50+ return this.render(
51+ this.data.slice((page - 1) * PER_PAGE, page * PER_PAGE),
52+ );
4653 }
4754
4855 renderPageLink(count) {
49− return this.tmpl('sidebarPageLink', count);
56+ return this.tmpl("sidebarPageLink", count);
5057 }
5158
5259 renderPrevLink(page) {
@@ -54,7 +61,7 @@ · 1 unmodified line
5461 }
5562
5663 renderNextLink(page) {
57− return this.renderPageLink(this.data.length - (page * PER_PAGE));
64+ return this.renderPageLink(this.data.length - page * PER_PAGE);
5865 }
5966
6067 totalPages() {
@@ -64,17 +71,27 @@ · 3 unmodified lines
6471 paginate(link) {
6572 $.lockScroll(link.nextSibling || link.previousSibling, () => {
6673 $.batchUpdate(this.el, () => {
67− if (link.nextSibling) { this.paginatePrev(link); } else { this.paginateNext(link); }
74+ if (link.nextSibling) {
75+ this.paginatePrev(link);
76+ } else {
77+ this.paginateNext(link);
78+ }
6879 });
6980 });
7081 }
7182
7283 paginateNext() {
73− if (this.el.lastChild) { this.remove(this.el.lastChild); } // remove link
74− if (this.page >= 2) { this.hideTopPage(); } // keep previous page into view
84+ if (this.el.lastChild) {
85+ this.remove(this.el.lastChild);
86+ } // remove link
87+ if (this.page >= 2) {
88+ this.hideTopPage();
89+ } // keep previous page into view
7590 this.page++;
7691 this.append(this.renderPage(this.page));
77− if (this.page < this.totalPages()) { this.append(this.renderNextLink(this.page)); }
92+ if (this.page < this.totalPages()) {
93+ this.append(this.renderNextLink(this.page));
94+ }
7895 }
7996
8097 paginatePrev() {
@@ -82,37 +99,55 @@ · 1 unmodified line
8299 this.hideBottomPage();
83100 this.page--;
84101 this.prepend(this.renderPage(this.page - 1)); // previous page is offset by one
85− if (this.page >= 3) { this.prepend(this.renderPrevLink(this.page - 1)); }
102+ if (this.page >= 3) {
103+ this.prepend(this.renderPrevLink(this.page - 1));
104+ }
86105 }
87106
88107 paginateTo(object) {
89108 const index = this.data.indexOf(object);
90109 if (index >= PER_PAGE) {
91− for (let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.paginateNext(); }
110+ for (
111+ let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end;
112+ asc ? i < end : i > end;
113+ asc ? i++ : i--
114+ ) {
115+ this.paginateNext();
116+ }
92117 }
93118 }
94119
95120 hideTopPage() {
96− const n = this.page <= 2 ?
97− PER_PAGE
98− :
99− PER_PAGE + 1; // remove link
100− for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.firstChild); }
121+ const n = this.page <= 2 ? PER_PAGE : PER_PAGE + 1; // remove link
122+ for (
123+ let i = 0, end = n, asc = 0 <= end;
124+ asc ? i < end : i > end;
125+ asc ? i++ : i--
126+ ) {
127+ this.remove(this.el.firstChild);
128+ }
101129 this.prepend(this.renderPrevLink(this.page));
102130 }
103131
104132 hideBottomPage() {
105− const n = this.page === this.totalPages() ?
106− (this.data.length % PER_PAGE) || PER_PAGE
107− :
108− PER_PAGE + 1; // remove link
109− for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.lastChild); }
133+ const n =
134+ this.page === this.totalPages()
135+ ? this.data.length % PER_PAGE || PER_PAGE
136+ : PER_PAGE + 1; // remove link
137+ for (
138+ let i = 0, end = n, asc = 0 <= end;
139+ asc ? i < end : i > end;
140+ asc ? i++ : i--
141+ ) {
142+ this.remove(this.el.lastChild);
143+ }
110144 this.append(this.renderNextLink(this.page - 1));
111145 }
112146
113147 onClick(event) {
114148 const target = $.eventTarget(event);
115− if (target.tagName === 'SPAN') { // link
149+ if (target.tagName === "SPAN") {
150+ // link
116151 $.stopEvent(event);
117152 this.paginate(target);
118153 }
assets/javascripts/views/misc/news.js+14−9
@@ -12,15 +12,16 @@ · 11 unmodified lines
1212
1313 const Cls = (app.views.News = class News extends app.views.Notif {
1414 static initClass() {
15− this.className += ' _notif-news';
16−
17− this.defautOptions =
18− {autoHide: 30000};
15+ this.className += " _notif-news";
16+
17+ this.defautOptions = { autoHide: 30000 };
1918 }
2019
2120 init() {
2221 this.unreadNews = this.getUnreadNews();
23− if (this.unreadNews.length) { this.show(); }
22+ if (this.unreadNews.length) {
23+ this.show();
24+ }
2425 this.markAllAsRead();
2526 }
2627
@@ -30,12 +31,16 @@ const Cls = (app.views.News = class News extends app.views.Notif { · 3 unmodified lines
3031
3132 getUnreadNews() {
3233 let time;
33− if (!(time = this.getLastReadTime())) { return []; }
34+ if (!(time = this.getLastReadTime())) {
35+ return [];
36+ }
3437
3538 return (() => {
3639 const result = [];
3740 for (var news of Array.from(app.news)) {
38− if (new Date(news[0]).getTime() <= time) { break; }
41+ if (new Date(news[0]).getTime() <= time) {
42+ break;
43+ }
3944 result.push(news);
4045 }
4146 return result;
@@ -47,11 +52,11 @@ const Cls = (app.views.News = class News extends app.views.Notif { · 5 unmodified lines
4752 }
4853
4954 getLastReadTime() {
50− return app.settings.get('news');
55+ return app.settings.get("news");
5156 }
5257
5358 markAllAsRead() {
54− app.settings.set('news', this.getLastNewsTime());
59+ app.settings.set("news", this.getLastNewsTime());
5560 }
5661 });
5762 Cls.initClass();
assets/javascripts/views/misc/notice.js+13−6
@@ -9,23 +9,30 @@ · 8 unmodified lines
99 */
1010 const Cls = (app.views.Notice = class Notice extends app.View {
1111 static initClass() {
12− this.className = '_notice';
13− this.attributes =
14− {role: 'alert'};
12+ this.className = "_notice";
13+ this.attributes = { role: "alert" };
1514 }
1615
17− constructor(type, ...rest) { this.type = type; [...this.args] = Array.from(rest); super(...arguments); }
16+ constructor(type, ...rest) {
17+ this.type = type;
18+ [...this.args] = Array.from(rest);
19+ super(...arguments);
20+ }
1821
1922 init() {
2023 this.activate();
2124 }
2225
2326 activate() {
24− if (super.activate(...arguments)) { this.show(); }
27+ if (super.activate(...arguments)) {
28+ this.show();
29+ }
2530 }
2631
2732 deactivate() {
28− if (super.deactivate(...arguments)) { this.hide(); }
33+ if (super.deactivate(...arguments)) {
34+ this.hide();
35+ }
2936 }
3037
3138 show() {
assets/javascripts/views/misc/notif.js+22−16
@@ -9,22 +9,21 @@ · 8 unmodified lines
99 */
1010 const Cls = (app.views.Notif = class Notif extends app.View {
1111 static initClass() {
12− this.className = '_notif';
13− this.activeClass = '_in';
14− this.attributes =
15− {role: 'alert'};
16−
17− this.defautOptions =
18− {autoHide: 15000};
19−
20− this.events =
21− {click: 'onClick'};
12+ this.className = "_notif";
13+ this.activeClass = "_in";
14+ this.attributes = { role: "alert" };
15+
16+ this.defautOptions = { autoHide: 15000 };
17+
18+ this.events = { click: "onClick" };
2219 }
2320
2421 constructor(type, options) {
2522 this.onClick = this.onClick.bind(this);
2623 this.type = type;
27− if (options == null) { options = {}; }
24+ if (options == null) {
25+ options = {};
26+ }
2827 this.options = options;
2928 this.options = $.extend({}, this.constructor.defautOptions, this.options);
3029 super(...arguments);
@@ -45,7 +44,9 @@ const Cls = (app.views.Notif = class Notif extends app.View { · 14 unmodified lines
4544 this.appendTo(document.body);
4645 this.el.offsetWidth; // force reflow
4746 this.addClass(this.constructor.activeClass);
48− if (this.options.autoHide) { this.timeout = this.delay(this.hide, this.options.autoHide); }
47+ if (this.options.autoHide) {
48+ this.timeout = this.delay(this.hide, this.options.autoHide);
49+ }
4950 }
5051 }
5152
@@ -63,15 +64,20 @@ const Cls = (app.views.Notif = class Notif extends app.View { · 11 unmodified lines
6364 const notifications = $$(`.${app.views.Notif.className}`);
6465 if (notifications.length) {
6566 const lastNotif = notifications[notifications.length - 1];
66− this.el.style.top = lastNotif.offsetTop + lastNotif.offsetHeight + 16 + 'px';
67+ this.el.style.top =
68+ lastNotif.offsetTop + lastNotif.offsetHeight + 16 + "px";
6769 }
6870 }
6971
7072 onClick(event) {
71− if (event.which !== 1) { return; }
73+ if (event.which !== 1) {
74+ return;
75+ }
7276 const target = $.eventTarget(event);
73− if (target.hasAttribute('data-behavior')) { return; }
74− if ((target.tagName !== 'A') || target.classList.contains('_notif-close')) {
77+ if (target.hasAttribute("data-behavior")) {
78+ return;
79+ }
80+ if (target.tagName !== "A" || target.classList.contains("_notif-close")) {
7581 $.stopEvent(event);
7682 this.hide();
7783 }
assets/javascripts/views/misc/tip.js+3−4
@@ -9,10 +9,9 @@ · 8 unmodified lines
99
1010 const Cls = (app.views.Tip = class Tip extends app.views.Notif {
1111 static initClass() {
12− this.className = '_notif _notif-tip';
13−
14− this.defautOptions =
15− {autoHide: false};
12+ this.className = "_notif _notif-tip";
13+
14+ this.defautOptions = { autoHide: false };
1615 }
1716
1817 render() {
assets/javascripts/views/misc/updates.js+30−12
@@ -12,34 +12,47 @@ · 11 unmodified lines
1212
1313 const Cls = (app.views.Updates = class Updates extends app.views.Notif {
1414 static initClass() {
15− this.className += ' _notif-news';
16−
17− this.defautOptions =
18− {autoHide: 30000};
15+ this.className += " _notif-news";
16+
17+ this.defautOptions = { autoHide: 30000 };
1918 }
2019
2120 init() {
2221 this.lastUpdateTime = this.getLastUpdateTime();
2322 this.updatedDocs = this.getUpdatedDocs();
2423 this.updatedDisabledDocs = this.getUpdatedDisabledDocs();
25− if ((this.updatedDocs.length > 0) || (this.updatedDisabledDocs.length > 0)) { this.show(); }
24+ if (this.updatedDocs.length > 0 || this.updatedDisabledDocs.length > 0) {
25+ this.show();
26+ }
2627 this.markAllAsRead();
2728 }
2829
2930 render() {
30− this.html(app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs));
31+ this.html(
32+ app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs),
33+ );
3134 }
3235
3336 getUpdatedDocs() {
34− if (!this.lastUpdateTime) { return []; }
35− return Array.from(app.docs.all()).filter((doc) => doc.mtime > this.lastUpdateTime);
37+ if (!this.lastUpdateTime) {
38+ return [];
39+ }
40+ return Array.from(app.docs.all()).filter(
41+ (doc) => doc.mtime > this.lastUpdateTime,
42+ );
3643 }
3744
3845 getUpdatedDisabledDocs() {
39− if (!this.lastUpdateTime) { return []; }
46+ if (!this.lastUpdateTime) {
47+ return [];
48+ }
4049 return (() => {
4150 const result = [];
42− for (var doc of Array.from(app.disabledDocs.all())) { if ((doc.mtime > this.lastUpdateTime) && app.docs.findBy('slug_without_version', doc.slug_without_version)) {
51+ for (var doc of Array.from(app.disabledDocs.all())) {
52+ if (
53+ doc.mtime > this.lastUpdateTime &&
54+ app.docs.findBy("slug_without_version", doc.slug_without_version)
55+ ) {
4356 result.push(doc);
4457 }
4558 }
@@ -48,11 +61,16 @@ const Cls = (app.views.Updates = class Updates extends app.views.Notif { · 2 unmodified lines
4861 }
4962
5063 getLastUpdateTime() {
51− return app.settings.get('version');
64+ return app.settings.get("version");
5265 }
5366
5467 markAllAsRead() {
55− app.settings.set('version', app.config.env === 'production' ? app.config.version : Math.floor(Date.now() / 1000));
68+ app.settings.set(
69+ "version",
70+ app.config.env === "production"
71+ ? app.config.version
72+ : Math.floor(Date.now() / 1000),
73+ );
5674 }
5775 });
5876 Cls.initClass();
assets/javascripts/views/pages/base.js+39−15
@@ -9,31 +9,44 @@ · 8 unmodified lines
99 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1010 */
1111 app.views.BasePage = class BasePage extends app.View {
12− constructor(el, entry) { this.paintCode = this.paintCode.bind(this); this.el = el; this.entry = entry; super(...arguments); }
12+ constructor(el, entry) {
13+ this.paintCode = this.paintCode.bind(this);
14+ this.el = el;
15+ this.entry = entry;
16+ super(...arguments);
17+ }
1318
1419 deactivate() {
1520 if (super.deactivate(...arguments)) {
16− return this.highlightNodes = [];
21+ return (this.highlightNodes = []);
1722 }
1823 }
1924
2025 render(content, fromCache) {
21− if (fromCache == null) { fromCache = false; }
26+ if (fromCache == null) {
27+ fromCache = false;
28+ }
2229 this.highlightNodes = [];
2330 this.previousTiming = null;
24− if (!this.constructor.className) { this.addClass(`_${this.entry.doc.type}`); }
31+ if (!this.constructor.className) {
32+ this.addClass(`_${this.entry.doc.type}`);
33+ }
2534 this.html(content);
26− if (!fromCache) { this.highlightCode(); }
35+ if (!fromCache) {
36+ this.highlightCode();
37+ }
2738 this.activate();
28− if (this.afterRender) { this.delay(this.afterRender); }
39+ if (this.afterRender) {
40+ this.delay(this.afterRender);
41+ }
2942 if (this.highlightNodes.length > 0) {
3043 $.requestAnimationFrame(() => $.requestAnimationFrame(this.paintCode));
3144 }
3245 }
3346
3447 highlightCode() {
35− for (var el of Array.from(this.findAll('pre[data-language]'))) {
36− var language = el.getAttribute('data-language');
48+ for (var el of Array.from(this.findAll("pre[data-language]"))) {
49+ var language = el.getAttribute("data-language");
3750 el.classList.add(`language-${language}`);
3851 this.highlightNodes.push(el);
3952 }
@@ -41,23 +54,34 @@ app.views.BasePage = class BasePage extends app.View { · 1 unmodified line
4154
4255 paintCode(timing) {
4356 if (this.previousTiming) {
44− if (Math.round(1000 / (timing - this.previousTiming)) > 50) { // fps
45− this.nodesPerFrame = Math.round(Math.min(this.nodesPerFrame * 1.25, 50));
57+ if (Math.round(1000 / (timing - this.previousTiming)) > 50) {
58+ // fps
59+ this.nodesPerFrame = Math.round(
60+ Math.min(this.nodesPerFrame * 1.25, 50),
61+ );
4662 } else {
47− this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * .8, 10));
63+ this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * 0.8, 10));
4864 }
4965 } else {
5066 this.nodesPerFrame = 10;
5167 }
5268
53− for (var el of Array.from(this.highlightNodes.splice(0, this.nodesPerFrame))) {
69+ for (var el of Array.from(
70+ this.highlightNodes.splice(0, this.nodesPerFrame),
71+ )) {
5472 var clipEl;
55− if (clipEl = el.lastElementChild) { $.remove(clipEl); }
73+ if ((clipEl = el.lastElementChild)) {
74+ $.remove(clipEl);
75+ }
5676 Prism.highlightElement(el);
57− if (clipEl) { $.append(el, clipEl); }
77+ if (clipEl) {
78+ $.append(el, clipEl);
79+ }
5880 }
5981
60− if (this.highlightNodes.length > 0) { $.requestAnimationFrame(this.paintCode); }
82+ if (this.highlightNodes.length > 0) {
83+ $.requestAnimationFrame(this.paintCode);
84+ }
6185 this.previousTiming = timing;
6286 }
6387 };
assets/javascripts/views/pages/hidden.js+9−5
@@ -8,20 +8,24 @@ · 7 unmodified lines
88 */
99 const Cls = (app.views.HiddenPage = class HiddenPage extends app.View {
1010 static initClass() {
11− this.events =
12− {click: 'onClick'};
11+ this.events = { click: "onClick" };
1312 }
1413
15− constructor(el, entry) { this.onClick = this.onClick.bind(this); this.el = el; this.entry = entry; super(...arguments); }
14+ constructor(el, entry) {
15+ this.onClick = this.onClick.bind(this);
16+ this.el = el;
17+ this.entry = entry;
18+ super(...arguments);
19+ }
1620
1721 init() {
18− this.addSubview(this.notice = new app.views.Notice('disabledDoc'));
22+ this.addSubview((this.notice = new app.views.Notice("disabledDoc")));
1923 this.activate();
2024 }
2125
2226 onClick(event) {
2327 let link;
24− if (link = $.closestLink(event.target, this.el)) {
28+ if ((link = $.closestLink(event.target, this.el))) {
2529 $.stopEvent(event);
2630 $.popup(link);
2731 }
assets/javascripts/views/pages/jquery.js+30−17
@@ -10,46 +10,54 @@ · 9 unmodified lines
1010 */
1111 //= require views/pages/base
1212
13−const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage {
13+const Cls = (app.views.JqueryPage = class JqueryPage extends (
14+ app.views.BasePage
15+) {
1416 constructor(...args) {
1517 this.onIframeLoaded = this.onIframeLoaded.bind(this);
1618 super(...args);
1719 }
1820
1921 static initClass() {
20− this.demoClassName = '_jquery-demo';
22+ this.demoClassName = "_jquery-demo";
2123 }
2224
2325 afterRender() {
2426 // Prevent jQuery Mobile's demo iframes from scrolling the page
25− for (var iframe of Array.from(this.findAllByTag('iframe'))) {
26− iframe.style.display = 'none';
27− $.on(iframe, 'load', this.onIframeLoaded);
27+ for (var iframe of Array.from(this.findAllByTag("iframe"))) {
28+ iframe.style.display = "none";
29+ $.on(iframe, "load", this.onIframeLoaded);
2830 }
2931
3032 return this.runExamples();
3133 }
3234
3335 onIframeLoaded(event) {
34− event.target.style.display = '';
35− $.off(event.target, 'load', this.onIframeLoaded);
36+ event.target.style.display = "";
37+ $.off(event.target, "load", this.onIframeLoaded);
3638 }
3739
3840 runExamples() {
39− for (var el of Array.from(this.findAllByClass('entry-example'))) {
40− try { this.runExample(el); } catch (error) {}
41+ for (var el of Array.from(this.findAllByClass("entry-example"))) {
42+ try {
43+ this.runExample(el);
44+ } catch (error) {}
4145 }
4246 }
4347
4448 runExample(el) {
4549 let iframe;
46− const source = el.getElementsByClassName('syntaxhighlighter')[0];
47− if (!source || (source.innerHTML.indexOf('!doctype') === -1)) { return; }
50+ const source = el.getElementsByClassName("syntaxhighlighter")[0];
51+ if (!source || source.innerHTML.indexOf("!doctype") === -1) {
52+ return;
53+ }
4854
49− if (!(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0])) {
50− iframe = document.createElement('iframe');
55+ if (
56+ !(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0])
57+ ) {
58+ iframe = document.createElement("iframe");
5159 iframe.className = this.constructor.demoClassName;
52− iframe.width = '100%';
60+ iframe.width = "100%";
5361 iframe.height = 200;
5462 el.appendChild(iframe);
5563 }
@@ -60,8 +68,13 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage · 4 unmodified lines
6068 }
6169
6270 fixIframeSource(source) {
63− source = source.replace('"/resources/', '"https://api.jquery.com/resources/'); // attr(), keydown()
64− source = source.replace('</head>', `\
71+ source = source.replace(
72+ '"/resources/',
73+ '"https://api.jquery.com/resources/',
74+ ); // attr(), keydown()
75+ source = source.replace(
76+ "</head>",
77+ `\
6578 <style>
6679 html, body { border: 0; margin: 0; padding: 0; }
6780 body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; }
@@ -75,7 +88,7 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage · 7 unmodified lines
7588 });
7689 </script>
7790 </head>\
78−`
91+`,
7992 );
8093 return source.replace(/<script>/gi, '<script nonce="devdocs">');
8194 }
assets/javascripts/views/pages/rdoc.js+11−7
@@ -10,19 +10,23 @@ · 9 unmodified lines
1010
1111 const Cls = (app.views.RdocPage = class RdocPage extends app.views.BasePage {
1212 static initClass() {
13− this.events =
14− {click: 'onClick'};
13+ this.events = { click: "onClick" };
1514 }
1615
1716 onClick(event) {
18− if (!event.target.classList.contains('method-click-advice')) { return; }
17+ if (!event.target.classList.contains("method-click-advice")) {
18+ return;
19+ }
1920 $.stopEvent(event);
2021
21− const source = $('.method-source-code', event.target.closest('.method-detail'));
22− const isShown = source.style.display === 'block';
22+ const source = $(
23+ ".method-source-code",
24+ event.target.closest(".method-detail"),
25+ );
26+ const isShown = source.style.display === "block";
2327
24− source.style.display = isShown ? 'none' : 'block';
25− return event.target.textContent = isShown ? 'Show source' : 'Hide source';
28+ source.style.display = isShown ? "none" : "block";
29+ return (event.target.textContent = isShown ? "Show source" : "Hide source");
2630 }
2731 });
2832 Cls.initClass();
assets/javascripts/views/pages/sqlite.js+15−10
@@ -8,28 +8,33 @@ · 7 unmodified lines
88 */
99 //= require views/pages/base
1010
11−const Cls = (app.views.SqlitePage = class SqlitePage extends app.views.BasePage {
11+const Cls = (app.views.SqlitePage = class SqlitePage extends (
12+ app.views.BasePage
13+) {
1214 constructor(...args) {
1315 this.onClick = this.onClick.bind(this);
1416 super(...args);
1517 }
1618
1719 static initClass() {
18− this.events =
19− {click: 'onClick'};
20+ this.events = { click: "onClick" };
2021 }
2122
2223 onClick(event) {
2324 let el, id;
24− if (!(id = event.target.getAttribute('data-toggle'))) { return; }
25− if (!(el = this.find(`#${id}`))) { return; }
25+ if (!(id = event.target.getAttribute("data-toggle"))) {
26+ return;
27+ }
28+ if (!(el = this.find(`#${id}`))) {
29+ return;
30+ }
2631 $.stopEvent(event);
27− if (el.style.display === 'none') {
28− el.style.display = 'block';
29− event.target.textContent = 'hide';
32+ if (el.style.display === "none") {
33+ el.style.display = "block";
34+ event.target.textContent = "hide";
3035 } else {
31− el.style.display = 'none';
32− event.target.textContent = 'show';
36+ el.style.display = "none";
37+ event.target.textContent = "show";
3338 }
3439 }
3540 });
assets/javascripts/views/pages/support_tables.js+11−6
@@ -7,19 +7,24 @@ · 6 unmodified lines
77 */
88 //= require views/pages/base
99
10−const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends app.views.BasePage {
10+const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends (
11+ app.views.BasePage
12+) {
1113 static initClass() {
12− this.events =
13− {click: 'onClick'};
14+ this.events = { click: "onClick" };
1415 }
1516
1617 onClick(event) {
17− if (!event.target.classList.contains('show-all')) { return; }
18+ if (!event.target.classList.contains("show-all")) {
19+ return;
20+ }
1821 $.stopEvent(event);
1922
2023 let el = event.target;
21− while (el.tagName !== 'TABLE') { el = el.parentNode; }
22− el.classList.add('show-all');
24+ while (el.tagName !== "TABLE") {
25+ el = el.parentNode;
26+ }
27+ el.classList.add("show-all");
2328 }
2429 });
2530 Cls.initClass();
assets/javascripts/views/search/search.js+113−62
@@ -9,7 +9,7 @@ · 8 unmodified lines
99 * DS207: Consider shorter variations of null checks
1010 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1111 */
12−(function() {
12+(function () {
1313 let SEARCH_PARAM = undefined;
1414 let HASH_RGX = undefined;
1515 const Cls = (app.views.Search = class Search extends app.View {
@@ -33,86 +33,106 @@ · 17 unmodified lines
3333
3434 static initClass() {
3535 SEARCH_PARAM = app.config.search_param;
36−
37− this.el = '._search';
38− this.activeClass = '_search-active';
39−
36+
37+ this.el = "._search";
38+ this.activeClass = "_search-active";
39+
4040 this.elements = {
41− input: '._search-input',
42− resetLink: '._search-clear'
41+ input: "._search-input",
42+ resetLink: "._search-clear",
4343 };
44−
44+
4545 this.events = {
46− input: 'onInput',
47− click: 'onClick',
48− submit: 'onSubmit'
46+ input: "onInput",
47+ click: "onClick",
48+ submit: "onSubmit",
4949 };
50−
50+
5151 this.shortcuts = {
52− typing: 'focus',
53− altG: 'google',
54− altS: 'stackoverflow',
55− altD: 'duckduckgo'
52+ typing: "focus",
53+ altG: "google",
54+ altS: "stackoverflow",
55+ altD: "duckduckgo",
5656 };
57−
58− this.routes =
59− {after: 'afterRoute'};
60−
57+
58+ this.routes = { after: "afterRoute" };
59+
6160 HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.*)`);
6261 }
6362
6463 init() {
65− this.addSubview(this.scope = new app.views.SearchScope(this.el));
64+ this.addSubview((this.scope = new app.views.SearchScope(this.el)));
6665
67− this.searcher = new app.Searcher;
68− this.searcher
69− .on('results', this.onResults)
70− .on('end', this.onEnd);
66+ this.searcher = new app.Searcher();
67+ this.searcher.on("results", this.onResults).on("end", this.onEnd);
7168
72− this.scope
73− .on('change', this.onScopeChange);
69+ this.scope.on("change", this.onScopeChange);
7470
75− app.on('ready', this.onReady);
76− $.on(window, 'hashchange', this.searchUrl);
77− $.on(window, 'focus', this.onWindowFocus);
71+ app.on("ready", this.onReady);
72+ $.on(window, "hashchange", this.searchUrl);
73+ $.on(window, "focus", this.onWindowFocus);
7874 }
7975
8076 focus() {
81− if (document.activeElement === this.input) { return; }
82− if (app.settings.get('noAutofocus')) { return; }
83− this.input.focus();
77+ if (document.activeElement === this.input) {
78+ return;
79+ }
80+ if (app.settings.get("noAutofocus")) {
81+ return;
82+ }
83+ this.input.focus();
8484 }
8585
8686 autoFocus() {
87− if (app.isMobile() || $.isAndroid() || $.isIOS()) { return; }
88− if ((document.activeElement != null ? document.activeElement.tagName : undefined) === 'INPUT') { return; }
89− if (app.settings.get('noAutofocus')) { return; }
87+ if (app.isMobile() || $.isAndroid() || $.isIOS()) {
88+ return;
89+ }
90+ if (
91+ (document.activeElement != null
92+ ? document.activeElement.tagName
93+ : undefined) === "INPUT"
94+ ) {
95+ return;
96+ }
97+ if (app.settings.get("noAutofocus")) {
98+ return;
99+ }
90100 this.input.focus();
91101 }
92102
93103 onWindowFocus(event) {
94− if (event.target === window) { return this.autoFocus(); }
104+ if (event.target === window) {
105+ return this.autoFocus();
106+ }
95107 }
96108
97109 getScopeDoc() {
98− if (this.scope.isActive()) { return this.scope.getScope(); }
110+ if (this.scope.isActive()) {
111+ return this.scope.getScope();
112+ }
99113 }
100114
101115 reset(force) {
102− if (force || !this.input.value) { this.scope.reset(); }
116+ if (force || !this.input.value) {
117+ this.scope.reset();
118+ }
103119 this.el.reset();
104120 this.onInput();
105121 this.autoFocus();
106122 }
107123
108124 onReady() {
109− this.value = '';
125+ this.value = "";
110126 this.delay(this.onInput);
111127 }
112128
113129 onInput() {
114− if ((this.value == null) || // ignore events pre-"ready"
115− (this.value === this.input.value)) { return; }
130+ if (
131+ this.value == null || // ignore events pre-"ready"
132+ this.value === this.input.value
133+ ) {
134+ return;
135+ }
116136 this.value = this.input.value;
117137
118138 if (this.value.length) {
@@ -123,25 +143,33 @@ · 4 unmodified lines
123143 }
124144
125145 search(url) {
126− if (url == null) { url = false; }
146+ if (url == null) {
147+ url = false;
148+ }
127149 this.addClass(this.constructor.activeClass);
128− this.trigger('searching');
150+ this.trigger("searching");
129151
130152 this.hasResults = null;
131− this.flags = {urlSearch: url, initialResults: true};
132− this.searcher.find(this.scope.getScope().entries.all(), 'text', this.value);
153+ this.flags = { urlSearch: url, initialResults: true };
154+ this.searcher.find(
155+ this.scope.getScope().entries.all(),
156+ "text",
157+ this.value,
158+ );
133159 }
134160
135161 searchUrl() {
136162 let value;
137− if (location.pathname === '/') {
163+ if (location.pathname === "/") {
138164 this.scope.searchUrl();
139165 } else if (!app.router.isIndex()) {
140166 return;
141167 }
142168
143− if (!(value = this.extractHashValue())) { return; }
144− this.input.value = (this.value = value);
169+ if (!(value = this.extractHashValue())) {
170+ return;
171+ }
172+ this.input.value = this.value = value;
145173 this.input.setSelectionRange(value.length, value.length);
146174 this.search(true);
147175 return true;
@@ -149,13 +177,15 @@ · 1 unmodified line
149177
150178 clear() {
151179 this.removeClass(this.constructor.activeClass);
152− this.trigger('clear');
180+ this.trigger("clear");
153181 }
154182
155183 externalSearch(url) {
156184 let value;
157− if (value = this.value) {
158− if (this.scope.name()) { value = `${this.scope.name()} ${value}`; }
185+ if ((value = this.value)) {
186+ if (this.scope.name()) {
187+ value = `${this.scope.name()} ${value}`;
188+ }
159189 $.popup(`${url}${encodeURIComponent(value)}`);
160190 this.reset();
161191 }
@@ -174,13 +204,17 @@ · 12 unmodified lines
174204 }
175205
176206 onResults(results) {
177− if (results.length) { this.hasResults = true; }
178− this.trigger('results', results, this.flags);
207+ if (results.length) {
208+ this.hasResults = true;
209+ }
210+ this.trigger("results", results, this.flags);
179211 this.flags.initialResults = false;
180212 }
181213
182214 onEnd() {
183− if (!this.hasResults) { this.trigger('noresults'); }
215+ if (!this.hasResults) {
216+ this.trigger("noresults");
217+ }
184218 }
185219
186220 onClick(event) {
@@ -195,14 +229,24 @@ · 8 unmodified lines
195229 }
196230
197231 onScopeChange() {
198− this.value = '';
232+ this.value = "";
199233 this.onInput();
200234 }
201235
202236 afterRoute(name, context) {
203− if ((app.shortcuts.eventInProgress != null ? app.shortcuts.eventInProgress.name : undefined) === 'escape') { return; }
204− if (!context.init && app.router.isIndex()) { this.reset(true); }
205− if (context.hash) { this.delay(this.searchUrl); }
237+ if (
238+ (app.shortcuts.eventInProgress != null
239+ ? app.shortcuts.eventInProgress.name
240+ : undefined) === "escape"
241+ ) {
242+ return;
243+ }
244+ if (!context.init && app.router.isIndex()) {
245+ this.reset(true);
246+ }
247+ if (context.hash) {
248+ this.delay(this.searchUrl);
249+ }
206250 $.requestAnimationFrame(this.autoFocus);
207251 }
208252
@@ -215,7 +259,12 @@ · 6 unmodified lines
215259 }
216260
217261 getHashValue() {
218− try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {}
262+ try {
263+ return __guard__(
264+ HASH_RGX.exec($.urlDecode(location.hash)),
265+ (x) => x[1],
266+ );
267+ } catch (error) {}
219268 }
220269 });
221270 Cls.initClass();
@@ -223,5 +272,7 @@ · 1 unmodified line
223272 })();
224273
225274 function __guard__(value, transform) {
226− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
227−}
275+ return typeof value !== "undefined" && value !== null
276+ ? transform(value)
277+ : undefined;
278+}
assets/javascripts/views/search/search_scope.js+91−53
@@ -9,41 +9,49 @@ · 8 unmodified lines
99 * DS207: Consider shorter variations of null checks
1010 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
1111 */
12−(function() {
12+(function () {
1313 let SEARCH_PARAM = undefined;
1414 let HASH_RGX = undefined;
1515 const Cls = (app.views.SearchScope = class SearchScope extends app.View {
1616 static initClass() {
1717 SEARCH_PARAM = app.config.search_param;
18−
18+
1919 this.elements = {
20− input: '._search-input',
21− tag: '._search-tag'
20+ input: "._search-input",
21+ tag: "._search-tag",
2222 };
23−
23+
2424 this.events = {
25− click: 'onClick',
26− keydown: 'onKeydown',
27− textInput: 'onTextInput'
25+ click: "onClick",
26+ keydown: "onKeydown",
27+ textInput: "onTextInput",
2828 };
29−
30− this.routes =
31− {after: 'afterRoute'};
32−
29+
30+ this.routes = { after: "afterRoute" };
31+
3332 HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.+?) .`);
3433 }
3534
36− constructor(el) { this.onResults = this.onResults.bind(this); this.reset = this.reset.bind(this); this.doScopeSearch = this.doScopeSearch.bind(this); this.onClick = this.onClick.bind(this); this.onKeydown = this.onKeydown.bind(this); this.onTextInput = this.onTextInput.bind(this); this.afterRoute = this.afterRoute.bind(this); this.el = el; super(...arguments); }
35+ constructor(el) {
36+ this.onResults = this.onResults.bind(this);
37+ this.reset = this.reset.bind(this);
38+ this.doScopeSearch = this.doScopeSearch.bind(this);
39+ this.onClick = this.onClick.bind(this);
40+ this.onKeydown = this.onKeydown.bind(this);
41+ this.onTextInput = this.onTextInput.bind(this);
42+ this.afterRoute = this.afterRoute.bind(this);
43+ this.el = el;
44+ super(...arguments);
45+ }
3746
3847 init() {
39− this.placeholder = this.input.getAttribute('placeholder');
48+ this.placeholder = this.input.getAttribute("placeholder");
4049
4150 this.searcher = new app.SynchronousSearcher({
4251 fuzzy_min_length: 2,
43− max_results: 1
52+ max_results: 1,
4453 });
45− this.searcher.on('results', this.onResults);
46−
54+ this.searcher.on("results", this.onResults);
4755 }
4856
4957 getScope() {
@@ -55,26 +63,34 @@ · 5 unmodified lines
5563 }
5664
5765 name() {
58− return (this.doc != null ? this.doc.name : undefined);
66+ return this.doc != null ? this.doc.name : undefined;
5967 }
6068
6169 search(value, searchDisabled) {
62− if (searchDisabled == null) { searchDisabled = false; }
63− if (this.doc) { return; }
64− this.searcher.find(app.docs.all(), 'text', value);
65− if (!this.doc && searchDisabled) { this.searcher.find(app.disabledDocs.all(), 'text', value); }
70+ if (searchDisabled == null) {
71+ searchDisabled = false;
72+ }
73+ if (this.doc) {
74+ return;
75+ }
76+ this.searcher.find(app.docs.all(), "text", value);
77+ if (!this.doc && searchDisabled) {
78+ this.searcher.find(app.disabledDocs.all(), "text", value);
79+ }
6680 }
6781
6882 searchUrl() {
6983 let value;
70− if (value = this.extractHashValue()) {
84+ if ((value = this.extractHashValue())) {
7185 this.search(value, true);
7286 }
7387 }
7488
7589 onResults(results) {
7690 let doc;
77− if (!(doc = results[0])) { return; }
91+ if (!(doc = results[0])) {
92+ return;
93+ }
7894 if (app.docs.contains(doc)) {
7995 this.selectDoc(doc);
8096 } else {
@@ -84,45 +100,49 @@ · 3 unmodified lines
84100
85101 selectDoc(doc) {
86102 const previousDoc = this.doc;
87− if (doc === previousDoc) { return; }
103+ if (doc === previousDoc) {
104+ return;
105+ }
88106 this.doc = doc;
89107
90108 this.tag.textContent = doc.fullName;
91− this.tag.style.display = 'block';
109+ this.tag.style.display = "block";
92110
93− this.input.removeAttribute('placeholder');
111+ this.input.removeAttribute("placeholder");
94112 this.input.value = this.input.value.slice(this.input.selectionStart);
95− this.input.style.paddingLeft = this.tag.offsetWidth + 10 + 'px';
113+ this.input.style.paddingLeft = this.tag.offsetWidth + 10 + "px";
96114
97− $.trigger(this.input, 'input');
98− this.trigger('change', this.doc, previousDoc);
115+ $.trigger(this.input, "input");
116+ this.trigger("change", this.doc, previousDoc);
99117 }
100118
101119 redirectToDoc(doc) {
102− const {
103− hash
104− } = location;
105− app.router.replaceHash('');
120+ const { hash } = location;
121+ app.router.replaceHash("");
106122 location.assign(doc.fullPath() + hash);
107123 }
108124
109125 reset() {
110− if (!this.doc) { return; }
126+ if (!this.doc) {
127+ return;
128+ }
111129 const previousDoc = this.doc;
112130 this.doc = null;
113131
114− this.tag.textContent = '';
115− this.tag.style.display = 'none';
132+ this.tag.textContent = "";
133+ this.tag.style.display = "none";
116134
117− this.input.setAttribute('placeholder', this.placeholder);
118− this.input.style.paddingLeft = '';
135+ this.input.setAttribute("placeholder", this.placeholder);
136+ this.input.style.paddingLeft = "";
119137
120− this.trigger('change', null, previousDoc);
138+ this.trigger("change", null, previousDoc);
121139 }
122140
123141 doScopeSearch(event) {
124142 this.search(this.input.value.slice(0, this.input.selectionStart));
125− if (this.doc) { $.stopEvent(event); }
143+ if (this.doc) {
144+ $.stopEvent(event);
145+ }
126146 }
127147
128148 onClick(event) {
@@ -133,38 +153,54 @@ · 4 unmodified lines
133153 }
134154
135155 onKeydown(event) {
136− if (event.which === 8) { // backspace
137− if (this.doc && (this.input.selectionEnd === 0)) {
156+ if (event.which === 8) {
157+ // backspace
158+ if (this.doc && this.input.selectionEnd === 0) {
138159 this.reset();
139160 $.stopEvent(event);
140161 }
141162 } else if (!this.doc && this.input.value && !$.isChromeForAndroid()) {
142− if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) { return; }
143− if ((event.which === 9) || // tab
144− ((event.which === 32) && app.isMobile())) { // space
163+ if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) {
164+ return;
165+ }
166+ if (
167+ event.which === 9 || // tab
168+ (event.which === 32 && app.isMobile())
169+ ) {
170+ // space
145171 this.doScopeSearch(event);
146172 }
147173 }
148174 }
149175
150176 onTextInput(event) {
151− if (!$.isChromeForAndroid()) { return; }
152− if (!this.doc && this.input.value && (event.data === ' ')) {
177+ if (!$.isChromeForAndroid()) {
178+ return;
179+ }
180+ if (!this.doc && this.input.value && event.data === " ") {
153181 this.doScopeSearch(event);
154182 }
155183 }
156184
157185 extractHashValue() {
158186 let value;
159− if (value = this.getHashValue()) {
160− const newHash = $.urlDecode(location.hash).replace(`#${SEARCH_PARAM}=${value} `, `#${SEARCH_PARAM}=`);
187+ if ((value = this.getHashValue())) {
188+ const newHash = $.urlDecode(location.hash).replace(
189+ `#${SEARCH_PARAM}=${value} `,
190+ `#${SEARCH_PARAM}=`,
191+ );
161192 app.router.replaceHash(newHash);
162193 return value;
163194 }
164195 }
165196
166197 getHashValue() {
167− try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {}
198+ try {
199+ return __guard__(
200+ HASH_RGX.exec($.urlDecode(location.hash)),
201+ (x) => x[1],
202+ );
203+ } catch (error) {}
168204 }
169205
170206 afterRoute(name, context) {
@@ -178,5 +214,7 @@ · 7 unmodified lines
178214 })();
179215
180216 function __guard__(value, transform) {
181− return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
182−}
217+ return typeof value !== "undefined" && value !== null
218+ ? transform(value)
219+ : undefined;
220+}
assets/javascripts/views/sidebar/doc_list.js+90−55
@@ -20,65 +20,75 @@ const Cls = (app.views.DocList = class DocList extends app.View { · 19 unmodified lines
2020 }
2121
2222 static initClass() {
23− this.className = '_list';
24− this.attributes =
25− {role: 'navigation'};
26−
23+ this.className = "_list";
24+ this.attributes = { role: "navigation" };
25+
2726 this.events = {
28− open: 'onOpen',
29− close: 'onClose',
30− click: 'onClick'
27+ open: "onOpen",
28+ close: "onClose",
29+ click: "onClick",
3130 };
32−
33− this.routes =
34− {after: 'afterRoute'};
35−
31+
32+ this.routes = { after: "afterRoute" };
33+
3634 this.elements = {
37− disabledTitle: '._list-title',
38− disabledList: '._disabled-list'
35+ disabledTitle: "._list-title",
36+ disabledList: "._disabled-list",
3937 };
4038 }
4139
4240 init() {
4341 this.lists = {};
4442
45− this.addSubview(this.listFocus = new app.views.ListFocus(this.el));
46− this.addSubview(this.listFold = new app.views.ListFold(this.el));
47− this.addSubview(this.listSelect = new app.views.ListSelect(this.el));
43+ this.addSubview((this.listFocus = new app.views.ListFocus(this.el)));
44+ this.addSubview((this.listFold = new app.views.ListFold(this.el)));
45+ this.addSubview((this.listSelect = new app.views.ListSelect(this.el)));
4846
49− app.on('ready', this.render);
47+ app.on("ready", this.render);
5048 }
5149
5250 activate() {
5351 if (super.activate(...arguments)) {
54− for (var slug in this.lists) { var list = this.lists[slug]; list.activate(); }
52+ for (var slug in this.lists) {
53+ var list = this.lists[slug];
54+ list.activate();
55+ }
5556 this.listSelect.selectCurrent();
5657 }
5758 }
5859
5960 deactivate() {
6061 if (super.deactivate(...arguments)) {
61− for (var slug in this.lists) { var list = this.lists[slug]; list.deactivate(); }
62+ for (var slug in this.lists) {
63+ var list = this.lists[slug];
64+ list.deactivate();
65+ }
6266 }
6367 }
6468
6569 render() {
66− let html = '';
70+ let html = "";
6771 for (var doc of Array.from(app.docs.all())) {
68− html += this.tmpl('sidebarDoc', doc, {fullName: app.docs.countAllBy('name', doc.name) > 1});
72+ html += this.tmpl("sidebarDoc", doc, {
73+ fullName: app.docs.countAllBy("name", doc.name) > 1,
74+ });
6975 }
7076 this.html(html);
71− if (!app.isSingleDoc() && (app.disabledDocs.size() !== 0)) { this.renderDisabled(); }
77+ if (!app.isSingleDoc() && app.disabledDocs.size() !== 0) {
78+ this.renderDisabled();
79+ }
7280 }
7381
7482 renderDisabled() {
75− this.append(this.tmpl('sidebarDisabled', {count: app.disabledDocs.size()}));
83+ this.append(
84+ this.tmpl("sidebarDisabled", { count: app.disabledDocs.size() }),
85+ );
7686 this.refreshElements();
7787 this.renderDisabledList();
7888 }
7989
8090 renderDisabledList() {
81− if (app.settings.get('hideDisabled')) {
91+ if (app.settings.get("hideDisabled")) {
8292 this.removeDisabledList();
8393 } else {
8494 this.appendDisabledList();
@@ -87,60 +97,67 @@ const Cls = (app.views.DocList = class DocList extends app.View { · 2 unmodified lines
8797
8898 appendDisabledList() {
8999 let doc;
90− let html = '';
100+ let html = "";
91101 const docs = [].concat(...Array.from(app.disabledDocs.all() || []));
92102
93103 while ((doc = docs.shift())) {
94104 if (doc.version != null) {
95− var versions = '';
105+ var versions = "";
96106 while (true) {
97− versions += this.tmpl('sidebarDoc', doc, {disabled: true});
98− if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) { break; }
107+ versions += this.tmpl("sidebarDoc", doc, { disabled: true });
108+ if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) {
109+ break;
110+ }
99111 doc = docs.shift();
100112 }
101− html += this.tmpl('sidebarDisabledVersionedDoc', doc, versions);
113+ html += this.tmpl("sidebarDisabledVersionedDoc", doc, versions);
102114 } else {
103− html += this.tmpl('sidebarDoc', doc, {disabled: true});
115+ html += this.tmpl("sidebarDoc", doc, { disabled: true });
104116 }
105117 }
106118
107− this.append(this.tmpl('sidebarDisabledList', html));
108− this.disabledTitle.classList.add('open-title');
119+ this.append(this.tmpl("sidebarDisabledList", html));
120+ this.disabledTitle.classList.add("open-title");
109121 this.refreshElements();
110122 }
111123
112124 removeDisabledList() {
113− if (this.disabledList) { $.remove(this.disabledList); }
114− this.disabledTitle.classList.remove('open-title');
125+ if (this.disabledList) {
126+ $.remove(this.disabledList);
127+ }
128+ this.disabledTitle.classList.remove("open-title");
115129 this.refreshElements();
116130 }
117131
118132 reset(options) {
119− if (options == null) { options = {}; }
133+ if (options == null) {
134+ options = {};
135+ }
120136 this.listSelect.deselect();
121137 if (this.listFocus != null) {
122138 this.listFocus.blur();
123139 }
124140 this.listFold.reset();
125− if (options.revealCurrent || app.isSingleDoc()) { this.revealCurrent(); }
141+ if (options.revealCurrent || app.isSingleDoc()) {
142+ this.revealCurrent();
143+ }
126144 }

125 more lines aren’t shown here.

assets/javascripts/views/sidebar/doc_picker.js+62−32

This diff is large: its 160 lines aren’t shown here.

assets/javascripts/views/sidebar/entry_list.js+10−5

This diff is large: its 32 lines aren’t shown here.

assets/javascripts/views/sidebar/results.js+49−28

This diff is large: its 126 lines aren’t shown here.

assets/javascripts/views/sidebar/sidebar.js+79−43

This diff is large: its 216 lines aren’t shown here.

assets/javascripts/views/sidebar/sidebar_hover.js+35−26

This diff is large: its 126 lines aren’t shown here.

assets/javascripts/views/sidebar/type_list.js+36−15

This diff is large: its 100 lines aren’t shown here.

assets/javascripts/views/view.js+64−23

This diff is large: its 162 lines aren’t shown here.