diff --git a/assets/javascripts/app/app.js b/assets/javascripts/app/app.js index de716aa8..997fea38 100644 --- a/assets/javascripts/app/app.js +++ b/assets/javascripts/app/app.js @@ -14,33 +14,41 @@ this.app = { _$$: $$, _page: page, collections: {}, - models: {}, - templates: {}, - views: {}, + models: {}, + templates: {}, + views: {}, init() { - try { this.initErrorTracking(); } catch (error) {} - if (!this.browserCheck()) { return; } + try { + this.initErrorTracking(); + } catch (error) {} + if (!this.browserCheck()) { + return; + } - this.el = $('._app'); - this.localStorage = new LocalStorageStore; - if (app.ServiceWorker.isEnabled()) { this.serviceWorker = new app.ServiceWorker; } - this.settings = new app.Settings; + this.el = $("._app"); + this.localStorage = new LocalStorageStore(); + if (app.ServiceWorker.isEnabled()) { + this.serviceWorker = new app.ServiceWorker(); + } + this.settings = new app.Settings(); this.db = new app.DB(); this.settings.initLayout(); - this.docs = new app.collections.Docs; - this.disabledDocs = new app.collections.Docs; - this.entries = new app.collections.Entries; + this.docs = new app.collections.Docs(); + this.disabledDocs = new app.collections.Docs(); + this.entries = new app.collections.Entries(); - this.router = new app.Router; - this.shortcuts = new app.Shortcuts; - this.document = new app.views.Document; - if (this.isMobile()) { this.mobile = new app.views.Mobile; } + this.router = new app.Router(); + this.shortcuts = new app.Shortcuts(); + this.document = new app.views.Document(); + if (this.isMobile()) { + this.mobile = new app.views.Mobile(); + } - if (document.body.hasAttribute('data-doc')) { - this.DOC = JSON.parse(document.body.getAttribute('data-doc')); + if (document.body.hasAttribute("data-doc")) { + this.DOC = JSON.parse(document.body.getAttribute("data-doc")); this.bootOne(); } else if (this.DOCS) { this.bootAll(); @@ -50,7 +58,9 @@ this.app = { }, browserCheck() { - if (this.isSupportedBrowser()) { return true; } + if (this.isSupportedBrowser()) { + return true; + } document.body.innerHTML = app.templates.unsupportedBrowser; this.hideLoadingScreen(); return false; @@ -61,7 +71,7 @@ this.app = { // from a domain other than our own, because things are likely to break. // (e.g. cross-domain requests) if (this.isInvalidLocation()) { - new app.views.Notif('InvalidLocation'); + new app.views.Notif("InvalidLocation"); } else { if (this.config.sentry_dsn) { Raven.config(this.config.sentry_dsn, { @@ -70,9 +80,12 @@ this.app = { includePaths: [/devdocs/], ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/], tags: { - mode: this.isSingleDoc() ? 'single' : 'full', + mode: this.isSingleDoc() ? "single" : "full", iframe: (window.top !== window).toString(), - electron: (!!__guard__(window.process != null ? window.process.versions : undefined, x => x.electron)).toString() + electron: (!!__guard__( + window.process != null ? window.process.versions : undefined, + (x) => x.electron, + )).toString(), }, shouldSendCallback: () => { try { @@ -89,12 +102,16 @@ this.app = { dataCallback(data) { try { $.extend(data.user || (data.user = {}), app.settings.dump()); - if (data.user.docs) { data.user.docs = data.user.docs.split('/'); } - if (app.lastIDBTransaction) { data.user.lastIDBTransaction = app.lastIDBTransaction; } + if (data.user.docs) { + data.user.docs = data.user.docs.split("/"); + } + if (app.lastIDBTransaction) { + data.user.lastIDBTransaction = app.lastIDBTransaction; + } data.tags.scriptCount = document.scripts.length; } catch (error) {} return data; - } + }, }).install(); } this.previousErrorHandler = onerror; @@ -106,8 +123,10 @@ this.app = { bootOne() { this.doc = new app.models.Doc(this.DOC); this.docs.reset([this.doc]); - this.doc.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true}); - new app.views.Notice('singleDoc', this.doc); + this.doc.load(this.start.bind(this), this.onBootError.bind(this), { + readCache: true, + }); + new app.views.Notice("singleDoc", this.doc); delete this.DOC; }, @@ -117,40 +136,61 @@ this.app = { (docs.indexOf(doc.slug) >= 0 ? this.docs : this.disabledDocs).add(doc); } this.migrateDocs(); - this.docs.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true, writeCache: true}); + this.docs.load(this.start.bind(this), this.onBootError.bind(this), { + readCache: true, + writeCache: true, + }); delete this.DOCS; }, start() { let doc; - for (doc of Array.from(this.docs.all())) { this.entries.add(doc.toEntry()); } - for (doc of Array.from(this.disabledDocs.all())) { this.entries.add(doc.toEntry()); } - for (doc of Array.from(this.docs.all())) { this.initDoc(doc); } - this.trigger('ready'); + for (doc of Array.from(this.docs.all())) { + this.entries.add(doc.toEntry()); + } + for (doc of Array.from(this.disabledDocs.all())) { + this.entries.add(doc.toEntry()); + } + for (doc of Array.from(this.docs.all())) { + this.initDoc(doc); + } + this.trigger("ready"); this.router.start(); this.hideLoadingScreen(); setTimeout(() => { - if (!this.doc) { this.welcomeBack(); } - return this.removeEvent('ready bootError'); - } - , 50); + if (!this.doc) { + this.welcomeBack(); + } + return this.removeEvent("ready bootError"); + }, 50); }, initDoc(doc) { - for (var type of Array.from(doc.types.all())) { doc.entries.add(type.toEntry()); } + for (var type of Array.from(doc.types.all())) { + doc.entries.add(type.toEntry()); + } this.entries.add(doc.entries.all()); }, migrateDocs() { let needsSaving; for (var slug of Array.from(this.settings.getDocs())) { - if (!this.docs.findBy('slug', slug)) {var doc; - + if (!this.docs.findBy("slug", slug)) { + var doc; + needsSaving = true; - if (slug === 'webpack~2') { doc = this.disabledDocs.findBy('slug', 'webpack'); } - if (slug === 'angular~4_typescript') { doc = this.disabledDocs.findBy('slug', 'angular'); } - if (slug === 'angular~2_typescript') { doc = this.disabledDocs.findBy('slug', 'angular~2'); } - if (!doc) { doc = this.disabledDocs.findBy('slug_without_version', slug); } + if (slug === "webpack~2") { + doc = this.disabledDocs.findBy("slug", "webpack"); + } + if (slug === "angular~4_typescript") { + doc = this.disabledDocs.findBy("slug", "angular"); + } + if (slug === "angular~2_typescript") { + doc = this.disabledDocs.findBy("slug", "angular~2"); + } + if (!doc) { + doc = this.disabledDocs.findBy("slug_without_version", slug); + } if (doc) { this.disabledDocs.remove(doc); this.docs.add(doc); @@ -158,56 +198,70 @@ this.app = { } } - if (needsSaving) { this.saveDocs(); } + if (needsSaving) { + this.saveDocs(); + } }, enableDoc(doc, _onSuccess, onError) { - if (this.docs.contains(doc)) { return; } + if (this.docs.contains(doc)) { + return; + } const onSuccess = () => { - if (this.docs.contains(doc)) { return; } + if (this.docs.contains(doc)) { + return; + } this.disabledDocs.remove(doc); this.docs.add(doc); this.docs.sort(); this.initDoc(doc); this.saveDocs(); - if (app.settings.get('autoInstall')) { + if (app.settings.get("autoInstall")) { doc.install(_onSuccess, onError); } else { _onSuccess(); } }; - doc.load(onSuccess, onError, {writeCache: true}); + doc.load(onSuccess, onError, { writeCache: true }); }, saveDocs() { this.settings.setDocs(Array.from(this.docs.all()).map((doc) => doc.slug)); this.db.migrate(); - return (this.serviceWorker != null ? this.serviceWorker.updateInBackground() : undefined); + return this.serviceWorker != null + ? this.serviceWorker.updateInBackground() + : undefined; }, welcomeBack() { - let visitCount = this.settings.get('count'); - this.settings.set('count', ++visitCount); - if (visitCount === 5) { new app.views.Notif('Share', {autoHide: null}); } + let visitCount = this.settings.get("count"); + this.settings.set("count", ++visitCount); + if (visitCount === 5) { + new app.views.Notif("Share", { autoHide: null }); + } new app.views.News(); new app.views.Updates(); - return this.updateChecker = new app.UpdateChecker(); + return (this.updateChecker = new app.UpdateChecker()); }, reboot() { - if ((location.pathname !== '/') && (location.pathname !== '/settings')) { + if (location.pathname !== "/" && location.pathname !== "/settings") { window.location = `/#${location.pathname}`; } else { - window.location = '/'; + window.location = "/"; } }, reload() { this.docs.clearCache(); this.disabledDocs.clearCache(); - if (this.serviceWorker) { this.serviceWorker.reload(); } else { this.reboot(); } + if (this.serviceWorker) { + this.serviceWorker.reload(); + } else { + this.reboot(); + } }, reset() { @@ -219,11 +273,13 @@ this.app = { if (this.serviceWorker != null) { this.serviceWorker.update(); } - window.location = '/'; + window.location = "/"; }, showTip(tip) { - if (this.isSingleDoc()) { return; } + if (this.isSingleDoc()) { + return; + } const tips = this.settings.getTips(); if (tips.indexOf(tip) === -1) { tips.push(tip); @@ -233,44 +289,61 @@ this.app = { }, hideLoadingScreen() { - if ($.overlayScrollbarsEnabled()) { document.body.classList.add('_overlay-scrollbars'); } - document.documentElement.classList.remove('_booting'); + if ($.overlayScrollbarsEnabled()) { + document.body.classList.add("_overlay-scrollbars"); + } + document.documentElement.classList.remove("_booting"); }, indexHost() { // Can't load the index files from the host/CDN when service worker is // enabled because it doesn't support caching URLs that use CORS. - return this.config[this.serviceWorker && this.settings.hasDocs() ? 'index_path' : 'docs_origin']; + return this.config[ + this.serviceWorker && this.settings.hasDocs() + ? "index_path" + : "docs_origin" + ]; }, onBootError(...args) { - this.trigger('bootError'); + this.trigger("bootError"); this.hideLoadingScreen(); }, onQuotaExceeded() { - if (this.quotaExceeded) { return; } + if (this.quotaExceeded) { + return; + } this.quotaExceeded = true; - new app.views.Notif('QuotaExceeded', {autoHide: null}); + new app.views.Notif("QuotaExceeded", { autoHide: null }); }, onCookieBlocked(key, value, actual) { - if (this.cookieBlocked) { return; } + if (this.cookieBlocked) { + return; + } this.cookieBlocked = true; - new app.views.Notif('CookieBlocked', {autoHide: null}); - Raven.captureMessage(`CookieBlocked/${key}`, {level: 'warning', extra: {value, actual}}); + new app.views.Notif("CookieBlocked", { autoHide: null }); + Raven.captureMessage(`CookieBlocked/${key}`, { + level: "warning", + extra: { value, actual }, + }); }, onWindowError(...args) { - if (this.cookieBlocked) { return; } + if (this.cookieBlocked) { + return; + } if (this.isInjectionError(...Array.from(args || []))) { this.onInjectionError(); } else if (this.isAppError(...Array.from(args || []))) { - if (typeof this.previousErrorHandler === 'function') { + if (typeof this.previousErrorHandler === "function") { this.previousErrorHandler(...Array.from(args || [])); } this.hideLoadingScreen(); - if (!this.errorNotif) { this.errorNotif = new app.views.Notif('Error'); } + if (!this.errorNotif) { + this.errorNotif = new app.views.Notif("Error"); + } this.errorNotif.show(); } }, @@ -280,75 +353,103 @@ this.app = { this.injectionError = true; alert(`\ JavaScript code has been injected in the page which prevents DevDocs from running correctly. -Please check your browser extensions/addons. ` - ); - Raven.captureMessage('injection error', {level: 'info'}); +Please check your browser extensions/addons. `); + Raven.captureMessage("injection error", { level: "info" }); } }, isInjectionError() { // Some browser extensions expect the entire web to use jQuery. // I gave up trying to fight back. - return (window.$ !== app._$) || (window.$$ !== app._$$) || (window.page !== app._page) || (typeof $.empty !== 'function') || (typeof page.show !== 'function'); + return ( + window.$ !== app._$ || + window.$$ !== app._$$ || + window.page !== app._page || + typeof $.empty !== "function" || + typeof page.show !== "function" + ); }, isAppError(error, file) { // Ignore errors from external scripts. - return file && (file.indexOf('devdocs') !== -1) && (file.indexOf('.js') === (file.length - 3)); + return ( + file && + file.indexOf("devdocs") !== -1 && + file.indexOf(".js") === file.length - 3 + ); }, isSupportedBrowser() { try { const features = { - bind: !!Function.prototype.bind, - pushState: !!history.pushState, - matchMedia: !!window.matchMedia, + bind: !!Function.prototype.bind, + pushState: !!history.pushState, + matchMedia: !!window.matchMedia, insertAdjacentHTML: !!document.body.insertAdjacentHTML, - defaultPrevented: document.createEvent('CustomEvent').defaultPrevented === false, - cssVariables: !!__guardMethod__(CSS, 'supports', o => o.supports('(--t: 0)')) + defaultPrevented: + document.createEvent("CustomEvent").defaultPrevented === false, + cssVariables: !!__guardMethod__(CSS, "supports", (o) => + o.supports("(--t: 0)"), + ), }; for (var key in features) { var value = features[key]; if (!value) { - Raven.captureMessage(`unsupported/${key}`, {level: 'info'}); + Raven.captureMessage(`unsupported/${key}`, { level: "info" }); return false; } } return true; } catch (error) { - Raven.captureMessage('unsupported/exception', {level: 'info', extra: { error }}); + Raven.captureMessage("unsupported/exception", { + level: "info", + extra: { error }, + }); return false; } }, isSingleDoc() { - return document.body.hasAttribute('data-doc'); + return document.body.hasAttribute("data-doc"); }, isMobile() { - return this._isMobile != null ? this._isMobile : (this._isMobile = app.views.Mobile.detect()); + return this._isMobile != null + ? this._isMobile + : (this._isMobile = app.views.Mobile.detect()); }, isAndroidWebview() { - return this._isAndroidWebview != null ? this._isAndroidWebview : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview()); + return this._isAndroidWebview != null + ? this._isAndroidWebview + : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview()); }, isInvalidLocation() { - return (this.config.env === 'production') && (location.host.indexOf(app.config.production_host) !== 0); - } + return ( + this.config.env === "production" && + location.host.indexOf(app.config.production_host) !== 0 + ); + }, }; $.extend(app, Events); function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; + return typeof value !== "undefined" && value !== null + ? transform(value) + : undefined; } function __guardMethod__(obj, methodName, transform) { - if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') { + if ( + typeof obj !== "undefined" && + obj !== null && + typeof obj[methodName] === "function" + ) { return transform(obj, methodName); } else { return undefined; } -} \ No newline at end of file +} diff --git a/assets/javascripts/app/db.js b/assets/javascripts/app/db.js index ff752243..68bcc1f6 100644 --- a/assets/javascripts/app/db.js +++ b/assets/javascripts/app/db.js @@ -9,12 +9,12 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -(function() { +(function () { let NAME = undefined; let VERSION = undefined; const Cls = (app.DB = class DB { static initClass() { - NAME = 'docs'; + NAME = "docs"; VERSION = 15; } @@ -29,18 +29,27 @@ } db(fn) { - if (!this.useIndexedDB) { return fn(); } - if (fn) { this.callbacks.push(fn); } - if (this.open) { return; } + if (!this.useIndexedDB) { + return fn(); + } + if (fn) { + this.callbacks.push(fn); + } + if (this.open) { + return; + } try { this.open = true; - const req = indexedDB.open(NAME, (VERSION * this.versionMultipler) + this.userVersion()); + const req = indexedDB.open( + NAME, + VERSION * this.versionMultipler + this.userVersion(), + ); req.onsuccess = this.onOpenSuccess; req.onerror = this.onOpenError; req.onupgradeneeded = this.onUpgradeNeeded; } catch (error) { - this.fail('exception', error); + this.fail("exception", error); } } @@ -49,13 +58,17 @@ const db = event.target.result; if (db.objectStoreNames.length === 0) { - try { db.close(); } catch (error1) {} + try { + db.close(); + } catch (error1) {} this.open = false; - this.fail('empty'); - } else if (error = this.buggyIDB(db)) { - try { db.close(); } catch (error2) {} + this.fail("empty"); + } else if ((error = this.buggyIDB(db))) { + try { + db.close(); + } catch (error2) {} this.open = false; - this.fail('buggy', error); + this.fail("buggy", error); } else { this.runCallbacks(db); this.open = false; @@ -66,36 +79,43 @@ onOpenError(event) { event.preventDefault(); this.open = false; - const { - error - } = event.target; + const { error } = event.target; switch (error.name) { - case 'QuotaExceededError': + case "QuotaExceededError": this.onQuotaExceededError(); break; - case 'VersionError': + case "VersionError": this.onVersionError(); break; - case 'InvalidStateError': - this.fail('private_mode'); + case "InvalidStateError": + this.fail("private_mode"); break; default: - this.fail('cant_open', error); + this.fail("cant_open", error); } } fail(reason, error) { this.cachedDocs = null; this.useIndexedDB = false; - if (!this.reason) { this.reason = reason; } - if (!this.error) { this.error = error; } - if (error) { if (typeof console.error === 'function') { - console.error('IDB error', error); - } } + if (!this.reason) { + this.reason = reason; + } + if (!this.error) { + this.error = error; + } + if (error) { + if (typeof console.error === "function") { + console.error("IDB error", error); + } + } this.runCallbacks(); - if (error && (reason === 'cant_open')) { - Raven.captureMessage(`${error.name}: ${error.message}`, {level: 'warning', fingerprint: [error.name]}); + if (error && reason === "cant_open") { + Raven.captureMessage(`${error.name}: ${error.message}`, { + level: "warning", + fingerprint: [error.name], + }); } } @@ -103,34 +123,39 @@ this.reset(); this.db(); app.onQuotaExceeded(); - Raven.captureMessage('QuotaExceededError', {level: 'warning'}); + Raven.captureMessage("QuotaExceededError", { level: "warning" }); } onVersionError() { const req = indexedDB.open(NAME); - req.onsuccess = event => { + req.onsuccess = (event) => { return this.handleVersionMismatch(event.target.result.version); }; - req.onerror = function(event) { + req.onerror = function (event) { event.preventDefault(); - return this.fail('cant_open', error); + return this.fail("cant_open", error); }; } handleVersionMismatch(actualVersion) { if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) { - this.fail('version'); + this.fail("version"); } else { - this.setUserVersion(actualVersion - (VERSION * this.versionMultipler)); + this.setUserVersion(actualVersion - VERSION * this.versionMultipler); this.db(); } } buggyIDB(db) { - if (this.checkedBuggyIDB) { return; } + if (this.checkedBuggyIDB) { + return; + } this.checkedBuggyIDB = true; try { - this.idbTransaction(db, {stores: $.makeArray(db.objectStoreNames).slice(0, 2), mode: 'readwrite'}).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937 + this.idbTransaction(db, { + stores: $.makeArray(db.objectStoreNames).slice(0, 2), + mode: "readwrite", + }).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937 return; } catch (error) { return error; @@ -139,53 +164,72 @@ runCallbacks(db) { let fn; - while ((fn = this.callbacks.shift())) { fn(db); } + while ((fn = this.callbacks.shift())) { + fn(db); + } } onUpgradeNeeded(event) { let db; - if (!(db = event.target.result)) { return; } + if (!(db = event.target.result)) { + return; + } const objectStoreNames = $.makeArray(db.objectStoreNames); - if (!$.arrayDelete(objectStoreNames, 'docs')) { - try { db.createObjectStore('docs'); } catch (error) {} + if (!$.arrayDelete(objectStoreNames, "docs")) { + try { + db.createObjectStore("docs"); + } catch (error) {} } for (var doc of Array.from(app.docs.all())) { if (!$.arrayDelete(objectStoreNames, doc.slug)) { - try { db.createObjectStore(doc.slug); } catch (error1) {} + try { + db.createObjectStore(doc.slug); + } catch (error1) {} } } for (var name of Array.from(objectStoreNames)) { - try { db.deleteObjectStore(name); } catch (error2) {} + try { + db.deleteObjectStore(name); + } catch (error2) {} } } store(doc, data, onSuccess, onError, _retry) { - if (_retry == null) { _retry = true; } - this.db(db => { + if (_retry == null) { + _retry = true; + } + this.db((db) => { if (!db) { onError(); return; } - const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); + const txn = this.idbTransaction(db, { + stores: ["docs", doc.slug], + mode: "readwrite", + ignoreError: false, + }); txn.oncomplete = () => { if (this.cachedDocs != null) { this.cachedDocs[doc.slug] = doc.mtime; } onSuccess(); }; - txn.onerror = event => { + txn.onerror = (event) => { event.preventDefault(); - if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { + if ( + (txn.error != null ? txn.error.name : undefined) === + "NotFoundError" && + _retry + ) { this.migrate(); setTimeout(() => { return this.store(doc, data, onSuccess, onError, false); - } - , 0); + }, 0); } else { onError(event); } @@ -193,42 +237,54 @@ let store = txn.objectStore(doc.slug); store.clear(); - for (var path in data) { var content = data[path]; store.add(content, path); } + for (var path in data) { + var content = data[path]; + store.add(content, path); + } - store = txn.objectStore('docs'); + store = txn.objectStore("docs"); store.put(doc.mtime, doc.slug); }); } unstore(doc, onSuccess, onError, _retry) { - if (_retry == null) { _retry = true; } - this.db(db => { + if (_retry == null) { + _retry = true; + } + this.db((db) => { if (!db) { onError(); return; } - const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); + const txn = this.idbTransaction(db, { + stores: ["docs", doc.slug], + mode: "readwrite", + ignoreError: false, + }); txn.oncomplete = () => { if (this.cachedDocs != null) { delete this.cachedDocs[doc.slug]; } onSuccess(); }; - txn.onerror = function(event) { + txn.onerror = function (event) { event.preventDefault(); - if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { + if ( + (txn.error != null ? txn.error.name : undefined) === + "NotFoundError" && + _retry + ) { this.migrate(); setTimeout(() => { return this.unstore(doc, onSuccess, onError, false); - } - , 0); + }, 0); } else { onError(event); } }; - let store = txn.objectStore('docs'); + let store = txn.objectStore("docs"); store.delete(doc.slug); store = txn.objectStore(doc.slug); @@ -243,20 +299,23 @@ return; } - this.db(db => { + this.db((db) => { if (!db) { fn(false); return; } - const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); - const store = txn.objectStore('docs'); + const txn = this.idbTransaction(db, { + stores: ["docs"], + mode: "readonly", + }); + const store = txn.objectStore("docs"); const req = store.get(doc.slug); - req.onsuccess = function() { + req.onsuccess = function () { fn(req.result); }; - req.onerror = function(event) { + req.onerror = function (event) { event.preventDefault(); fn(false); }; @@ -264,36 +323,41 @@ } cachedVersion(doc) { - if (!this.cachedDocs) { return; } + if (!this.cachedDocs) { + return; + } return this.cachedDocs[doc.slug] || false; } versions(docs, fn) { let versions; - if (versions = this.cachedVersions(docs)) { + if ((versions = this.cachedVersions(docs))) { fn(versions); return; } - return this.db(db => { + return this.db((db) => { if (!db) { fn(false); return; } - const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); - txn.oncomplete = function() { + const txn = this.idbTransaction(db, { + stores: ["docs"], + mode: "readonly", + }); + txn.oncomplete = function () { fn(result); }; - const store = txn.objectStore('docs'); + const store = txn.objectStore("docs"); var result = {}; - docs.forEach(function(doc) { + docs.forEach(function (doc) { const req = store.get(doc.slug); - req.onsuccess = function() { + req.onsuccess = function () { result[doc.slug] = req.result; }; - req.onerror = function(event) { + req.onerror = function (event) { event.preventDefault(); result[doc.slug] = false; }; @@ -302,9 +366,13 @@ } cachedVersions(docs) { - if (!this.cachedDocs) { return; } + if (!this.cachedDocs) { + return; + } const result = {}; - for (var doc of Array.from(docs)) { result[doc.slug] = this.cachedVersion(doc); } + for (var doc of Array.from(docs)) { + result[doc.slug] = this.cachedVersion(doc); + } return result; } @@ -320,14 +388,14 @@ loadWithXHR(entry, onSuccess, onError) { return ajax({ url: entry.fileUrl(), - dataType: 'html', + dataType: "html", success: onSuccess, - error: onError + error: onError, }); } loadWithIDB(entry, onSuccess, onError) { - return this.db(db => { + return this.db((db) => { if (!db) { onError(); return; @@ -339,14 +407,21 @@ return; } - const txn = this.idbTransaction(db, {stores: [entry.doc.slug], mode: 'readonly'}); + const txn = this.idbTransaction(db, { + stores: [entry.doc.slug], + mode: "readonly", + }); const store = txn.objectStore(entry.doc.slug); const req = store.get(entry.dbPath()); - req.onsuccess = function() { - if (req.result) { onSuccess(req.result); } else { onError(); } + req.onsuccess = function () { + if (req.result) { + onSuccess(req.result); + } else { + onError(); + } }; - req.onerror = function(event) { + req.onerror = function (event) { event.preventDefault(); onError(); }; @@ -355,31 +430,38 @@ } loadDocsCache(db) { - if (this.cachedDocs) { return; } + if (this.cachedDocs) { + return; + } this.cachedDocs = {}; - const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); + const txn = this.idbTransaction(db, { + stores: ["docs"], + mode: "readonly", + }); txn.oncomplete = () => { setTimeout(this.checkForCorruptedDocs, 50); }; - const req = txn.objectStore('docs').openCursor(); - req.onsuccess = event => { + const req = txn.objectStore("docs").openCursor(); + req.onsuccess = (event) => { let cursor; - if (!(cursor = event.target.result)) { return; } + if (!(cursor = event.target.result)) { + return; + } this.cachedDocs[cursor.key] = cursor.value; cursor.continue(); }; - req.onerror = function(event) { + req.onerror = function (event) { event.preventDefault(); }; } checkForCorruptedDocs() { - this.db(db => { + this.db((db) => { let slug; this.corruptedDocs = []; - const docs = ((() => { + const docs = (() => { const result = []; for (var key in this.cachedDocs) { var value = this.cachedDocs[key]; @@ -388,11 +470,13 @@ } } return result; - })()); - if (docs.length === 0) { return; } + })(); + if (docs.length === 0) { + return; + } for (slug of Array.from(docs)) { - if (!app.docs.findBy('slug', slug)) { + if (!app.docs.findBy("slug", slug)) { this.corruptedDocs.push(slug); } } @@ -406,46 +490,64 @@ return; } - const txn = this.idbTransaction(db, {stores: docs, mode: 'readonly', ignoreError: false}); + const txn = this.idbTransaction(db, { + stores: docs, + mode: "readonly", + ignoreError: false, + }); txn.oncomplete = () => { - if (this.corruptedDocs.length > 0) { setTimeout(this.deleteCorruptedDocs, 0); } + if (this.corruptedDocs.length > 0) { + setTimeout(this.deleteCorruptedDocs, 0); + } }; for (var doc of Array.from(docs)) { - txn.objectStore(doc).get('index').onsuccess = event => { - if (!event.target.result) { this.corruptedDocs.push(event.target.source.name); } + txn.objectStore(doc).get("index").onsuccess = (event) => { + if (!event.target.result) { + this.corruptedDocs.push(event.target.source.name); + } }; } }); } deleteCorruptedDocs() { - this.db(db => { + this.db((db) => { let doc; - const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readwrite', ignoreError: false}); - const store = txn.objectStore('docs'); + const txn = this.idbTransaction(db, { + stores: ["docs"], + mode: "readwrite", + ignoreError: false, + }); + const store = txn.objectStore("docs"); while ((doc = this.corruptedDocs.pop())) { this.cachedDocs[doc] = false; store.delete(doc); } }); - Raven.captureMessage('corruptedDocs', {level: 'info', extra: { docs: this.corruptedDocs.join(',') }}); + Raven.captureMessage("corruptedDocs", { + level: "info", + extra: { docs: this.corruptedDocs.join(",") }, + }); } shouldLoadWithIDB(entry) { - return this.useIndexedDB && (!this.cachedDocs || this.cachedDocs[entry.doc.slug]); + return ( + this.useIndexedDB && + (!this.cachedDocs || this.cachedDocs[entry.doc.slug]) + ); } idbTransaction(db, options) { app.lastIDBTransaction = [options.stores, options.mode]; const txn = db.transaction(options.stores, options.mode); if (options.ignoreError !== false) { - txn.onerror = function(event) { + txn.onerror = function (event) { event.preventDefault(); }; } if (options.ignoreAbort !== false) { - txn.onabort = function(event) { + txn.onabort = function (event) { event.preventDefault(); }; } @@ -453,9 +555,11 @@ } reset() { - try { if (typeof indexedDB !== 'undefined' && indexedDB !== null) { - indexedDB.deleteDatabase(NAME); - } } catch (error) {} + try { + if (typeof indexedDB !== "undefined" && indexedDB !== null) { + indexedDB.deleteDatabase(NAME); + } + } catch (error) {} } useIndexedDB() { @@ -463,7 +567,7 @@ if (!app.isSingleDoc() && window.indexedDB) { return true; } else { - this.reason = 'not_supported'; + this.reason = "not_supported"; return false; } } catch (error) { @@ -472,15 +576,15 @@ } migrate() { - app.settings.set('schema', this.userVersion() + 1); + app.settings.set("schema", this.userVersion() + 1); } setUserVersion(version) { - app.settings.set('schema', version); + app.settings.set("schema", version); } userVersion() { - return app.settings.get('schema'); + return app.settings.get("schema"); } }); Cls.initClass(); diff --git a/assets/javascripts/app/router.js b/assets/javascripts/app/router.js index 96966b02..bfb24d64 100644 --- a/assets/javascripts/app/router.js +++ b/assets/javascripts/app/router.js @@ -12,19 +12,19 @@ const Cls = (app.Router = class Router { static initClass() { $.extend(this.prototype, Events); - + this.routes = [ - ['*', 'before' ], - ['/', 'root' ], - ['/settings', 'settings' ], - ['/offline', 'offline' ], - ['/about', 'about' ], - ['/news', 'news' ], - ['/help', 'help' ], - ['/:doc-:type/', 'type' ], - ['/:doc/', 'doc' ], - ['/:doc/:path(*)', 'entry' ], - ['*', 'notFound' ] + ["*", "before"], + ["/", "root"], + ["/settings", "settings"], + ["/offline", "offline"], + ["/about", "about"], + ["/news", "news"], + ["/help", "help"], + ["/:doc-:type/", "type"], + ["/:doc/", "doc"], + ["/:doc/:path(*)", "entry"], + ["*", "notFound"], ]; } @@ -45,16 +45,16 @@ const Cls = (app.Router = class Router { triggerRoute(name) { this.trigger(name, this.context); - this.trigger('after', name, this.context); + this.trigger("after", name, this.context); } before(context, next) { let res; const previousContext = this.context; this.context = context; - this.trigger('before', context); + this.trigger("before", context); - if (res = next()) { + if ((res = next())) { this.context = previousContext; return res; } else { @@ -64,10 +64,14 @@ const Cls = (app.Router = class Router { doc(context, next) { let doc; - if (doc = app.docs.findBySlug(context.params.doc) || app.disabledDocs.findBySlug(context.params.doc)) { + if ( + (doc = + app.docs.findBySlug(context.params.doc) || + app.disabledDocs.findBySlug(context.params.doc)) + ) { context.doc = doc; context.entry = doc.toEntry(); - this.triggerRoute('entry'); + this.triggerRoute("entry"); return; } else { return next(); @@ -78,10 +82,13 @@ const Cls = (app.Router = class Router { let type; const doc = app.docs.findBySlug(context.params.doc); - if (type = doc != null ? doc.types.findBy('slug', context.params.type) : undefined) { + if ( + (type = + doc != null ? doc.types.findBy("slug", context.params.type) : undefined) + ) { context.doc = doc; context.type = type; - this.triggerRoute('type'); + this.triggerRoute("type"); return; } else { return next(); @@ -91,86 +98,110 @@ const Cls = (app.Router = class Router { entry(context, next) { let entry; const doc = app.docs.findBySlug(context.params.doc); - if (!doc) { return next(); } - let { - path - } = context.params; - const { - hash - } = context; - - if (entry = doc.findEntryByPathAndHash(path, hash)) { + if (!doc) { + return next(); + } + let { path } = context.params; + const { hash } = context; + + if ((entry = doc.findEntryByPathAndHash(path, hash))) { context.doc = doc; context.entry = entry; - this.triggerRoute('entry'); + this.triggerRoute("entry"); return; - } else if (path.slice(-6) === '/index') { + } else if (path.slice(-6) === "/index") { path = path.substr(0, path.length - 6); - if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } + if ((entry = doc.findEntryByPathAndHash(path, hash))) { + return entry.fullPath(); + } } else { path = `${path}/index`; - if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } + if ((entry = doc.findEntryByPathAndHash(path, hash))) { + return entry.fullPath(); + } } return next(); } root() { - if (app.isSingleDoc()) { return '/'; } - this.triggerRoute('root'); + if (app.isSingleDoc()) { + return "/"; + } + this.triggerRoute("root"); } settings(context) { - if (app.isSingleDoc()) { return `/#/${context.path}`; } - this.triggerRoute('settings'); + if (app.isSingleDoc()) { + return `/#/${context.path}`; + } + this.triggerRoute("settings"); } - offline(context){ - if (app.isSingleDoc()) { return `/#/${context.path}`; } - this.triggerRoute('offline'); + offline(context) { + if (app.isSingleDoc()) { + return `/#/${context.path}`; + } + this.triggerRoute("offline"); } about(context) { - if (app.isSingleDoc()) { return `/#/${context.path}`; } - context.page = 'about'; - this.triggerRoute('page'); + if (app.isSingleDoc()) { + return `/#/${context.path}`; + } + context.page = "about"; + this.triggerRoute("page"); } news(context) { - if (app.isSingleDoc()) { return `/#/${context.path}`; } - context.page = 'news'; - this.triggerRoute('page'); + if (app.isSingleDoc()) { + return `/#/${context.path}`; + } + context.page = "news"; + this.triggerRoute("page"); } help(context) { - if (app.isSingleDoc()) { return `/#/${context.path}`; } - context.page = 'help'; - this.triggerRoute('page'); + if (app.isSingleDoc()) { + return `/#/${context.path}`; + } + context.page = "help"; + this.triggerRoute("page"); } notFound(context) { - this.triggerRoute('notFound'); + this.triggerRoute("notFound"); } isIndex() { - return ((this.context != null ? this.context.path : undefined) === '/') || (app.isSingleDoc() && __guard__(this.context != null ? this.context.entry : undefined, x => x.isIndex())); + return ( + (this.context != null ? this.context.path : undefined) === "/" || + (app.isSingleDoc() && + __guard__(this.context != null ? this.context.entry : undefined, (x) => + x.isIndex(), + )) + ); } isSettings() { - return (this.context != null ? this.context.path : undefined) === '/settings'; + return ( + (this.context != null ? this.context.path : undefined) === "/settings" + ); } setInitialPath() { // Remove superfluous forward slashes at the beginning of the path let path; - if ((path = location.pathname.replace(/^\/{2,}/g, '/')) !== location.pathname) { + if ( + (path = location.pathname.replace(/^\/{2,}/g, "/")) !== location.pathname + ) { page.replace(path + location.search + location.hash, null, true); } - if (location.pathname === '/') { - if (path = this.getInitialPathFromHash()) { + if (location.pathname === "/") { + if ((path = this.getInitialPathFromHash())) { page.replace(path + location.search, null, true); - } else if (path = this.getInitialPathFromCookie()) { + } else if ((path = this.getInitialPathFromCookie())) { page.replace(path + location.search + location.hash, null, true); } } @@ -178,24 +209,33 @@ const Cls = (app.Router = class Router { getInitialPathFromHash() { try { - return __guard__((new RegExp("#/(.+)")).exec(decodeURIComponent(location.hash)), x => x[1]); + return __guard__( + new RegExp("#/(.+)").exec(decodeURIComponent(location.hash)), + (x) => x[1], + ); } catch (error) {} } getInitialPathFromCookie() { let path; - if (path = Cookies.get('initial_path')) { - Cookies.expire('initial_path'); + if ((path = Cookies.get("initial_path"))) { + Cookies.expire("initial_path"); return path; } } replaceHash(hash) { - page.replace(location.pathname + location.search + (hash || ''), null, true); + page.replace( + location.pathname + location.search + (hash || ""), + null, + true, + ); } }); Cls.initClass(); function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== "undefined" && value !== null + ? transform(value) + : undefined; +} diff --git a/assets/javascripts/app/searcher.js b/assets/javascripts/app/searcher.js index f95e6f72..a10ae217 100644 --- a/assets/javascripts/app/searcher.js +++ b/assets/javascripts/app/searcher.js @@ -16,116 +16,150 @@ // Match functions // -let fuzzyRegexp, i, index, lastIndex, match, matcher, matchIndex, matchLength, queryLength, score, separators, value, valueLength; -const SEPARATOR = '.'; +let fuzzyRegexp, + i, + index, + lastIndex, + match, + matcher, + matchIndex, + matchLength, + queryLength, + score, + separators, + value, + valueLength; +const SEPARATOR = "."; let query = -(queryLength = -(value = -(valueLength = -(matcher = // current match function -(fuzzyRegexp = // query fuzzy regexp -(index = // position of the query in the string being matched -(lastIndex = // last position of the query in the string being matched -(match = // regexp match data -(matchIndex = -(matchLength = -(score = // score for the current match -(separators = // counter -(i = null))))))))))))); // cursor + (queryLength = + value = + valueLength = + matcher = // current match function + fuzzyRegexp = // query fuzzy regexp + index = // position of the query in the string being matched + lastIndex = // last position of the query in the string being matched + match = // regexp match data + matchIndex = + matchLength = + score = // score for the current match + separators = // counter + i = + null); // cursor function exactMatch() { -index = value.indexOf(query); -if (!(index >= 0)) { return; } + index = value.indexOf(query); + if (!(index >= 0)) { + return; + } -lastIndex = value.lastIndexOf(query); + lastIndex = value.lastIndexOf(query); -if (index !== lastIndex) { - return Math.max(scoreExactMatch(), ((index = lastIndex) && scoreExactMatch()) || 0); -} else { - return scoreExactMatch(); -} + if (index !== lastIndex) { + return Math.max( + scoreExactMatch(), + ((index = lastIndex) && scoreExactMatch()) || 0, + ); + } else { + return scoreExactMatch(); + } } function scoreExactMatch() { -// Remove one point for each unmatched character. -score = 100 - (valueLength - queryLength); - -if (index > 0) { - // If the character preceding the query is a dot, assign the same score - // as if the query was found at the beginning of the string, minus one. - if (value.charAt(index - 1) === SEPARATOR) { - score += index - 1; - // Don't match a single-character query unless it's found at the beginning - // of the string or is preceded by a dot. - } else if (queryLength === 1) { - return; - // (1) Remove one point for each unmatched character up to the nearest - // preceding dot or the beginning of the string. - // (2) Remove one point for each unmatched character following the query. - } else { + // Remove one point for each unmatched character. + score = 100 - (valueLength - queryLength); + + if (index > 0) { + // If the character preceding the query is a dot, assign the same score + // as if the query was found at the beginning of the string, minus one. + if (value.charAt(index - 1) === SEPARATOR) { + score += index - 1; + // Don't match a single-character query unless it's found at the beginning + // of the string or is preceded by a dot. + } else if (queryLength === 1) { + return; + // (1) Remove one point for each unmatched character up to the nearest + // preceding dot or the beginning of the string. + // (2) Remove one point for each unmatched character following the query. + } else { + i = index - 2; + while (i >= 0 && value.charAt(i) !== SEPARATOR) { + i--; + } + score -= + index - + i + // (1) + (valueLength - queryLength - index); // (2) + } + + // Remove one point for each dot preceding the query, except for the one + // immediately before the query. + separators = 0; i = index - 2; - while ((i >= 0) && (value.charAt(i) !== SEPARATOR)) { i--; } - score -= (index - i) + // (1) - (valueLength - queryLength - index); // (2) + while (i >= 0) { + if (value.charAt(i) === SEPARATOR) { + separators++; + } + i--; + } + score -= separators; } - // Remove one point for each dot preceding the query, except for the one - // immediately before the query. + // Remove five points for each dot following the query. separators = 0; - i = index - 2; + i = valueLength - queryLength - index - 1; while (i >= 0) { - if (value.charAt(i) === SEPARATOR) { separators++; } + if (value.charAt(index + queryLength + i) === SEPARATOR) { + separators++; + } i--; } - score -= separators; -} - -// Remove five points for each dot following the query. -separators = 0; -i = valueLength - queryLength - index - 1; -while (i >= 0) { - if (value.charAt(index + queryLength + i) === SEPARATOR) { separators++; } - i--; -} -score -= separators * 5; + score -= separators * 5; -return Math.max(1, score); + return Math.max(1, score); } function fuzzyMatch() { -if ((valueLength <= queryLength) || (value.indexOf(query) >= 0)) { return; } -if (!(match = fuzzyRegexp.exec(value))) { return; } -matchIndex = match.index; -matchLength = match[0].length; -score = scoreFuzzyMatch(); -if (match = fuzzyRegexp.exec(value.slice(i = value.lastIndexOf(SEPARATOR) + 1))) { - matchIndex = i + match.index; + if (valueLength <= queryLength || value.indexOf(query) >= 0) { + return; + } + if (!(match = fuzzyRegexp.exec(value))) { + return; + } + matchIndex = match.index; matchLength = match[0].length; - return Math.max(score, scoreFuzzyMatch()); -} else { - return score; -} + score = scoreFuzzyMatch(); + if ( + (match = fuzzyRegexp.exec( + value.slice((i = value.lastIndexOf(SEPARATOR) + 1)), + )) + ) { + matchIndex = i + match.index; + matchLength = match[0].length; + return Math.max(score, scoreFuzzyMatch()); + } else { + return score; + } } function scoreFuzzyMatch() { -// When the match is at the beginning of the string or preceded by a dot. -if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { - return Math.max(66, 100 - matchLength); -// When the match is at the end of the string. -} else if ((matchIndex + matchLength) === valueLength) { - return Math.max(33, 67 - matchLength); -// When the match is in the middle of the string. -} else { - return Math.max(1, 34 - matchLength); -} + // When the match is at the beginning of the string or preceded by a dot. + if (matchIndex === 0 || value.charAt(matchIndex - 1) === SEPARATOR) { + return Math.max(66, 100 - matchLength); + // When the match is at the end of the string. + } else if (matchIndex + matchLength === valueLength) { + return Math.max(33, 67 - matchLength); + // When the match is in the middle of the string. + } else { + return Math.max(1, 34 - matchLength); + } } // // Searchers // -(function() { +(function () { let CHUNK_SIZE = undefined; let DEFAULTS = undefined; let SEPARATORS_REGEXP = undefined; @@ -141,25 +175,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { const Cls = (app.Searcher = class Searcher { static initClass() { $.extend(this.prototype, Events); - + CHUNK_SIZE = 20000; - + DEFAULTS = { max_results: app.config.max_results, - fuzzy_min_length: 3 + fuzzy_min_length: 3, }; - - SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g; + + SEPARATORS_REGEXP = + /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g; EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/; INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/; EMPTY_PARANTHESES_REGEXP = /\(\)/; EVENT_REGEXP = /\ event$/; DOT_REGEXP = /\.+/g; WHITESPACE_REGEXP = /\s/g; - - EMPTY_STRING = ''; - ELLIPSIS = '...'; - STRING = 'string'; + + EMPTY_STRING = ""; + ELLIPSIS = "..."; + STRING = "string"; } static normalizeString(string) { @@ -176,13 +211,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { static normalizeQuery(string) { string = this.normalizeString(string); - return string.replace(EOS_SEPARATORS_REGEXP, '$1.'); + return string.replace(EOS_SEPARATORS_REGEXP, "$1."); } constructor(options) { this.match = this.match.bind(this); this.matchChunks = this.matchChunks.bind(this); - if (options == null) { options = {}; } + if (options == null) { + options = {}; + } this.options = $.extend({}, DEFAULTS, options); } @@ -194,11 +231,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { this.query = q; this.setup(); - if (this.isValid()) { this.match(); } else { this.end(); } + if (this.isValid()) { + this.match(); + } else { + this.end(); + } } setup() { - query = (this.query = this.constructor.normalizeQuery(this.query)); + query = this.query = this.constructor.normalizeQuery(this.query); queryLength = query.length; this.dataLength = this.data.length; this.matchers = [exactMatch]; @@ -216,12 +257,14 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { } isValid() { - return (queryLength > 0) && (query !== SEPARATOR); + return queryLength > 0 && query !== SEPARATOR; } end() { - if (!this.totalResults) { this.triggerResults([]); } - this.trigger('end'); + if (!this.totalResults) { + this.triggerResults([]); + } + this.trigger("end"); this.free(); } @@ -233,8 +276,17 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { } free() { - this.data = (this.attr = (this.dataLength = (this.matchers = (this.matcher = (this.query = - (this.totalResults = (this.scoreMap = (this.cursor = (this.timeout = null))))))))); + this.data = + this.attr = + this.dataLength = + this.matchers = + this.matcher = + this.query = + this.totalResults = + this.scoreMap = + this.cursor = + this.timeout = + null; } match() { @@ -254,7 +306,7 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { matchChunks() { this.matchChunk(); - if ((this.cursor === this.dataLength) || this.scoredEnough()) { + if (this.cursor === this.dataLength || this.scoredEnough()) { this.delay(this.match); this.sendResults(); } else { @@ -263,28 +315,36 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { } matchChunk() { - ({ - matcher - } = this); - for (let j = 0, end = this.chunkSize(), asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { + ({ matcher } = this); + for ( + let j = 0, end = this.chunkSize(), asc = 0 <= end; + asc ? j < end : j > end; + asc ? j++ : j-- + ) { value = this.data[this.cursor][this.attr]; - if (value.split) { // string + if (value.split) { + // string valueLength = value.length; - if (score = matcher()) { this.addResult(this.data[this.cursor], score); } - } else { // array + if ((score = matcher())) { + this.addResult(this.data[this.cursor], score); + } + } else { + // array score = 0; for (value of Array.from(this.data[this.cursor][this.attr])) { valueLength = value.length; score = Math.max(score, matcher() || 0); } - if (score > 0) { this.addResult(this.data[this.cursor], score); } + if (score > 0) { + this.addResult(this.data[this.cursor], score); + } } this.cursor++; } } chunkSize() { - if ((this.cursor + CHUNK_SIZE) > this.dataLength) { + if (this.cursor + CHUNK_SIZE > this.dataLength) { return this.dataLength % CHUNK_SIZE; } else { return CHUNK_SIZE; @@ -292,8 +352,11 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { } scoredEnough() { - return (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= this.options.max_results; - } + return ( + (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= + this.options.max_results + ); + } foundEnough() { return this.totalResults >= this.options.max_results; @@ -301,7 +364,9 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { addResult(object, score) { let name; - (this.scoreMap[name = Math.round(score)] || (this.scoreMap[name] = [])).push(object); + ( + this.scoreMap[(name = Math.round(score))] || (this.scoreMap[name] = []) + ).push(object); this.totalResults++; } @@ -318,21 +383,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { sendResults() { const results = this.getResults(); - if (results.length) { this.triggerResults(results); } + if (results.length) { + this.triggerResults(results); + } } triggerResults(results) { - this.trigger('results', results); + this.trigger("results", results); } delay(fn) { - return this.timeout = setTimeout(fn, 1); + return (this.timeout = setTimeout(fn, 1)); } queryToFuzzyRegexp(string) { - const chars = string.split(''); - for (i = 0; i < chars.length; i++) { var char = chars[i]; chars[i] = $.escapeRegexp(char); } - return new RegExp(chars.join('.*?')); + const chars = string.split(""); + for (i = 0; i < chars.length; i++) { + var char = chars[i]; + chars[i] = $.escapeRegexp(char); + } + return new RegExp(chars.join(".*?")); } }); Cls.initClass(); @@ -347,7 +417,9 @@ app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher { match() { if (this.matcher) { - if (!this.allResults) { this.allResults = []; } + if (!this.allResults) { + this.allResults = []; + } this.allResults.push.apply(this.allResults, this.getResults()); } return super.match(...arguments); diff --git a/assets/javascripts/app/serviceworker.js b/assets/javascripts/app/serviceworker.js index ed94036c..b94d8c2e 100644 --- a/assets/javascripts/app/serviceworker.js +++ b/assets/javascripts/app/serviceworker.js @@ -21,22 +21,28 @@ const Cls = (app.ServiceWorker = class ServiceWorker { this.registration = null; this.notifyUpdate = true; - navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'}) + navigator.serviceWorker + .register(app.config.service_worker_path, { scope: "/" }) .then( - registration => this.updateRegistration(registration), - error => console.error('Could not register service worker:', error)); + (registration) => this.updateRegistration(registration), + (error) => console.error("Could not register service worker:", error), + ); } update() { - if (!this.registration) { return; } + if (!this.registration) { + return; + } this.notifyUpdate = true; - return this.registration.update().catch(function() {}); + return this.registration.update().catch(function () {}); } updateInBackground() { - if (!this.registration) { return; } + if (!this.registration) { + return; + } this.notifyUpdate = false; - return this.registration.update().catch(function() {}); + return this.registration.update().catch(function () {}); } reload() { @@ -45,24 +51,32 @@ const Cls = (app.ServiceWorker = class ServiceWorker { updateRegistration(registration) { this.registration = registration; - $.on(this.registration, 'updatefound', this.onUpdateFound); + $.on(this.registration, "updatefound", this.onUpdateFound); } onUpdateFound() { - if (this.installingRegistration) { $.off(this.installingRegistration, 'statechange', this.onStateChange()); } + if (this.installingRegistration) { + $.off(this.installingRegistration, "statechange", this.onStateChange()); + } this.installingRegistration = this.registration.installing; - $.on(this.installingRegistration, 'statechange', this.onStateChange); + $.on(this.installingRegistration, "statechange", this.onStateChange); } onStateChange() { - if (this.installingRegistration && (this.installingRegistration.state === 'installed') && navigator.serviceWorker.controller) { + if ( + this.installingRegistration && + this.installingRegistration.state === "installed" && + navigator.serviceWorker.controller + ) { this.installingRegistration = null; this.onUpdateReady(); } } onUpdateReady() { - if (this.notifyUpdate) { this.trigger('updateready'); } + if (this.notifyUpdate) { + this.trigger("updateready"); + } } }); Cls.initClass(); diff --git a/assets/javascripts/app/settings.js b/assets/javascripts/app/settings.js index 7bc7af7e..85d72681 100644 --- a/assets/javascripts/app/settings.js +++ b/assets/javascripts/app/settings.js @@ -10,44 +10,39 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -(function() { +(function () { let PREFERENCE_KEYS = undefined; let INTERNAL_KEYS = undefined; const Cls = (app.Settings = class Settings { static initClass() { PREFERENCE_KEYS = [ - 'hideDisabled', - 'hideIntro', - 'manualUpdate', - 'fastScroll', - 'arrowScroll', - 'analyticsConsent', - 'docs', - 'dark', // legacy - 'theme', - 'layout', - 'size', - 'tips', - 'noAutofocus', - 'autoInstall', - 'spaceScroll', - 'spaceTimeout' + "hideDisabled", + "hideIntro", + "manualUpdate", + "fastScroll", + "arrowScroll", + "analyticsConsent", + "docs", + "dark", // legacy + "theme", + "layout", + "size", + "tips", + "noAutofocus", + "autoInstall", + "spaceScroll", + "spaceTimeout", ]; - - INTERNAL_KEYS = [ - 'count', - 'schema', - 'version', - 'news' - ]; - + + INTERNAL_KEYS = ["count", "schema", "version", "news"]; + this.prototype.LAYOUTS = [ - '_max-width', - '_sidebar-hidden', - '_native-scrollbars', - '_text-justify-hyphenate' + "_max-width", + "_sidebar-hidden", + "_native-scrollbars", + "_text-justify-hyphenate", ]; - + this.defaults = { count: 0, hideDisabled: false, @@ -56,29 +51,38 @@ manualUpdate: false, schema: 1, analyticsConsent: false, - theme: 'auto', + theme: "auto", spaceScroll: 1, - spaceTimeout: 0.5 + spaceTimeout: 0.5, }; } constructor() { - this.store = new CookiesStore; + this.store = new CookiesStore(); this.cache = {}; - this.autoSupported = window.matchMedia('(prefers-color-scheme)').media !== 'not all'; + this.autoSupported = + window.matchMedia("(prefers-color-scheme)").media !== "not all"; if (this.autoSupported) { - this.darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); - this.darkModeQuery.addListener(() => this.setTheme(this.get('theme'))); + this.darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)"); + this.darkModeQuery.addListener(() => this.setTheme(this.get("theme"))); } } - get(key) { let left; - if (this.cache.hasOwnProperty(key)) { return this.cache[key]; } - this.cache[key] = (left = this.store.get(key)) != null ? left : this.constructor.defaults[key]; - if ((key === 'theme') && (this.cache[key] === 'auto') && !this.darkModeQuery) { - return this.cache[key] = 'default'; + if (this.cache.hasOwnProperty(key)) { + return this.cache[key]; + } + this.cache[key] = + (left = this.store.get(key)) != null + ? left + : this.constructor.defaults[key]; + if ( + key === "theme" && + this.cache[key] === "auto" && + !this.darkModeQuery + ) { + return (this.cache[key] = "default"); } else { return this.cache[key]; } @@ -87,7 +91,9 @@ set(key, value) { this.store.set(key, value); delete this.cache[key]; - if (key === 'theme') { this.setTheme(value); } + if (key === "theme") { + this.setTheme(value); + } } del(key) { @@ -96,51 +102,58 @@ } hasDocs() { - try { return !!this.store.get('docs'); } catch (error) {} + try { + return !!this.store.get("docs"); + } catch (error) {} } getDocs() { - return __guard__(this.store.get('docs'), x => x.split('/')) || app.config.default_docs; + return ( + __guard__(this.store.get("docs"), (x) => x.split("/")) || + app.config.default_docs + ); } setDocs(docs) { - this.set('docs', docs.join('/')); + this.set("docs", docs.join("/")); } getTips() { - return __guard__(this.store.get('tips'), x => x.split('/')) || []; + return __guard__(this.store.get("tips"), (x) => x.split("/")) || []; } setTips(tips) { - this.set('tips', tips.join('/')); + this.set("tips", tips.join("/")); } setLayout(name, enable) { this.toggleLayout(name, enable); - const layout = (this.store.get('layout') || '').split(' '); - $.arrayDelete(layout, ''); + const layout = (this.store.get("layout") || "").split(" "); + $.arrayDelete(layout, ""); if (enable) { - if (layout.indexOf(name) === -1) { layout.push(name); } + if (layout.indexOf(name) === -1) { + layout.push(name); + } } else { $.arrayDelete(layout, name); } if (layout.length > 0) { - this.set('layout', layout.join(' ')); + this.set("layout", layout.join(" ")); } else { - this.del('layout'); + this.del("layout"); } } hasLayout(name) { - const layout = (this.store.get('layout') || '').split(' '); + const layout = (this.store.get("layout") || "").split(" "); return layout.indexOf(name) !== -1; } setSize(value) { - this.set('size', value); + this.set("size", value); } dump() { @@ -149,7 +162,9 @@ export() { const data = this.dump(); - for (var key of Array.from(INTERNAL_KEYS)) { delete data[key]; } + for (var key of Array.from(INTERNAL_KEYS)) { + delete data[key]; + } return data; } @@ -158,11 +173,15 @@ const object = this.export(); for (key in object) { value = object[key]; - if (!data.hasOwnProperty(key)) { this.del(key); } + if (!data.hasOwnProperty(key)) { + this.del(key); + } } for (key in data) { value = data[key]; - if (PREFERENCE_KEYS.indexOf(key) !== -1) { this.set(key, value); } + if (PREFERENCE_KEYS.indexOf(key) !== -1) { + this.set(key, value); + } } } @@ -172,44 +191,54 @@ } initLayout() { - if (this.get('dark') === 1) { - this.set('theme', 'dark'); - this.del('dark'); + if (this.get("dark") === 1) { + this.set("theme", "dark"); + this.del("dark"); + } + this.setTheme(this.get("theme")); + for (var layout of Array.from(this.LAYOUTS)) { + this.toggleLayout(layout, this.hasLayout(layout)); } - this.setTheme(this.get('theme')); - for (var layout of Array.from(this.LAYOUTS)) { this.toggleLayout(layout, this.hasLayout(layout)); } this.initSidebarWidth(); } setTheme(theme) { - if (theme === 'auto') { - theme = this.darkModeQuery.matches ? 'dark' : 'default'; + if (theme === "auto") { + theme = this.darkModeQuery.matches ? "dark" : "default"; } - const { - classList - } = document.documentElement; - classList.remove('_theme-default', '_theme-dark'); - classList.add('_theme-' + theme); + const { classList } = document.documentElement; + classList.remove("_theme-default", "_theme-dark"); + classList.add("_theme-" + theme); this.updateColorMeta(); } updateColorMeta() { - const color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim(); - $('meta[name=theme-color]').setAttribute('content', color); + const color = getComputedStyle(document.documentElement) + .getPropertyValue("--headerBackground") + .trim(); + $("meta[name=theme-color]").setAttribute("content", color); } toggleLayout(layout, enable) { - const { - classList - } = document.body; + const { classList } = document.body; // sidebar is always shown for settings; its state is updated in app.views.Settings - if ((layout !== '_sidebar-hidden') || !(app.router != null ? app.router.isSettings : undefined)) { classList.toggle(layout, enable); } - classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled()); + if ( + layout !== "_sidebar-hidden" || + !(app.router != null ? app.router.isSettings : undefined) + ) { + classList.toggle(layout, enable); + } + classList.toggle("_overlay-scrollbars", $.overlayScrollbarsEnabled()); } initSidebarWidth() { - const size = this.get('size'); - if (size) { document.documentElement.style.setProperty('--sidebarWidth', size + 'px'); } + const size = this.get("size"); + if (size) { + document.documentElement.style.setProperty( + "--sidebarWidth", + size + "px", + ); + } } }); Cls.initClass(); @@ -217,5 +246,7 @@ })(); function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== "undefined" && value !== null + ? transform(value) + : undefined; +} diff --git a/assets/javascripts/app/shortcuts.js b/assets/javascripts/app/shortcuts.js index 02e8170e..785f554a 100644 --- a/assets/javascripts/app/shortcuts.js +++ b/assets/javascripts/app/shortcuts.js @@ -21,111 +21,151 @@ const Cls = (app.Shortcuts = class Shortcuts { } start() { - $.on(document, 'keydown', this.onKeydown); - $.on(document, 'keypress', this.onKeypress); + $.on(document, "keydown", this.onKeydown); + $.on(document, "keypress", this.onKeypress); } stop() { - $.off(document, 'keydown', this.onKeydown); - $.off(document, 'keypress', this.onKeypress); + $.off(document, "keydown", this.onKeydown); + $.off(document, "keypress", this.onKeypress); } swapArrowKeysBehavior() { - return app.settings.get('arrowScroll'); + return app.settings.get("arrowScroll"); } spaceScroll() { - return app.settings.get('spaceScroll'); + return app.settings.get("spaceScroll"); } showTip() { - app.showTip('KeyNav'); - return this.showTip = null; + app.showTip("KeyNav"); + return (this.showTip = null); } spaceTimeout() { - return app.settings.get('spaceTimeout'); + return app.settings.get("spaceTimeout"); } onKeydown(event) { - if (this.buggyEvent(event)) { return; } + if (this.buggyEvent(event)) { + return; + } const result = (() => { if (event.ctrlKey || event.metaKey) { - if (!event.altKey && !event.shiftKey) { return this.handleKeydownSuperEvent(event); } - } else if (event.shiftKey) { - if (!event.altKey) { return this.handleKeydownShiftEvent(event); } - } else if (event.altKey) { - return this.handleKeydownAltEvent(event); - } else { - return this.handleKeydownEvent(event); - } + if (!event.altKey && !event.shiftKey) { + return this.handleKeydownSuperEvent(event); + } + } else if (event.shiftKey) { + if (!event.altKey) { + return this.handleKeydownShiftEvent(event); + } + } else if (event.altKey) { + return this.handleKeydownAltEvent(event); + } else { + return this.handleKeydownEvent(event); + } })(); - if (result === false) { event.preventDefault(); } + if (result === false) { + event.preventDefault(); + } } onKeypress(event) { - if (this.buggyEvent(event) || ((event.charCode === 63) && (document.activeElement.tagName === 'INPUT'))) { return; } + if ( + this.buggyEvent(event) || + (event.charCode === 63 && document.activeElement.tagName === "INPUT") + ) { + return; + } if (!event.ctrlKey && !event.metaKey) { const result = this.handleKeypressEvent(event); - if (result === false) { event.preventDefault(); } + if (result === false) { + event.preventDefault(); + } } } handleKeydownEvent(event, _force) { - if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownAltEvent(event, true); } + if ( + !_force && + [37, 38, 39, 40].includes(event.which) && + this.swapArrowKeysBehavior() + ) { + return this.handleKeydownAltEvent(event, true); + } - if (!event.target.form && ((48 <= event.which && event.which <= 57) || (65 <= event.which && event.which <= 90))) { - this.trigger('typing'); + if ( + !event.target.form && + ((48 <= event.which && event.which <= 57) || + (65 <= event.which && event.which <= 90)) + ) { + this.trigger("typing"); return; } switch (event.which) { case 8: - if (!event.target.form) { return this.trigger('typing'); } + if (!event.target.form) { + return this.trigger("typing"); + } break; case 13: - return this.trigger('enter'); + return this.trigger("enter"); case 27: - this.trigger('escape'); + this.trigger("escape"); return false; case 32: - if ((event.target.type === 'search') && this.spaceScroll() && (!this.lastKeypress || (this.lastKeypress < (Date.now() - (this.spaceTimeout() * 1000))))) { - this.trigger('pageDown'); + if ( + event.target.type === "search" && + this.spaceScroll() && + (!this.lastKeypress || + this.lastKeypress < Date.now() - this.spaceTimeout() * 1000) + ) { + this.trigger("pageDown"); return false; } break; case 33: - return this.trigger('pageUp'); + return this.trigger("pageUp"); case 34: - return this.trigger('pageDown'); + return this.trigger("pageDown"); case 35: - if (!event.target.form) { return this.trigger('pageBottom'); } + if (!event.target.form) { + return this.trigger("pageBottom"); + } break; case 36: - if (!event.target.form) { return this.trigger('pageTop'); } + if (!event.target.form) { + return this.trigger("pageTop"); + } break; case 37: - if (!event.target.value) { return this.trigger('left'); } + if (!event.target.value) { + return this.trigger("left"); + } break; case 38: - this.trigger('up'); - if (typeof this.showTip === 'function') { + this.trigger("up"); + if (typeof this.showTip === "function") { this.showTip(); } return false; case 39: - if (!event.target.value) { return this.trigger('right'); } + if (!event.target.value) { + return this.trigger("right"); + } break; case 40: - this.trigger('down'); - if (typeof this.showTip === 'function') { + this.trigger("down"); + if (typeof this.showTip === "function") { this.showTip(); } return false; case 191: if (!event.target.form) { - this.trigger('typing'); + this.trigger("typing"); return false; } break; @@ -135,52 +175,58 @@ const Cls = (app.Shortcuts = class Shortcuts { handleKeydownSuperEvent(event) { switch (event.which) { case 13: - return this.trigger('superEnter'); + return this.trigger("superEnter"); case 37: if (this.isMac) { - this.trigger('superLeft'); + this.trigger("superLeft"); return false; } break; case 38: - this.trigger('pageTop'); + this.trigger("pageTop"); return false; case 39: if (this.isMac) { - this.trigger('superRight'); + this.trigger("superRight"); return false; } break; case 40: - this.trigger('pageBottom'); + this.trigger("pageBottom"); return false; case 188: - this.trigger('preferences'); + this.trigger("preferences"); return false; } } handleKeydownShiftEvent(event, _force) { - if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } + if ( + !_force && + [37, 38, 39, 40].includes(event.which) && + this.swapArrowKeysBehavior() + ) { + return this.handleKeydownEvent(event, true); + } - if (!event.target.form && (65 <= event.which && event.which <= 90)) { - this.trigger('typing'); + if (!event.target.form && 65 <= event.which && event.which <= 90) { + this.trigger("typing"); return; } switch (event.which) { case 32: - this.trigger('pageUp'); + this.trigger("pageUp"); return false; case 38: - if (!__guard__(getSelection(), x => x.toString())) { - this.trigger('altUp'); + if (!__guard__(getSelection(), (x) => x.toString())) { + this.trigger("altUp"); return false; } break; case 40: - if (!__guard__(getSelection(), x1 => x1.toString())) { - this.trigger('altDown'); + if (!__guard__(getSelection(), (x1) => x1.toString())) { + this.trigger("altDown"); return false; } break; @@ -188,58 +234,64 @@ const Cls = (app.Shortcuts = class Shortcuts { } handleKeydownAltEvent(event, _force) { - if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } + if ( + !_force && + [37, 38, 39, 40].includes(event.which) && + this.swapArrowKeysBehavior() + ) { + return this.handleKeydownEvent(event, true); + } switch (event.which) { case 9: - return this.trigger('altRight', event); + return this.trigger("altRight", event); case 37: if (!this.isMac) { - this.trigger('superLeft'); + this.trigger("superLeft"); return false; } break; case 38: - this.trigger('altUp'); + this.trigger("altUp"); return false; case 39: if (!this.isMac) { - this.trigger('superRight'); + this.trigger("superRight"); return false; } break; case 40: - this.trigger('altDown'); + this.trigger("altDown"); return false; case 67: - this.trigger('altC'); + this.trigger("altC"); return false; case 68: - this.trigger('altD'); + this.trigger("altD"); return false; case 70: - return this.trigger('altF', event); + return this.trigger("altF", event); case 71: - this.trigger('altG'); + this.trigger("altG"); return false; case 79: - this.trigger('altO'); + this.trigger("altO"); return false; case 82: - this.trigger('altR'); + this.trigger("altR"); return false; case 83: - this.trigger('altS'); + this.trigger("altS"); return false; } } handleKeypressEvent(event) { - if ((event.which === 63) && !event.target.value) { - this.trigger('help'); + if (event.which === 63 && !event.target.value) { + this.trigger("help"); return false; } else { - return this.lastKeypress = Date.now(); + return (this.lastKeypress = Date.now()); } } @@ -257,5 +309,7 @@ const Cls = (app.Shortcuts = class Shortcuts { Cls.initClass(); function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== "undefined" && value !== null + ? transform(value) + : undefined; +} diff --git a/assets/javascripts/app/update_checker.js b/assets/javascripts/app/update_checker.js index ab3e0022..5d967539 100644 --- a/assets/javascripts/app/update_checker.js +++ b/assets/javascripts/app/update_checker.js @@ -11,9 +11,9 @@ app.UpdateChecker = class UpdateChecker { this.onFocus = this.onFocus.bind(this); this.lastCheck = Date.now(); - $.on(window, 'focus', this.onFocus); + $.on(window, "focus", this.onFocus); if (app.serviceWorker != null) { - app.serviceWorker.on('updateready', this.onUpdateReady); + app.serviceWorker.on("updateready", this.onUpdateReady); } setTimeout(this.checkDocs, 0); @@ -24,31 +24,39 @@ app.UpdateChecker = class UpdateChecker { app.serviceWorker.update(); } else { ajax({ - url: $('script[src*="application"]').getAttribute('src'), - dataType: 'application/javascript', - error: (_, xhr) => { if (xhr.status === 404) { return this.onUpdateReady(); } } + url: $('script[src*="application"]').getAttribute("src"), + dataType: "application/javascript", + error: (_, xhr) => { + if (xhr.status === 404) { + return this.onUpdateReady(); + } + }, }); } } onUpdateReady() { - new app.views.Notif('UpdateReady', {autoHide: null}); + new app.views.Notif("UpdateReady", { autoHide: null }); } checkDocs() { - if (!app.settings.get('manualUpdate')) { + if (!app.settings.get("manualUpdate")) { app.docs.updateInBackground(); } else { - app.docs.checkForUpdates(i => { if (i > 0) { return this.onDocsUpdateReady(); } }); + app.docs.checkForUpdates((i) => { + if (i > 0) { + return this.onDocsUpdateReady(); + } + }); } } onDocsUpdateReady() { - new app.views.Notif('UpdateDocs', {autoHide: null}); + new app.views.Notif("UpdateDocs", { autoHide: null }); } onFocus() { - if ((Date.now() - this.lastCheck) > 21600e3) { + if (Date.now() - this.lastCheck > 21600e3) { this.lastCheck = Date.now(); this.check(); } diff --git a/assets/javascripts/application.js b/assets/javascripts/application.js index 4a0eeb1d..81995783 100644 --- a/assets/javascripts/application.js +++ b/assets/javascripts/application.js @@ -27,8 +27,8 @@ //= require tracking -var init = function() { - document.removeEventListener('DOMContentLoaded', init, false); +var init = function () { + document.removeEventListener("DOMContentLoaded", init, false); if (document.body) { return app.init(); @@ -37,4 +37,4 @@ var init = function() { } }; -document.addEventListener('DOMContentLoaded', init, false); +document.addEventListener("DOMContentLoaded", init, false); diff --git a/assets/javascripts/collections/collection.js b/assets/javascripts/collections/collection.js index d34edf96..fedaf358 100644 --- a/assets/javascripts/collections/collection.js +++ b/assets/javascripts/collections/collection.js @@ -9,7 +9,9 @@ */ app.Collection = class Collection { constructor(objects) { - if (objects == null) { objects = []; } + if (objects == null) { + objects = []; + } this.reset(objects); } @@ -18,16 +20,22 @@ app.Collection = class Collection { } reset(objects) { - if (objects == null) { objects = []; } + if (objects == null) { + objects = []; + } this.models = []; - for (var object of Array.from(objects)) { this.add(object); } + for (var object of Array.from(objects)) { + this.add(object); + } } add(object) { if (object instanceof app.Model) { this.models.push(object); } else if (object instanceof Array) { - for (var obj of Array.from(object)) { this.add(obj); } + for (var obj of Array.from(object)) { + this.add(obj); + } } else if (object instanceof app.Collection) { this.models.push(...Array.from(object.all() || [])); } else { @@ -48,7 +56,9 @@ app.Collection = class Collection { } each(fn) { - for (var model of Array.from(this.models)) { fn(model); } + for (var model of Array.from(this.models)) { + fn(model); + } } all() { @@ -61,7 +71,9 @@ app.Collection = class Collection { findBy(attr, value) { for (var model of Array.from(this.models)) { - if (model[attr] === value) { return model; } + if (model[attr] === value) { + return model; + } } } @@ -71,7 +83,11 @@ app.Collection = class Collection { countAllBy(attr, value) { let i = 0; - for (var model of Array.from(this.models)) { if (model[attr] === value) { i += 1; } } + for (var model of Array.from(this.models)) { + if (model[attr] === value) { + i += 1; + } + } return i; } }; diff --git a/assets/javascripts/collections/docs.js b/assets/javascripts/collections/docs.js index 500ab828..09ab78fa 100644 --- a/assets/javascripts/collections/docs.js +++ b/assets/javascripts/collections/docs.js @@ -8,29 +8,35 @@ * DS206: Consider reworking classes to avoid initClass * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -(function() { +(function () { let NORMALIZE_VERSION_RGX = undefined; let NORMALIZE_VERSION_SUB = undefined; let CONCURRENCY = undefined; const Cls = (app.collections.Docs = class Docs extends app.Collection { static initClass() { - this.model = 'Doc'; - + this.model = "Doc"; + NORMALIZE_VERSION_RGX = /\.(\d)$/; - NORMALIZE_VERSION_SUB = '.0$1'; - + NORMALIZE_VERSION_SUB = ".0$1"; + // Load models concurrently. // It's not pretty but I didn't want to import a promise library only for this. CONCURRENCY = 3; } findBySlug(slug) { - return this.findBy('slug', slug) || this.findBy('slug_without_version', slug); + return ( + this.findBy("slug", slug) || this.findBy("slug_without_version", slug) + ); } sort() { - return this.models.sort(function(a, b) { + return this.models.sort(function (a, b) { if (a.name === b.name) { - if (!a.version || (a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB))) { + if ( + !a.version || + a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > + b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) + ) { return -1; } else { return 1; @@ -48,13 +54,13 @@ var next = () => { if (i < this.models.length) { this.models[i].load(next, fail, options); - } else if (i === ((this.models.length + CONCURRENCY) - 1)) { + } else if (i === this.models.length + CONCURRENCY - 1) { onComplete(); } i++; }; - var fail = function(...args) { + var fail = function (...args) { if (onError) { onError(...Array.from(args || [])); onError = null; @@ -62,11 +68,19 @@ next(); }; - for (let j = 0, end = CONCURRENCY, asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { next(); } + for ( + let j = 0, end = CONCURRENCY, asc = 0 <= end; + asc ? j < end : j > end; + asc ? j++ : j-- + ) { + next(); + } } clearCache() { - for (var doc of Array.from(this.models)) { doc.clearCache(); } + for (var doc of Array.from(this.models)) { + doc.clearCache(); + } } uninstall(callback) { @@ -82,11 +96,11 @@ } getInstallStatuses(callback) { - app.db.versions(this.models, function(statuses) { + app.db.versions(this.models, function (statuses) { if (statuses) { for (var key in statuses) { var value = statuses[key]; - statuses[key] = {installed: !!value, mtime: value}; + statuses[key] = { installed: !!value, mtime: value }; } } callback(statuses); @@ -94,22 +108,31 @@ } checkForUpdates(callback) { - this.getInstallStatuses(statuses => { + this.getInstallStatuses((statuses) => { let i = 0; if (statuses) { - for (var slug in statuses) { var status = statuses[slug]; if (this.findBy('slug', slug).isOutdated(status)) { i += 1; } } + for (var slug in statuses) { + var status = statuses[slug]; + if (this.findBy("slug", slug).isOutdated(status)) { + i += 1; + } + } } callback(i); }); } updateInBackground() { - this.getInstallStatuses(statuses => { - if (!statuses) { return; } + this.getInstallStatuses((statuses) => { + if (!statuses) { + return; + } for (var slug in statuses) { var status = statuses[slug]; - var doc = this.findBy('slug', slug); - if (doc.isOutdated(status)) { doc.install($.noop, $.noop); } + var doc = this.findBy("slug", slug); + if (doc.isOutdated(status)) { + doc.install($.noop, $.noop); + } } }); } diff --git a/assets/javascripts/collections/entries.js b/assets/javascripts/collections/entries.js index fd547d39..73240311 100644 --- a/assets/javascripts/collections/entries.js +++ b/assets/javascripts/collections/entries.js @@ -7,7 +7,7 @@ */ const Cls = (app.collections.Entries = class Entries extends app.Collection { static initClass() { - this.model = 'Entry'; + this.model = "Entry"; } }); Cls.initClass(); diff --git a/assets/javascripts/collections/types.js b/assets/javascripts/collections/types.js index 35cf204a..cb8d153b 100644 --- a/assets/javascripts/collections/types.js +++ b/assets/javascripts/collections/types.js @@ -8,14 +8,15 @@ * DS206: Consider reworking classes to avoid initClass * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -(function() { +(function () { let GUIDES_RGX = undefined; let APPENDIX_RGX = undefined; const Cls = (app.collections.Types = class Types extends app.Collection { static initClass() { - this.model = 'Type'; - - GUIDES_RGX = /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i; + this.model = "Type"; + + GUIDES_RGX = + /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i; APPENDIX_RGX = /appendix/i; } @@ -23,9 +24,11 @@ const result = []; for (var type of Array.from(this.models)) { var name; - (result[name = this._groupFor(type)] || (result[name] = [])).push(type); + (result[(name = this._groupFor(type))] || (result[name] = [])).push( + type, + ); } - return result.filter(e => e.length > 0); + return result.filter((e) => e.length > 0); } _groupFor(type) { diff --git a/assets/javascripts/debug.js b/assets/javascripts/debug.js index 89699e8a..91765b57 100644 --- a/assets/javascripts/debug.js +++ b/assets/javascripts/debug.js @@ -9,26 +9,33 @@ * DS209: Avoid top-level return * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -if (!(typeof console !== 'undefined' && console !== null ? console.time : undefined) || !console.groupCollapsed) { return; } +if ( + !(typeof console !== "undefined" && console !== null + ? console.time + : undefined) || + !console.groupCollapsed +) { + return; +} // // App // const _init = app.init; -app.init = function() { - console.time('Init'); +app.init = function () { + console.time("Init"); _init.call(app); - console.timeEnd('Init'); - return console.time('Load'); + console.timeEnd("Init"); + return console.time("Load"); }; const _start = app.start; -app.start = function() { - console.timeEnd('Load'); - console.time('Start'); +app.start = function () { + console.timeEnd("Load"); + console.time("Start"); _start.call(app, ...arguments); - return console.timeEnd('Start'); + return console.timeEnd("Start"); }; // @@ -38,47 +45,50 @@ app.start = function() { const _super = app.Searcher; const _proto = app.Searcher.prototype; -app.Searcher = function() { +app.Searcher = function () { _super.apply(this, arguments); const _setup = this.setup.bind(this); - this.setup = function() { + this.setup = function () { console.groupCollapsed(`Search: ${this.query}`); - console.time('Total'); + console.time("Total"); return _setup(); }; const _match = this.match.bind(this); this.match = () => { - if (this.matcher) { console.timeEnd(this.matcher.name); } + if (this.matcher) { + console.timeEnd(this.matcher.name); + } return _match(); }; const _setupMatcher = this.setupMatcher.bind(this); - this.setupMatcher = function() { + this.setupMatcher = function () { console.time(this.matcher.name); return _setupMatcher(); }; const _end = this.end.bind(this); - this.end = function() { + this.end = function () { console.log(`Results: ${this.totalResults}`); - console.timeEnd('Total'); + console.timeEnd("Total"); console.groupEnd(); return _end(); }; const _kill = this.kill.bind(this); - this.kill = function() { + this.kill = function () { if (this.timeout) { - if (this.matcher) { console.timeEnd(this.matcher.name); } + if (this.matcher) { + console.timeEnd(this.matcher.name); + } console.groupEnd(); - console.timeEnd('Total'); - console.warn('Killed'); + console.timeEnd("Total"); + console.warn("Killed"); } return _kill(); }; - }; $.extend(app.Searcher, _super); @@ -89,23 +99,40 @@ app.Searcher.prototype = _proto; // View tree // -this.viewTree = function(view, level, visited) { - if (view == null) { view = app.document; } - if (level == null) { level = 0; } - if (visited == null) { visited = []; } - if (visited.indexOf(view) >= 0) { return; } +this.viewTree = function (view, level, visited) { + if (view == null) { + view = app.document; + } + if (level == null) { + level = 0; + } + if (visited == null) { + visited = []; + } + if (visited.indexOf(view) >= 0) { + return; + } visited.push(view); - console.log(`%c ${Array(level + 1).join(' ')}${view.constructor.name}: ${!!view.activated}`, - 'color:' + ((view.activated && 'green') || 'red')); + console.log( + `%c ${Array(level + 1).join(" ")}${ + view.constructor.name + }: ${!!view.activated}`, + "color:" + ((view.activated && "green") || "red"), + ); for (var key of Object.keys(view || {})) { var value = view[key]; - if ((key !== 'view') && value) { - if ((typeof value === 'object') && value.setupElement) { + if (key !== "view" && value) { + if (typeof value === "object" && value.setupElement) { this.viewTree(value, level + 1, visited); } else if (value.constructor.toString().match(/Object\(\)/)) { - for (var k of Object.keys(value || {})) { var v = value[k]; if (v && (typeof v === 'object') && v.setupElement) { this.viewTree(v, level + 1, visited); } } + for (var k of Object.keys(value || {})) { + var v = value[k]; + if (v && typeof v === "object" && v.setupElement) { + this.viewTree(v, level + 1, visited); + } + } } } } diff --git a/assets/javascripts/lib/ajax.js b/assets/javascripts/lib/ajax.js index 69ac16d7..20e420eb 100644 --- a/assets/javascripts/lib/ajax.js +++ b/assets/javascripts/lib/ajax.js @@ -9,11 +9,11 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ const MIME_TYPES = { - json: 'application/json', - html: 'text/html' + json: "application/json", + html: "text/html", }; -this.ajax = function(options) { +this.ajax = function (options) { applyDefaults(options); serializeData(options); @@ -26,7 +26,7 @@ this.ajax = function(options) { xhr.send(options.data); if (options.async) { - return {abort: abort.bind(undefined, xhr)}; + return { abort: abort.bind(undefined, xhr) }; } else { return parseResponse(xhr, options); } @@ -34,51 +34,63 @@ this.ajax = function(options) { ajax.defaults = { async: true, - dataType: 'json', + dataType: "json", timeout: 30, - type: 'GET' + type: "GET", }; - // contentType - // context - // data - // error - // headers - // progress - // success - // url - -var applyDefaults = function(options) { +// contentType +// context +// data +// error +// headers +// progress +// success +// url + +var applyDefaults = function (options) { for (var key in ajax.defaults) { - if (options[key] == null) { options[key] = ajax.defaults[key]; } + if (options[key] == null) { + options[key] = ajax.defaults[key]; + } } }; -var serializeData = function(options) { - if (!options.data) { return; } +var serializeData = function (options) { + if (!options.data) { + return; + } - if (options.type === 'GET') { - options.url += '?' + serializeParams(options.data); + if (options.type === "GET") { + options.url += "?" + serializeParams(options.data); options.data = null; } else { options.data = serializeParams(options.data); } }; -var serializeParams = params => ((() => { - const result = []; - for (var key in params) { - var value = params[key]; - result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); - } - return result; -})()).join('&'); +var serializeParams = (params) => + (() => { + const result = []; + for (var key in params) { + var value = params[key]; + result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); + } + return result; + })().join("&"); -var applyCallbacks = function(xhr, options) { - if (!options.async) { return; } +var applyCallbacks = function (xhr, options) { + if (!options.async) { + return; + } - xhr.timer = setTimeout(onTimeout.bind(undefined, xhr, options), options.timeout * 1000); - if (options.progress) { xhr.onprogress = options.progress; } - xhr.onreadystatechange = function() { + xhr.timer = setTimeout( + onTimeout.bind(undefined, xhr, options), + options.timeout * 1000, + ); + if (options.progress) { + xhr.onprogress = options.progress; + } + xhr.onreadystatechange = function () { if (xhr.readyState === 4) { clearTimeout(xhr.timer); onComplete(xhr, options); @@ -86,19 +98,26 @@ var applyCallbacks = function(xhr, options) { }; }; -var applyHeaders = function(xhr, options) { - if (!options.headers) { options.headers = {}; } +var applyHeaders = function (xhr, options) { + if (!options.headers) { + options.headers = {}; + } if (options.contentType) { - options.headers['Content-Type'] = options.contentType; + options.headers["Content-Type"] = options.contentType; } - if (!options.headers['Content-Type'] && options.data && (options.type !== 'GET')) { - options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if ( + !options.headers["Content-Type"] && + options.data && + options.type !== "GET" + ) { + options.headers["Content-Type"] = "application/x-www-form-urlencoded"; } if (options.dataType) { - options.headers['Accept'] = MIME_TYPES[options.dataType] || options.dataType; + options.headers["Accept"] = + MIME_TYPES[options.dataType] || options.dataType; } for (var key in options.headers) { @@ -107,50 +126,52 @@ var applyHeaders = function(xhr, options) { } }; -var onComplete = function(xhr, options) { +var onComplete = function (xhr, options) { if (200 <= xhr.status && xhr.status < 300) { let response; if ((response = parseResponse(xhr, options)) != null) { onSuccess(response, xhr, options); } else { - onError('invalid', xhr, options); + onError("invalid", xhr, options); } } else { - onError('error', xhr, options); + onError("error", xhr, options); } }; -var onSuccess = function(response, xhr, options) { +var onSuccess = function (response, xhr, options) { if (options.success != null) { options.success.call(options.context, response, xhr, options); } }; -var onError = function(type, xhr, options) { +var onError = function (type, xhr, options) { if (options.error != null) { options.error.call(options.context, type, xhr, options); } }; -var onTimeout = function(xhr, options) { +var onTimeout = function (xhr, options) { xhr.abort(); - onError('timeout', xhr, options); + onError("timeout", xhr, options); }; -var abort = function(xhr) { +var abort = function (xhr) { clearTimeout(xhr.timer); xhr.onreadystatechange = null; xhr.abort(); }; -var parseResponse = function(xhr, options) { - if (options.dataType === 'json') { +var parseResponse = function (xhr, options) { + if (options.dataType === "json") { return parseJSON(xhr.responseText); } else { return xhr.responseText; } }; -var parseJSON = function(json) { - try { return JSON.parse(json); } catch (error) {} +var parseJSON = function (json) { + try { + return JSON.parse(json); + } catch (error) {} }; diff --git a/assets/javascripts/lib/cookies_store.js b/assets/javascripts/lib/cookies_store.js index 13e0ac94..80a912cc 100644 --- a/assets/javascripts/lib/cookies_store.js +++ b/assets/javascripts/lib/cookies_store.js @@ -8,14 +8,14 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -(function() { +(function () { let INT = undefined; const Cls = (this.CookiesStore = class CookiesStore { static initClass() { // Intentionally called CookiesStore instead of CookieStore // Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome // Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 - + INT = /^\d+$/; } @@ -23,7 +23,9 @@ get(key) { let value = Cookies.get(key); - if ((value != null) && INT.test(value)) { value = parseInt(value, 10); } + if (value != null && INT.test(value)) { + value = parseInt(value, 10); + } return value; } @@ -33,10 +35,19 @@ return; } - if (value === true) { value = 1; } - if (value && (typeof INT.test === 'function' ? INT.test(value) : undefined)) { value = parseInt(value, 10); } - Cookies.set(key, '' + value, {path: '/', expires: 1e8}); - if (this.get(key) !== value) { this.constructor.onBlocked(key, value, this.get(key)); } + if (value === true) { + value = 1; + } + if ( + value && + (typeof INT.test === "function" ? INT.test(value) : undefined) + ) { + value = parseInt(value, 10); + } + Cookies.set(key, "" + value, { path: "/", expires: 1e8 }); + if (this.get(key) !== value) { + this.constructor.onBlocked(key, value, this.get(key)); + } } del(key) { @@ -46,7 +57,7 @@ reset() { try { for (var cookie of Array.from(document.cookie.split(/;\s?/))) { - Cookies.expire(cookie.split('=')[0]); + Cookies.expire(cookie.split("=")[0]); } return; } catch (error) {} @@ -55,8 +66,8 @@ dump() { const result = {}; for (var cookie of Array.from(document.cookie.split(/;\s?/))) { - if (cookie[0] !== '_') { - cookie = cookie.split('='); + if (cookie[0] !== "_") { + cookie = cookie.split("="); result[cookie[0]] = cookie[1]; } } diff --git a/assets/javascripts/lib/events.js b/assets/javascripts/lib/events.js index a077854c..5c23f8ce 100644 --- a/assets/javascripts/lib/events.js +++ b/assets/javascripts/lib/events.js @@ -11,22 +11,38 @@ */ this.Events = { on(event, callback) { - if (event.indexOf(' ') >= 0) { - for (var name of Array.from(event.split(' '))) { this.on(name, callback); } + if (event.indexOf(" ") >= 0) { + for (var name of Array.from(event.split(" "))) { + this.on(name, callback); + } } else { let base; - (((base = this._callbacks != null ? this._callbacks : (this._callbacks = {})))[event] != null ? base[event] : (base[event] = [])).push(callback); + ((base = + this._callbacks != null ? this._callbacks : (this._callbacks = {}))[ + event + ] != null + ? base[event] + : (base[event] = []) + ).push(callback); } return this; }, off(event, callback) { let callbacks, index; - if (event.indexOf(' ') >= 0) { - for (var name of Array.from(event.split(' '))) { this.off(name, callback); } - } else if ((callbacks = this._callbacks != null ? this._callbacks[event] : undefined) && ((index = callbacks.indexOf(callback)) >= 0)) { + if (event.indexOf(" ") >= 0) { + for (var name of Array.from(event.split(" "))) { + this.off(name, callback); + } + } else if ( + (callbacks = + this._callbacks != null ? this._callbacks[event] : undefined) && + (index = callbacks.indexOf(callback)) >= 0 + ) { callbacks.splice(index, 1); - if (!callbacks.length) { delete this._callbacks[event]; } + if (!callbacks.length) { + delete this._callbacks[event]; + } } return this; }, @@ -34,20 +50,28 @@ this.Events = { trigger(event, ...args) { let callbacks; this.eventInProgress = { name: event, args }; - if (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) { - for (var callback of Array.from(callbacks.slice(0))) { if (typeof callback === 'function') { - callback(...Array.from(args || [])); - } } + if ( + (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) + ) { + for (var callback of Array.from(callbacks.slice(0))) { + if (typeof callback === "function") { + callback(...Array.from(args || [])); + } + } } this.eventInProgress = null; - if (event !== 'all') { this.trigger('all', event, ...Array.from(args)); } + if (event !== "all") { + this.trigger("all", event, ...Array.from(args)); + } return this; }, removeEvent(event) { if (this._callbacks != null) { - for (var name of Array.from(event.split(' '))) { delete this._callbacks[name]; } + for (var name of Array.from(event.split(" "))) { + delete this._callbacks[name]; + } } return this; - } + }, }; diff --git a/assets/javascripts/lib/favicon.js b/assets/javascripts/lib/favicon.js index 5c04fe02..b4a7a43b 100644 --- a/assets/javascripts/lib/favicon.js +++ b/assets/javascripts/lib/favicon.js @@ -12,22 +12,24 @@ let currentSlug = null; const imageCache = {}; const urlCache = {}; -const withImage = function(url, action) { +const withImage = function (url, action) { if (imageCache[url]) { return action(imageCache[url]); } else { const img = new Image(); - img.crossOrigin = 'anonymous'; + img.crossOrigin = "anonymous"; img.src = url; - return img.onload = () => { + return (img.onload = () => { imageCache[url] = img; return action(img); - }; + }); } }; -this.setFaviconForDoc = function(doc) { - if (currentSlug === doc.slug) { return; } +this.setFaviconForDoc = function (doc) { + if (currentSlug === doc.slug) { + return; + } const favicon = $('link[rel="icon"]'); @@ -41,51 +43,67 @@ this.setFaviconForDoc = function(doc) { return; } - const iconEl = $(`._icon-${doc.slug.split('~')[0]}`); - if (iconEl === null) { return; } + const iconEl = $(`._icon-${doc.slug.split("~")[0]}`); + if (iconEl === null) { + return; + } - const styles = window.getComputedStyle(iconEl, ':before'); + const styles = window.getComputedStyle(iconEl, ":before"); - const backgroundPositionX = styles['background-position-x']; - const backgroundPositionY = styles['background-position-y']; - if ((backgroundPositionX === undefined) || (backgroundPositionY === undefined)) { return; } + const backgroundPositionX = styles["background-position-x"]; + const backgroundPositionY = styles["background-position-y"]; + if (backgroundPositionX === undefined || backgroundPositionY === undefined) { + return; + } const bgUrl = app.config.favicon_spritesheet; const sourceSize = 16; const sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2))); const sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2))); - return withImage(bgUrl, docImg => withImage(defaultUrl, function(defaultImg) { - const size = defaultImg.width; - - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - - canvas.width = size; - canvas.height = size; - ctx.drawImage(defaultImg, 0, 0); - - const docIconPercentage = 65; - const destinationCoords = (size / 100) * (100 - docIconPercentage); - const destinationSize = (size / 100) * docIconPercentage; - - ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize); - - try { - urlCache[doc.slug] = canvas.toDataURL(); - favicon.href = urlCache[doc.slug]; - - return currentSlug = doc.slug; - } catch (error) { - Raven.captureException(error, { level: 'info' }); - return this.resetFavicon(); - } - })); + return withImage(bgUrl, (docImg) => + withImage(defaultUrl, function (defaultImg) { + const size = defaultImg.width; + + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + + canvas.width = size; + canvas.height = size; + ctx.drawImage(defaultImg, 0, 0); + + const docIconPercentage = 65; + const destinationCoords = (size / 100) * (100 - docIconPercentage); + const destinationSize = (size / 100) * docIconPercentage; + + ctx.drawImage( + docImg, + sourceX, + sourceY, + sourceSize, + sourceSize, + destinationCoords, + destinationCoords, + destinationSize, + destinationSize, + ); + + try { + urlCache[doc.slug] = canvas.toDataURL(); + favicon.href = urlCache[doc.slug]; + + return (currentSlug = doc.slug); + } catch (error) { + Raven.captureException(error, { level: "info" }); + return this.resetFavicon(); + } + }), + ); }; -this.resetFavicon = function() { - if ((defaultUrl !== null) && (currentSlug !== null)) { +this.resetFavicon = function () { + if (defaultUrl !== null && currentSlug !== null) { $('link[rel="icon"]').href = defaultUrl; - return currentSlug = null; + return (currentSlug = null); } }; diff --git a/assets/javascripts/lib/license.js b/assets/javascripts/lib/license.js index 246df7d8..7379e3a8 100644 --- a/assets/javascripts/lib/license.js +++ b/assets/javascripts/lib/license.js @@ -6,4 +6,4 @@ * This source code is licensed under the terms of the Mozilla * Public License, v. 2.0, a copy of which may be obtained at: * http://mozilla.org/MPL/2.0/ -*/ + */ diff --git a/assets/javascripts/lib/page.js b/assets/javascripts/lib/page.js index 1459b44e..ba8d19c4 100644 --- a/assets/javascripts/lib/page.js +++ b/assets/javascripts/lib/page.js @@ -13,50 +13,54 @@ * Based on github.com/visionmedia/page.js * Licensed under the MIT license * Copyright 2012 TJ Holowaychuk -*/ + */ let running = false; let currentState = null; const callbacks = []; -this.page = function(value, fn) { - if (typeof value === 'function') { - page('*', value); - } else if (typeof fn === 'function') { +this.page = function (value, fn) { + if (typeof value === "function") { + page("*", value); + } else if (typeof fn === "function") { const route = new Route(value); callbacks.push(route.middleware(fn)); - } else if (typeof value === 'string') { + } else if (typeof value === "string") { page.show(value, fn); } else { page.start(value); } }; -page.start = function(options) { - if (options == null) { options = {}; } +page.start = function (options) { + if (options == null) { + options = {}; + } if (!running) { running = true; - addEventListener('popstate', onpopstate); - addEventListener('click', onclick); + addEventListener("popstate", onpopstate); + addEventListener("click", onclick); page.replace(currentPath(), null, null, true); } }; -page.stop = function() { +page.stop = function () { if (running) { running = false; - removeEventListener('click', onclick); - removeEventListener('popstate', onpopstate); + removeEventListener("click", onclick); + removeEventListener("popstate", onpopstate); } }; -page.show = function(path, state) { +page.show = function (path, state) { let res; - if (path === (currentState != null ? currentState.path : undefined)) { return; } + if (path === (currentState != null ? currentState.path : undefined)) { + return; + } const context = new Context(path, state); const previousState = currentState; currentState = context.state; - if (res = page.dispatch(context)) { + if ((res = page.dispatch(context))) { currentState = previousState; location.assign(res); } else { @@ -67,12 +71,14 @@ page.show = function(path, state) { return context; }; -page.replace = function(path, state, skipDispatch, init) { +page.replace = function (path, state, skipDispatch, init) { let result; let context = new Context(path, state || currentState); context.init = init; currentState = context.state; - if (!skipDispatch) { result = page.dispatch(context); } + if (!skipDispatch) { + result = page.dispatch(context); + } if (result) { context = new Context(result); context.init = init; @@ -81,15 +87,19 @@ page.replace = function(path, state, skipDispatch, init) { } context.replaceState(); updateCanonicalLink(); - if (!skipDispatch) { track(); } + if (!skipDispatch) { + track(); + } return context; }; -page.dispatch = function(context) { +page.dispatch = function (context) { let i = 0; - var next = function() { + var next = function () { let fn, res; - if (fn = callbacks[i++]) { res = fn(context, next); } + if ((fn = callbacks[i++])) { + res = fn(context, next); + } return res; }; return next(); @@ -113,11 +123,11 @@ class Context { } static isLastState(state) { - return state.id === (this.stateId - 1); + return state.id === this.stateId - 1; } static isInitialPopState(state) { - return (state.path === this.initialPath) && (this.stateId === 1); + return state.path === this.initialPath && this.stateId === 1; } static isSameSession(state) { @@ -125,27 +135,40 @@ class Context { } constructor(path, state) { - if (path == null) { path = '/'; } + if (path == null) { + path = "/"; + } this.path = path; - if (state == null) { state = {}; } + if (state == null) { + state = {}; + } this.state = state; - this.pathname = this.path.replace(/(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) => { - this.query = query; - this.hash = hash; - return ''; - }); - - if (this.state.id == null) { this.state.id = this.constructor.stateId++; } - if (this.state.sessionId == null) { this.state.sessionId = this.constructor.sessionId; } + this.pathname = this.path.replace( + /(?:\?([^#]*))?(?:#(.*))?$/, + (_, query, hash) => { + this.query = query; + this.hash = hash; + return ""; + }, + ); + + if (this.state.id == null) { + this.state.id = this.constructor.stateId++; + } + if (this.state.sessionId == null) { + this.state.sessionId = this.constructor.sessionId; + } this.state.path = this.path; } pushState() { - history.pushState(this.state, '', this.path); + history.pushState(this.state, "", this.path); } replaceState() { - try { history.replaceState(this.state, '', this.path); } catch (error) {} // NS_ERROR_FAILURE in Firefox + try { + history.replaceState(this.state, "", this.path); + } catch (error) {} // NS_ERROR_FAILURE in Firefox } } Context.initClass(); @@ -153,7 +176,9 @@ Context.initClass(); class Route { constructor(path, options) { this.path = path; - if (options == null) { options = {}; } + if (options == null) { + options = {}; + } this.keys = []; this.regexp = pathtoRegexp(this.path, this.keys); } @@ -172,13 +197,17 @@ class Route { match(path, params) { let matchData; - if (!(matchData = this.regexp.exec(path))) { return; } + if (!(matchData = this.regexp.exec(path))) { + return; + } const iterable = matchData.slice(1); for (let i = 0; i < iterable.length; i++) { var key; var value = iterable[i]; - if (typeof value === 'string') { value = decodeURIComponent(value); } + if (typeof value === "string") { + value = decodeURIComponent(value); + } if ((key = this.keys[i])) { params[key.name] = value; } else { @@ -189,32 +218,50 @@ class Route { } } -var pathtoRegexp = function(path, keys) { - if (path instanceof RegExp) { return path; } +var pathtoRegexp = function (path, keys) { + if (path instanceof RegExp) { + return path; + } - if (path instanceof Array) { path = `(${path.join('|')})`; } + if (path instanceof Array) { + path = `(${path.join("|")})`; + } path = path - .replace(/\/\(/g, '(?:/') - .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) { - if (slash == null) { slash = ''; } - if (format == null) { format = ''; } - keys.push({name: key, optional: !!optional}); - let str = optional ? '' : slash; - str += '(?:'; - if (optional) { str += slash; } - str += format; - str += capture || (format ? '([^/.]+?)' : '([^/]+?)'); - str += ')'; - if (optional) { str += optional; } - return str; - }).replace(/([\/.])/g, '\\$1') - .replace(/\*/g, '(.*)'); + .replace(/\/\(/g, "(?:/") + .replace( + /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, + function (_, slash, format, key, capture, optional) { + if (slash == null) { + slash = ""; + } + if (format == null) { + format = ""; + } + keys.push({ name: key, optional: !!optional }); + let str = optional ? "" : slash; + str += "(?:"; + if (optional) { + str += slash; + } + str += format; + str += capture || (format ? "([^/.]+?)" : "([^/]+?)"); + str += ")"; + if (optional) { + str += optional; + } + return str; + }, + ) + .replace(/([\/.])/g, "\\$1") + .replace(/\*/g, "(.*)"); return new RegExp(`^${path}$`); }; -var onpopstate = function(event) { - if (!event.state || Context.isInitialPopState(event.state)) { return; } +var onpopstate = function (event) { + if (!event.state || Context.isInitialPopState(event.state)) { + return; + } if (Context.isSameSession(event.state)) { page.replace(event.state.path, event.state); @@ -223,59 +270,83 @@ var onpopstate = function(event) { } }; -var onclick = function(event) { +var onclick = function (event) { try { - if ((event.which !== 1) || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { return; } + if ( + event.which !== 1 || + event.metaKey || + event.ctrlKey || + event.shiftKey || + event.defaultPrevented + ) { + return; + } } catch (error) { return; } let link = $.eventTarget(event); - while (link && (link.tagName !== 'A')) { link = link.parentNode; } + while (link && link.tagName !== "A") { + link = link.parentNode; + } if (link && !link.target && isSameOrigin(link.href)) { event.preventDefault(); let path = link.pathname + link.search + link.hash; - path = path.replace(/^\/\/+/, '/'); // IE11 bug + path = path.replace(/^\/\/+/, "/"); // IE11 bug page.show(path); } }; -var isSameOrigin = url => url.indexOf(`${location.protocol}//${location.hostname}`) === 0; +var isSameOrigin = (url) => + url.indexOf(`${location.protocol}//${location.hostname}`) === 0; -var updateCanonicalLink = function() { - if (!this.canonicalLink) { this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); } - return this.canonicalLink.setAttribute('href', `https://${location.host}${location.pathname}`); +var updateCanonicalLink = function () { + if (!this.canonicalLink) { + this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); + } + return this.canonicalLink.setAttribute( + "href", + `https://${location.host}${location.pathname}`, + ); }; const trackers = []; -page.track = function(fn) { +page.track = function (fn) { trackers.push(fn); }; -var track = function() { - if (app.config.env !== 'production') { return; } - if (navigator.doNotTrack === '1') { return; } - if (navigator.globalPrivacyControl) { return; } +var track = function () { + if (app.config.env !== "production") { + return; + } + if (navigator.doNotTrack === "1") { + return; + } + if (navigator.globalPrivacyControl) { + return; + } - const consentGiven = Cookies.get('analyticsConsent'); - const consentAsked = Cookies.get('analyticsConsentAsked'); + const consentGiven = Cookies.get("analyticsConsent"); + const consentAsked = Cookies.get("analyticsConsentAsked"); - if (consentGiven === '1') { - for (var tracker of Array.from(trackers)) { tracker.call(); } - } else if ((consentGiven === undefined) && (consentAsked === undefined)) { + if (consentGiven === "1") { + for (var tracker of Array.from(trackers)) { + tracker.call(); + } + } else if (consentGiven === undefined && consentAsked === undefined) { // Only ask for consent once per browser session - Cookies.set('analyticsConsentAsked', '1'); + Cookies.set("analyticsConsentAsked", "1"); - new app.views.Notif('AnalyticsConsent', {autoHide: null}); + new app.views.Notif("AnalyticsConsent", { autoHide: null }); } }; -this.resetAnalytics = function() { +this.resetAnalytics = function () { for (var cookie of Array.from(document.cookie.split(/;\s?/))) { - var name = cookie.split('=')[0]; - if ((name[0] === '_') && (name[1] !== '_')) { + var name = cookie.split("=")[0]; + if (name[0] === "_" && name[1] !== "_") { Cookies.expire(name); } } diff --git a/assets/javascripts/lib/util.js b/assets/javascripts/lib/util.js index dbf24074..fa0a43bf 100644 --- a/assets/javascripts/lib/util.js +++ b/assets/javascripts/lib/util.js @@ -16,32 +16,52 @@ // let smoothDistance, smoothDuration, smoothEnd, smoothStart; -this.$ = function(selector, el) { - if (el == null) { el = document; } - try { return el.querySelector(selector); } catch (error) {} +this.$ = function (selector, el) { + if (el == null) { + el = document; + } + try { + return el.querySelector(selector); + } catch (error) {} }; -this.$$ = function(selector, el) { - if (el == null) { el = document; } - try { return el.querySelectorAll(selector); } catch (error) {} +this.$$ = function (selector, el) { + if (el == null) { + el = document; + } + try { + return el.querySelectorAll(selector); + } catch (error) {} }; -$.id = id => document.getElementById(id); +$.id = (id) => document.getElementById(id); -$.hasChild = function(parent, el) { - if (!parent) { return; } +$.hasChild = function (parent, el) { + if (!parent) { + return; + } while (el) { - if (el === parent) { return true; } - if (el === document.body) { return; } + if (el === parent) { + return true; + } + if (el === document.body) { + return; + } el = el.parentNode; } }; -$.closestLink = function(el, parent) { - if (parent == null) { parent = document.body; } +$.closestLink = function (el, parent) { + if (parent == null) { + parent = document.body; + } while (el) { - if (el.tagName === 'A') { return el; } - if (el === parent) { return; } + if (el.tagName === "A") { + return el; + } + if (el === parent) { + return; + } el = el.parentNode; } }; @@ -50,55 +70,85 @@ $.closestLink = function(el, parent) { // Events // -$.on = function(el, event, callback, useCapture) { - if (useCapture == null) { useCapture = false; } - if (event.indexOf(' ') >= 0) { - for (var name of Array.from(event.split(' '))) { $.on(el, name, callback); } +$.on = function (el, event, callback, useCapture) { + if (useCapture == null) { + useCapture = false; + } + if (event.indexOf(" ") >= 0) { + for (var name of Array.from(event.split(" "))) { + $.on(el, name, callback); + } } else { el.addEventListener(event, callback, useCapture); } }; -$.off = function(el, event, callback, useCapture) { - if (useCapture == null) { useCapture = false; } - if (event.indexOf(' ') >= 0) { - for (var name of Array.from(event.split(' '))) { $.off(el, name, callback); } +$.off = function (el, event, callback, useCapture) { + if (useCapture == null) { + useCapture = false; + } + if (event.indexOf(" ") >= 0) { + for (var name of Array.from(event.split(" "))) { + $.off(el, name, callback); + } } else { el.removeEventListener(event, callback, useCapture); } }; -$.trigger = function(el, type, canBubble, cancelable) { - if (canBubble == null) { canBubble = true; } - if (cancelable == null) { cancelable = true; } - const event = document.createEvent('Event'); +$.trigger = function (el, type, canBubble, cancelable) { + if (canBubble == null) { + canBubble = true; + } + if (cancelable == null) { + cancelable = true; + } + const event = document.createEvent("Event"); event.initEvent(type, canBubble, cancelable); el.dispatchEvent(event); }; -$.click = function(el) { - const event = document.createEvent('MouseEvent'); - event.initMouseEvent('click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null); +$.click = function (el) { + const event = document.createEvent("MouseEvent"); + event.initMouseEvent( + "click", + true, + true, + window, + null, + 0, + 0, + 0, + 0, + false, + false, + false, + false, + 0, + null, + ); el.dispatchEvent(event); }; -$.stopEvent = function(event) { +$.stopEvent = function (event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); }; -$.eventTarget = event => event.target.correspondingUseElement || event.target; +$.eventTarget = (event) => event.target.correspondingUseElement || event.target; // // Manipulation // -const buildFragment = function(value) { +const buildFragment = function (value) { const fragment = document.createDocumentFragment(); if ($.isCollection(value)) { - for (var child of Array.from($.makeArray(value))) { fragment.appendChild(child); } + for (var child of Array.from($.makeArray(value))) { + fragment.appendChild(child); + } } else { fragment.innerHTML = value; } @@ -106,36 +156,40 @@ const buildFragment = function(value) { return fragment; }; -$.append = function(el, value) { - if (typeof value === 'string') { - el.insertAdjacentHTML('beforeend', value); +$.append = function (el, value) { + if (typeof value === "string") { + el.insertAdjacentHTML("beforeend", value); } else { - if ($.isCollection(value)) { value = buildFragment(value); } + if ($.isCollection(value)) { + value = buildFragment(value); + } el.appendChild(value); } }; -$.prepend = function(el, value) { +$.prepend = function (el, value) { if (!el.firstChild) { $.append(value); - } else if (typeof value === 'string') { - el.insertAdjacentHTML('afterbegin', value); + } else if (typeof value === "string") { + el.insertAdjacentHTML("afterbegin", value); } else { - if ($.isCollection(value)) { value = buildFragment(value); } + if ($.isCollection(value)) { + value = buildFragment(value); + } el.insertBefore(value, el.firstChild); } }; -$.before = function(el, value) { - if ((typeof value === 'string') || $.isCollection(value)) { +$.before = function (el, value) { + if (typeof value === "string" || $.isCollection(value)) { value = buildFragment(value); } el.parentNode.insertBefore(value, el); }; -$.after = function(el, value) { - if ((typeof value === 'string') || $.isCollection(value)) { +$.after = function (el, value) { + if (typeof value === "string" || $.isCollection(value)) { value = buildFragment(value); } @@ -146,11 +200,13 @@ $.after = function(el, value) { } }; -$.remove = function(value) { +$.remove = function (value) { if ($.isCollection(value)) { - for (var el of Array.from($.makeArray(value))) { if (el.parentNode != null) { - el.parentNode.removeChild(el); - } } + for (var el of Array.from($.makeArray(value))) { + if (el.parentNode != null) { + el.parentNode.removeChild(el); + } + } } else { if (value.parentNode != null) { value.parentNode.removeChild(value); @@ -158,13 +214,15 @@ $.remove = function(value) { } }; -$.empty = function(el) { - while (el.firstChild) { el.removeChild(el.firstChild); } +$.empty = function (el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } }; // Calls the function while the element is off the DOM to avoid triggering // unnecessary reflows and repaints. -$.batchUpdate = function(el, fn) { +$.batchUpdate = function (el, fn) { const parent = el.parentNode; const sibling = el.nextSibling; parent.removeChild(el); @@ -182,14 +240,16 @@ $.batchUpdate = function(el, fn) { // Offset // -$.rect = el => el.getBoundingClientRect(); +$.rect = (el) => el.getBoundingClientRect(); -$.offset = function(el, container) { - if (container == null) { container = document.body; } +$.offset = function (el, container) { + if (container == null) { + container = document.body; + } let top = 0; let left = 0; - while (el && (el !== container)) { + while (el && el !== container) { top += el.offsetTop; left += el.offsetLeft; el = el.offsetParent; @@ -197,102 +257,131 @@ $.offset = function(el, container) { return { top, - left + left, }; }; -$.scrollParent = function(el) { - while ((el = el.parentNode) && (el.nodeType === 1)) { +$.scrollParent = function (el) { + while ((el = el.parentNode) && el.nodeType === 1) { var needle; - if (el.scrollTop > 0) { break; } - if ((needle = __guard__(getComputedStyle(el), x => x.overflowY), ['auto', 'scroll'].includes(needle))) { break; } + if (el.scrollTop > 0) { + break; + } + if ( + ((needle = __guard__(getComputedStyle(el), (x) => x.overflowY)), + ["auto", "scroll"].includes(needle)) + ) { + break; + } } return el; }; -$.scrollTo = function(el, parent, position, options) { - if (position == null) { position = 'center'; } - if (options == null) { options = {}; } - if (!el) { return; } +$.scrollTo = function (el, parent, position, options) { + if (position == null) { + position = "center"; + } + if (options == null) { + options = {}; + } + if (!el) { + return; + } - if (parent == null) { parent = $.scrollParent(el); } - if (!parent) { return; } + if (parent == null) { + parent = $.scrollParent(el); + } + if (!parent) { + return; + } const parentHeight = parent.clientHeight; const parentScrollHeight = parent.scrollHeight; - if (!(parentScrollHeight > parentHeight)) { return; } + if (!(parentScrollHeight > parentHeight)) { + return; + } - const { - top - } = $.offset(el, parent); - const { - offsetTop - } = parent.firstElementChild; + const { top } = $.offset(el, parent); + const { offsetTop } = parent.firstElementChild; switch (position) { - case 'top': - parent.scrollTop = top - offsetTop - ((options.margin != null) ? options.margin : 0); + case "top": + parent.scrollTop = + top - offsetTop - (options.margin != null ? options.margin : 0); break; - case 'center': - parent.scrollTop = top - Math.round((parentHeight / 2) - (el.offsetHeight / 2)); + case "center": + parent.scrollTop = + top - Math.round(parentHeight / 2 - el.offsetHeight / 2); break; - case 'continuous': - var { - scrollTop - } = parent; + case "continuous": + var { scrollTop } = parent; var height = el.offsetHeight; - var lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight; - var offsetBottom = lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0; + var lastElementOffset = + parent.lastElementChild.offsetTop + + parent.lastElementChild.offsetHeight; + var offsetBottom = + lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0; // If the target element is above the visible portion of its scrollable // ancestor, move it near the top with a gap = options.topGap * target's height. - if ((top - offsetTop) <= (scrollTop + (height * (options.topGap || 1)))) { - parent.scrollTop = top - offsetTop - (height * (options.topGap || 1)); - // If the target element is below the visible portion of its scrollable - // ancestor, move it near the bottom with a gap = options.bottomGap * target's height. - } else if ((top + offsetBottom) >= ((scrollTop + parentHeight) - (height * ((options.bottomGap || 1) + 1)))) { - parent.scrollTop = ((top + offsetBottom) - parentHeight) + (height * ((options.bottomGap || 1) + 1)); + if (top - offsetTop <= scrollTop + height * (options.topGap || 1)) { + parent.scrollTop = top - offsetTop - height * (options.topGap || 1); + // If the target element is below the visible portion of its scrollable + // ancestor, move it near the bottom with a gap = options.bottomGap * target's height. + } else if ( + top + offsetBottom >= + scrollTop + parentHeight - height * ((options.bottomGap || 1) + 1) + ) { + parent.scrollTop = + top + + offsetBottom - + parentHeight + + height * ((options.bottomGap || 1) + 1); } break; } }; -$.scrollToWithImageLock = function(el, parent, ...args) { - if (parent == null) { parent = $.scrollParent(el); } - if (!parent) { return; } +$.scrollToWithImageLock = function (el, parent, ...args) { + if (parent == null) { + parent = $.scrollParent(el); + } + if (!parent) { + return; + } $.scrollTo(el, parent, ...Array.from(args)); // Lock the scroll position on the target element for up to 3 seconds while // nearby images are loaded and rendered. - for (var image of Array.from(parent.getElementsByTagName('img'))) { + for (var image of Array.from(parent.getElementsByTagName("img"))) { if (!image.complete) { - (function() { + (function () { let timeout; - const onLoad = function(event) { + const onLoad = function (event) { clearTimeout(timeout); unbind(event.target); return $.scrollTo(el, parent, ...Array.from(args)); }; - var unbind = target => $.off(target, 'load', onLoad); + var unbind = (target) => $.off(target, "load", onLoad); - $.on(image, 'load', onLoad); - return timeout = setTimeout(unbind.bind(null, image), 3000); + $.on(image, "load", onLoad); + return (timeout = setTimeout(unbind.bind(null, image), 3000)); })(); } } }; // Calls the function while locking the element's position relative to the window. -$.lockScroll = function(el, fn) { +$.lockScroll = function (el, fn) { let parent; - if (parent = $.scrollParent(el)) { - let { - top - } = $.rect(el); - if (![document.body, document.documentElement].includes(parent)) { top -= $.rect(parent).top; } + if ((parent = $.scrollParent(el))) { + let { top } = $.rect(el); + if (![document.body, document.documentElement].includes(parent)) { + top -= $.rect(parent).top; + } fn(); parent.scrollTop = $.offset(el, parent).top - top; } else { @@ -300,9 +389,14 @@ $.lockScroll = function(el, fn) { } }; -let smoothScroll = (smoothStart = (smoothEnd = (smoothDistance = (smoothDuration = null)))); +let smoothScroll = + (smoothStart = + smoothEnd = + smoothDistance = + smoothDuration = + null); -$.smoothScroll = function(el, end) { +$.smoothScroll = function (el, end) { if (!window.requestAnimationFrame) { el.scrollTop = end; return; @@ -322,12 +416,18 @@ $.smoothScroll = function(el, end) { smoothDuration = Math.min(300, Math.abs(smoothDistance)); const startTime = Date.now(); - smoothScroll = function() { + smoothScroll = function () { const p = Math.min(1, (Date.now() - startTime) / smoothDuration); - const y = Math.max(0, Math.floor(smoothStart + (smoothDistance * (p < 0.5 ? 2 * p * p : (p * (4 - (p * 2))) - 1)))); + const y = Math.max( + 0, + Math.floor( + smoothStart + + smoothDistance * (p < 0.5 ? 2 * p * p : p * (4 - p * 2) - 1), + ), + ); el.scrollTop = y; if (p === 1) { - return smoothScroll = null; + return (smoothScroll = null); } else { return requestAnimationFrame(smoothScroll); } @@ -339,7 +439,7 @@ $.smoothScroll = function(el, end) { // Utilities // -$.extend = function(target, ...objects) { +$.extend = function (target, ...objects) { for (var object of Array.from(objects)) { if (object) { for (var key in object) { @@ -351,7 +451,7 @@ $.extend = function(target, ...objects) { return target; }; -$.makeArray = function(object) { +$.makeArray = function (object) { if (Array.isArray(object)) { return object; } else { @@ -359,7 +459,7 @@ $.makeArray = function(object) { } }; -$.arrayDelete = function(array, object) { +$.arrayDelete = function (array, object) { const index = array.indexOf(object); if (index >= 0) { array.splice(index, 1); @@ -370,45 +470,49 @@ $.arrayDelete = function(array, object) { }; // Returns true if the object is an array or a collection of DOM elements. -$.isCollection = object => Array.isArray(object) || (typeof (object != null ? object.item : undefined) === 'function'); +$.isCollection = (object) => + Array.isArray(object) || + typeof (object != null ? object.item : undefined) === "function"; const ESCAPE_HTML_MAP = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/' + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "/": "/", }; const ESCAPE_HTML_REGEXP = /[&<>"'\/]/g; -$.escape = string => string.replace(ESCAPE_HTML_REGEXP, match => ESCAPE_HTML_MAP[match]); +$.escape = (string) => + string.replace(ESCAPE_HTML_REGEXP, (match) => ESCAPE_HTML_MAP[match]); const ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g; -$.escapeRegexp = string => string.replace(ESCAPE_REGEXP, "\\$1"); +$.escapeRegexp = (string) => string.replace(ESCAPE_REGEXP, "\\$1"); -$.urlDecode = string => decodeURIComponent(string.replace(/\+/g, '%20')); +$.urlDecode = (string) => decodeURIComponent(string.replace(/\+/g, "%20")); -$.classify = function(string) { - string = string.split('_'); +$.classify = function (string) { + string = string.split("_"); for (let i = 0; i < string.length; i++) { var substr = string[i]; string[i] = substr[0].toUpperCase() + substr.slice(1); } - return string.join(''); + return string.join(""); }; -$.framify = function(fn, obj) { +$.framify = function (fn, obj) { if (window.requestAnimationFrame) { - return (...args) => requestAnimationFrame(fn.bind(obj, ...Array.from(args))); + return (...args) => + requestAnimationFrame(fn.bind(obj, ...Array.from(args))); } else { return fn; } }; -$.requestAnimationFrame = function(fn) { +$.requestAnimationFrame = function (fn) { if (window.requestAnimationFrame) { requestAnimationFrame(fn); } else { @@ -420,37 +524,81 @@ $.requestAnimationFrame = function(fn) { // Miscellaneous // -$.noop = function() {}; +$.noop = function () {}; -$.popup = function(value) { +$.popup = function (value) { try { const win = window.open(); - if (win.opener) { win.opener = null; } + if (win.opener) { + win.opener = null; + } win.location = value.href || value; } catch (error) { - window.open(value.href || value, '_blank'); + window.open(value.href || value, "_blank"); } }; let isMac = null; -$.isMac = () => isMac != null ? isMac : (isMac = (navigator.userAgent != null ? navigator.userAgent.indexOf('Mac') : undefined) >= 0); +$.isMac = () => + isMac != null + ? isMac + : (isMac = + (navigator.userAgent != null + ? navigator.userAgent.indexOf("Mac") + : undefined) >= 0); let isIE = null; -$.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)); +$.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); let isChromeForAndroid = null; -$.isChromeForAndroid = () => isChromeForAndroid != null ? isChromeForAndroid : (isChromeForAndroid = ((navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0) && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)); +$.isChromeForAndroid = () => + isChromeForAndroid != null + ? isChromeForAndroid + : (isChromeForAndroid = + (navigator.userAgent != null + ? navigator.userAgent.indexOf("Android") + : undefined) >= 0 && + /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)); let isAndroid = null; -$.isAndroid = () => isAndroid != null ? isAndroid : (isAndroid = (navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0); +$.isAndroid = () => + isAndroid != null + ? isAndroid + : (isAndroid = + (navigator.userAgent != null + ? navigator.userAgent.indexOf("Android") + : undefined) >= 0); let isIOS = null; -$.isIOS = () => isIOS != null ? isIOS : (isIOS = ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPhone') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPad') : undefined) >= 0)); - -$.overlayScrollbarsEnabled = function() { - if (!$.isMac()) { return false; } - const div = document.createElement('div'); - div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute'); +$.isIOS = () => + isIOS != null + ? isIOS + : (isIOS = + (navigator.userAgent != null + ? navigator.userAgent.indexOf("iPhone") + : undefined) >= 0 || + (navigator.userAgent != null + ? navigator.userAgent.indexOf("iPad") + : undefined) >= 0); + +$.overlayScrollbarsEnabled = function () { + if (!$.isMac()) { + return false; + } + const div = document.createElement("div"); + div.setAttribute( + "style", + "width: 100px; height: 100px; overflow: scroll; position: absolute", + ); document.body.appendChild(div); const result = div.offsetWidth === div.clientWidth; document.body.removeChild(div); @@ -458,36 +606,39 @@ $.overlayScrollbarsEnabled = function() { }; const HIGHLIGHT_DEFAULTS = { - className: 'highlight', - delay: 1000 + className: "highlight", + delay: 1000, }; -$.highlight = function(el, options) { - if (options == null) { options = {}; } +$.highlight = function (el, options) { + if (options == null) { + options = {}; + } options = $.extend({}, HIGHLIGHT_DEFAULTS, options); el.classList.add(options.className); - setTimeout((() => el.classList.remove(options.className)), options.delay); + setTimeout(() => el.classList.remove(options.className), options.delay); }; -$.copyToClipboard = function(string) { +$.copyToClipboard = function (string) { let result; - const textarea = document.createElement('textarea'); - textarea.style.position = 'fixed'; + const textarea = document.createElement("textarea"); + textarea.style.position = "fixed"; textarea.style.opacity = 0; textarea.value = string; document.body.appendChild(textarea); try { textarea.select(); - result = !!document.execCommand('copy'); + result = !!document.execCommand("copy"); } catch (error) { result = false; - } - finally { + } finally { document.body.removeChild(textarea); } return result; }; function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== "undefined" && value !== null + ? transform(value) + : undefined; +} diff --git a/assets/javascripts/models/doc.js b/assets/javascripts/models/doc.js index 1e6fb9ef..f49ce49b 100644 --- a/assets/javascripts/models/doc.js +++ b/assets/javascripts/models/doc.js @@ -12,10 +12,12 @@ app.models.Doc = class Doc extends app.Model { constructor() { super(...arguments); this.reset(this); - this.slug_without_version = this.slug.split('~')[0]; - this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : ''); + this.slug_without_version = this.slug.split("~")[0]; + this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : ""); this.icon = this.slug_without_version; - if (this.version) { this.short_version = this.version.split(' ')[0]; } + if (this.version) { + this.short_version = this.version.split(" ")[0]; + } this.text = this.toEntry().text; } @@ -26,17 +28,25 @@ app.models.Doc = class Doc extends app.Model { resetEntries(entries) { this.entries = new app.collections.Entries(entries); - this.entries.each(entry => { return entry.doc = this; }); + this.entries.each((entry) => { + return (entry.doc = this); + }); } resetTypes(types) { this.types = new app.collections.Types(types); - this.types.each(type => { return type.doc = this; }); + this.types.each((type) => { + return (type.doc = this); + }); } fullPath(path) { - if (path == null) { path = ''; } - if (path[0] !== '/') { path = `/${path}`; } + if (path == null) { + path = ""; + } + if (path[0] !== "/") { + path = `/${path}`; + } return `/${this.slug}${path}`; } @@ -49,45 +59,57 @@ app.models.Doc = class Doc extends app.Model { } indexUrl() { - return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${this.mtime}`; + return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${ + this.mtime + }`; } toEntry() { - if (this.entry) { return this.entry; } + if (this.entry) { + return this.entry; + } this.entry = new app.models.Entry({ doc: this, name: this.fullName, - path: 'index' + path: "index", }); - if (this.version) { this.entry.addAlias(this.name); } + if (this.version) { + this.entry.addAlias(this.name); + } return this.entry; } findEntryByPathAndHash(path, hash) { let entry; - if (hash && (entry = this.entries.findBy('path', `${path}#${hash}`))) { + if (hash && (entry = this.entries.findBy("path", `${path}#${hash}`))) { return entry; - } else if (path === 'index') { + } else if (path === "index") { return this.toEntry(); } else { - return this.entries.findBy('path', path); + return this.entries.findBy("path", path); } } load(onSuccess, onError, options) { - if (options == null) { options = {}; } - if (options.readCache && this._loadFromCache(onSuccess)) { return; } + if (options == null) { + options = {}; + } + if (options.readCache && this._loadFromCache(onSuccess)) { + return; + } - const callback = data => { + const callback = (data) => { this.reset(data); onSuccess(); - if (options.writeCache) { this._setCache(data); } + if (options.writeCache) { + this._setCache(data); + } }; return ajax({ url: this.indexUrl(), success: callback, - error: onError + error: onError, }); } @@ -97,7 +119,9 @@ app.models.Doc = class Doc extends app.Model { _loadFromCache(onSuccess) { let data; - if (!(data = this._getCache())) { return; } + if (!(data = this._getCache())) { + return; + } const callback = () => { this.reset(data); @@ -110,7 +134,9 @@ app.models.Doc = class Doc extends app.Model { _getCache() { let data; - if (!(data = app.localStorage.get(this.slug))) { return; } + if (!(data = app.localStorage.get(this.slug))) { + return; + } if (data[0] === this.mtime) { return data[1]; @@ -125,7 +151,9 @@ app.models.Doc = class Doc extends app.Model { } install(onSuccess, onError, onProgress) { - if (this.installing) { return; } + if (this.installing) { + return; + } this.installing = true; const error = () => { @@ -133,7 +161,7 @@ app.models.Doc = class Doc extends app.Model { onError(); }; - const success = data => { + const success = (data) => { this.installing = null; app.db.store(this, data, onSuccess, error); }; @@ -143,12 +171,14 @@ app.models.Doc = class Doc extends app.Model { success, error, progress: onProgress, - timeout: 3600 + timeout: 3600, }); } uninstall(onSuccess, onError) { - if (this.installing) { return; } + if (this.installing) { + return; + } this.installing = true; const success = () => { @@ -165,12 +195,16 @@ app.models.Doc = class Doc extends app.Model { } getInstallStatus(callback) { - app.db.version(this, value => callback({installed: !!value, mtime: value})); + app.db.version(this, (value) => + callback({ installed: !!value, mtime: value }), + ); } isOutdated(status) { - if (!status) { return false; } - const isInstalled = status.installed || app.settings.get('autoInstall'); - return isInstalled && (this.mtime !== status.mtime); + if (!status) { + return false; + } + const isInstalled = status.installed || app.settings.get("autoInstall"); + return isInstalled && this.mtime !== status.mtime; } }; diff --git a/assets/javascripts/models/entry.js b/assets/javascripts/models/entry.js index 4ce36e0e..9c04c962 100644 --- a/assets/javascripts/models/entry.js +++ b/assets/javascripts/models/entry.js @@ -8,63 +8,62 @@ */ //= require app/searcher -(function() { +(function () { let applyAliases = undefined; const Cls = (app.models.Entry = class Entry extends app.Model { static initClass() { - let ALIASES; - applyAliases = function(string) { + applyAliases = function (string) { if (ALIASES.hasOwnProperty(string)) { return [string, ALIASES[string]]; } else { - const words = string.split('.'); + const words = string.split("."); for (let i = 0; i < words.length; i++) { var word = words[i]; if (ALIASES.hasOwnProperty(word)) { words[i] = ALIASES[word]; - return [string, words.join('.')]; + return [string, words.join(".")]; } } } return string; }; - - this.ALIASES = (ALIASES = { - 'angular': 'ng', - 'angular.js': 'ng', - 'backbone.js': 'bb', - 'c++': 'cpp', - 'coffeescript': 'cs', - 'crystal': 'cr', - 'elixir': 'ex', - 'javascript': 'js', - 'julia': 'jl', - 'jquery': '$', - 'knockout.js': 'ko', - 'kubernetes': 'k8s', - 'less': 'ls', - 'lodash': '_', - 'löve': 'love', - 'marionette': 'mn', - 'markdown': 'md', - 'matplotlib': 'mpl', - 'modernizr': 'mdr', - 'moment.js': 'mt', - 'openjdk': 'java', - 'nginx': 'ngx', - 'numpy': 'np', - 'pandas': 'pd', - 'postgresql': 'pg', - 'python': 'py', - 'ruby.on.rails': 'ror', - 'ruby': 'rb', - 'rust': 'rs', - 'sass': 'scss', - 'tensorflow': 'tf', - 'typescript': 'ts', - 'underscore.js': '_' - }); + + this.ALIASES = ALIASES = { + angular: "ng", + "angular.js": "ng", + "backbone.js": "bb", + "c++": "cpp", + coffeescript: "cs", + crystal: "cr", + elixir: "ex", + javascript: "js", + julia: "jl", + jquery: "$", + "knockout.js": "ko", + kubernetes: "k8s", + less: "ls", + lodash: "_", + löve: "love", + marionette: "mn", + markdown: "md", + matplotlib: "mpl", + modernizr: "mdr", + "moment.js": "mt", + openjdk: "java", + nginx: "ngx", + numpy: "np", + pandas: "pd", + postgresql: "pg", + python: "py", + "ruby.on.rails": "ror", + ruby: "rb", + rust: "rs", + sass: "scss", + tensorflow: "tf", + typescript: "ts", + "underscore.js": "_", + }; } // Attributes: name, type, path @@ -75,16 +74,18 @@ addAlias(name) { const text = applyAliases(app.Searcher.normalizeString(name)); - if (!Array.isArray(this.text)) { this.text = [this.text]; } + if (!Array.isArray(this.text)) { + this.text = [this.text]; + } this.text.push(Array.isArray(text) ? text[1] : text); } fullPath() { - return this.doc.fullPath(this.isIndex() ? '' : this.path); + return this.doc.fullPath(this.isIndex() ? "" : this.path); } dbPath() { - return this.path.replace(/#.*/, ''); + return this.path.replace(/#.*/, ""); } filePath() { @@ -96,17 +97,19 @@ } _filePath() { - let result = this.path.replace(/#.*/, ''); - if (result.slice(-5) !== '.html') { result += '.html'; } + let result = this.path.replace(/#.*/, ""); + if (result.slice(-5) !== ".html") { + result += ".html"; + } return result; } isIndex() { - return this.path === 'index'; + return this.path === "index"; } getType() { - return this.doc.types.findBy('name', this.type); + return this.doc.types.findBy("name", this.type); } loadFile(onSuccess, onError) { diff --git a/assets/javascripts/models/model.js b/assets/javascripts/models/model.js index 34535c96..9d8885d9 100644 --- a/assets/javascripts/models/model.js +++ b/assets/javascripts/models/model.js @@ -2,6 +2,9 @@ // Sanity-check the conversion and remove this comment. app.Model = class Model { constructor(attributes) { - for (var key in attributes) { var value = attributes[key]; this[key] = value; } + for (var key in attributes) { + var value = attributes[key]; + this[key] = value; + } } }; diff --git a/assets/javascripts/models/type.js b/assets/javascripts/models/type.js index 69b3a590..178e6a8b 100644 --- a/assets/javascripts/models/type.js +++ b/assets/javascripts/models/type.js @@ -13,14 +13,14 @@ app.models.Type = class Type extends app.Model { } entries() { - return this.doc.entries.findAllBy('type', this.name); + return this.doc.entries.findAllBy("type", this.name); } toEntry() { return new app.models.Entry({ doc: this.doc, name: `${this.doc.name} / ${this.name}`, - path: '..' + this.fullPath() + path: ".." + this.fullPath(), }); } }; diff --git a/assets/javascripts/templates/base.js b/assets/javascripts/templates/base.js index a1432c61..991f367f 100644 --- a/assets/javascripts/templates/base.js +++ b/assets/javascripts/templates/base.js @@ -6,14 +6,16 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -app.templates.render = function(name, value, ...args) { +app.templates.render = function (name, value, ...args) { const template = app.templates[name]; if (Array.isArray(value)) { - let result = ''; - for (var val of Array.from(value)) { result += template(val, ...Array.from(args)); } + let result = ""; + for (var val of Array.from(value)) { + result += template(val, ...Array.from(args)); + } return result; - } else if (typeof template === 'function') { + } else if (typeof template === "function") { return template(value, ...Array.from(args)); } else { return template; diff --git a/assets/javascripts/templates/error_tmpl.js b/assets/javascripts/templates/error_tmpl.js index 1ca6156a..0dbd9b52 100644 --- a/assets/javascripts/templates/error_tmpl.js +++ b/assets/javascripts/templates/error_tmpl.js @@ -7,62 +7,79 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -const error = function(title, text, links) { - if (text == null) { text = ''; } - if (links == null) { links = ''; } - if (text) { text = `

${text}

`; } - if (links) { links = ``; } +const error = function (title, text, links) { + if (text == null) { + text = ""; + } + if (links == null) { + links = ""; + } + if (text) { + text = `

${text}

`; + } + if (links) { + links = ``; + } return `

${title}

${text}${links}
`; }; const back = 'Go back'; -app.templates.notFoundPage = () => error(" Page not found. ", - " It may be missing from the source documentation or this could be a bug. ", - back); +app.templates.notFoundPage = () => + error( + " Page not found. ", + " It may be missing from the source documentation or this could be a bug. ", + back, + ); -app.templates.pageLoadError = () => error(" The page failed to load. ", - ` It may be missing from the server (try reloading the app) or you could be offline (try installing the documentation for offline usage when online again).
+app.templates.pageLoadError = () => + error( + " The page failed to load. ", + ` It may be missing from the server (try reloading the app) or you could be offline (try installing the documentation for offline usage when online again).
If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, - ` ${back} · ReloadRetry ` -); + ` ${back} · ReloadRetry `, + ); -app.templates.bootError = () => error(" The app failed to load. ", - ` Check your Internet connection and try reloading.
-If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. ` -); +app.templates.bootError = () => + error( + " The app failed to load. ", + ` Check your Internet connection and try reloading.
+If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, + ); -app.templates.offlineError = function(reason, exception) { - if (reason === 'cookie_blocked') { +app.templates.offlineError = function (reason, exception) { + if (reason === "cookie_blocked") { return error(" Cookies must be enabled to use offline mode. "); } - reason = (() => { switch (reason) { - case 'not_supported': - return ` DevDocs requires IndexedDB to cache documentations for offline access.
+ reason = (() => { + switch (reason) { + case "not_supported": + return ` DevDocs requires IndexedDB to cache documentations for offline access.
Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. `; - case 'buggy': - return ` DevDocs requires IndexedDB to cache documentations for offline access.
+ case "buggy": + return ` DevDocs requires IndexedDB to cache documentations for offline access.
Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. `; - case 'private_mode': - return ` Your browser appears to be running in private mode.
+ case "private_mode": + return ` Your browser appears to be running in private mode.
This prevents DevDocs from caching documentations for offline access.`; - case 'exception': - return ` An error occurred when trying to open the IndexedDB database:
+ case "exception": + return ` An error occurred when trying to open the IndexedDB database:
${exception.name}: ${exception.message} `; - case 'cant_open': - return ` An error occurred when trying to open the IndexedDB database:
+ case "cant_open": + return ` An error occurred when trying to open the IndexedDB database:
${exception.name}: ${exception.message}
This could be because you're browsing in private mode or have disallowed offline storage on the domain. `; - case 'version': - return ` The IndexedDB database was modified with a newer version of the app.
+ case "version": + return ` The IndexedDB database was modified with a newer version of the app.
Reload the page to use offline mode. `; - case 'empty': - return " The IndexedDB database appears to be corrupted. Try resetting the app. "; - } })(); + case "empty": + return ' The IndexedDB database appears to be corrupted. Try resetting the app. '; + } + })(); - return error('Offline mode is unavailable.', reason); + return error("Offline mode is unavailable.", reason); }; app.templates.unsupportedBrowser = `\ diff --git a/assets/javascripts/templates/notice_tmpl.js b/assets/javascripts/templates/notice_tmpl.js index 3fc87a47..7e168d3e 100644 --- a/assets/javascripts/templates/notice_tmpl.js +++ b/assets/javascripts/templates/notice_tmpl.js @@ -5,12 +5,12 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -const notice = text => `

${text}

`; +const notice = (text) => `

${text}

`; -app.templates.singleDocNotice = doc => notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to -${app.config.production_host} (or press esc). ` -); +app.templates.singleDocNotice = (doc) => + notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to +${app.config.production_host} (or press esc). `); -app.templates.disabledDocNotice = () => notice(` This documentation is disabled. -To enable it, go to Preferences. ` -); +app.templates.disabledDocNotice = () => + notice(` This documentation is disabled. +To enable it, go to Preferences. `); diff --git a/assets/javascripts/templates/notif_tmpl.js b/assets/javascripts/templates/notif_tmpl.js index e905bb80..4d743a85 100644 --- a/assets/javascripts/templates/notif_tmpl.js +++ b/assets/javascripts/templates/notif_tmpl.js @@ -6,7 +6,7 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -const notif = function(title, html) { +const notif = function (title, html) { html = html.replace(/${title} ${html} @@ -14,32 +14,56 @@ ${html} `; }; -const textNotif = (title, message) => notif(title, `

${message}`); +const textNotif = (title, message) => + notif(title, `

${message}`); -app.templates.notifUpdateReady = () => textNotif("DevDocs has been updated.", - "Reload the page to use the new version."); +app.templates.notifUpdateReady = () => + textNotif( + 'DevDocs has been updated.', + 'Reload the page to use the new version.', + ); -app.templates.notifError = () => textNotif(" Oops, an error occurred. ", - ` Try reloading, and if the problem persists, +app.templates.notifError = () => + textNotif( + " Oops, an error occurred. ", + ` Try reloading, and if the problem persists, resetting the app.
-You can also report this issue on GitHub. ` -); +You can also report this issue on GitHub. `, + ); -app.templates.notifQuotaExceeded = () => textNotif(" The offline database has exceeded its size limitation. ", - " 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. "); +app.templates.notifQuotaExceeded = () => + textNotif( + " The offline database has exceeded its size limitation. ", + " 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. ", + ); -app.templates.notifCookieBlocked = () => textNotif(" Please enable cookies. ", - " DevDocs will not work properly if cookies are disabled. "); +app.templates.notifCookieBlocked = () => + textNotif( + " Please enable cookies. ", + " DevDocs will not work properly if cookies are disabled. ", + ); -app.templates.notifInvalidLocation = () => textNotif(` DevDocs must be loaded from ${app.config.production_host} `, - " Otherwise things are likely to break. "); +app.templates.notifInvalidLocation = () => + textNotif( + ` DevDocs must be loaded from ${app.config.production_host} `, + " Otherwise things are likely to break. ", + ); -app.templates.notifImportInvalid = () => textNotif(" Oops, an error occurred. ", - " The file you selected is invalid. "); +app.templates.notifImportInvalid = () => + textNotif( + " Oops, an error occurred. ", + " The file you selected is invalid. ", + ); -app.templates.notifNews = news => notif('Changelog', `

${app.templates.newsList(news, {years: false})}
`); +app.templates.notifNews = (news) => + notif( + "Changelog", + `
${app.templates.newsList(news, { + years: false, + })}
`, + ); -app.templates.notifUpdates = function(docs, disabledDocs) { +app.templates.notifUpdates = function (docs, disabledDocs) { let doc; let html = '
'; @@ -48,9 +72,11 @@ app.templates.notifUpdates = function(docs, disabledDocs) { html += '
'; + html += ""; } if (disabledDocs.length > 0) { @@ -58,26 +84,35 @@ app.templates.notifUpdates = function(docs, disabledDocs) { html += ''; + html += ""; } - return notif('Updates', `${html}`); + return notif("Updates", `${html}`); }; -app.templates.notifShare = () => textNotif(" Hi there! ", - ` Like DevDocs? Help us reach more developers by sharing the link with your friends on +app.templates.notifShare = () => + textNotif( + " Hi there! ", + ` Like DevDocs? Help us reach more developers by sharing the link with your friends on Twitter, Facebook, -Reddit, etc.
Thanks :) ` -); +Reddit, etc.
Thanks :) `, + ); -app.templates.notifUpdateDocs = () => textNotif(" Documentation updates available. ", - " Install them as soon as possible to avoid broken pages. "); +app.templates.notifUpdateDocs = () => + textNotif( + " Documentation updates available. ", + ' Install them as soon as possible to avoid broken pages. ', + ); -app.templates.notifAnalyticsConsent = () => textNotif(" Tracking cookies ", - ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information. +app.templates.notifAnalyticsConsent = () => + textNotif( + " Tracking cookies ", + ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information. Please confirm if you accept our tracking cookies. You can always change your decision in the settings. -
Accept or Decline ` -); +
Accept or Decline `, + ); diff --git a/assets/javascripts/templates/pages/about_tmpl.js b/assets/javascripts/templates/pages/about_tmpl.js index d06484d0..9fd09042 100644 --- a/assets/javascripts/templates/pages/about_tmpl.js +++ b/assets/javascripts/templates/pages/about_tmpl.js @@ -8,12 +8,18 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -app.templates.aboutPage = function() { +app.templates.aboutPage = function () { let doc; - const all_docs = app.docs.all().concat(...Array.from(app.disabledDocs.all() || [])); + const all_docs = app.docs + .all() + .concat(...Array.from(app.disabledDocs.all() || [])); // de-duplicate docs by doc.name const docs = []; - for (doc of Array.from(all_docs)) { if (!(docs.find(d => d.name === doc.name))) { docs.push(doc); } } + for (doc of Array.from(all_docs)) { + if (!docs.find((d) => d.name === doc.name)) { + docs.push(doc); + } + } return `\