Prettier.io

pull/1441/head
Simon Legner 1 year ago
parent 6b335fb4fc
commit a905bdf5c1

@ -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;
}
}
}

@ -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();

@ -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;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -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);

@ -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();

@ -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;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -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;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -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();
}

@ -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);

@ -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;
}
};

@ -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);
}
}
});
}

@ -7,7 +7,7 @@
*/
const Cls = (app.collections.Entries = class Entries extends app.Collection {
static initClass() {
this.model = 'Entry';
this.model = "Entry";
}
});
Cls.initClass();

@ -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) {

@ -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);
}
}
}
}
}

@ -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) {}
};

@ -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];
}
}

@ -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;
}
},
};

@ -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);
}
};

@ -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/
*/
*/

@ -13,50 +13,54 @@
* Based on github.com/visionmedia/page.js
* Licensed under the MIT license
* Copyright 2012 TJ Holowaychuk <tj@vision-media.ca>
*/
*/
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);
}
}

@ -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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;'
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"/": "&#x2F;",
};
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;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -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;
}
};

@ -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) {

@ -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;
}
}
};

@ -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(),
});
}
};

@ -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;

@ -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 = `<p class="_error-text">${text}</p>`; }
if (links) { links = `<p class="_error-links">${links}</p>`; }
const error = function (title, text, links) {
if (text == null) {
text = "";
}
if (links == null) {
links = "";
}
if (text) {
text = `<p class="_error-text">${text}</p>`;
}
if (links) {
links = `<p class="_error-links">${links}</p>`;
}
return `<div class="_error"><h1 class="_error-title">${title}</h1>${text}${links}</div>`;
};
const back = '<a href="#" data-behavior="back" class="_error-link">Go back</a>';
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 <a href="/offline">installing the documentation for offline usage</a> when online again).<br>
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 <a href="/offline">installing the documentation for offline usage</a> when online again).<br>
If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `,
` ${back} &middot; <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a>
&middot; <a href="#" class="_error-link" data-retry>Retry</a> `
);
` ${back} &middot; <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a>
&middot; <a href="#" class="_error-link" data-retry>Retry</a> `,
);
app.templates.bootError = () => error(" The app failed to load. ",
` Check your Internet connection and try <a href="#" data-behavior="reload">reloading</a>.<br>
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 <a href="#" data-behavior="reload">reloading</a>.<br>
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.<br>
reason = (() => {
switch (reason) {
case "not_supported":
return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
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.<br>
case "buggy":
return ` DevDocs requires IndexedDB to cache documentations for offline access.<br>
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.<br>
case "private_mode":
return ` Your browser appears to be running in private mode.<br>
This prevents DevDocs from caching documentations for offline access.`;
case 'exception':
return ` An error occurred when trying to open the IndexedDB database:<br>
case "exception":
return ` An error occurred when trying to open the IndexedDB database:<br>
<code class="_label">${exception.name}: ${exception.message}</code> `;
case 'cant_open':
return ` An error occurred when trying to open the IndexedDB database:<br>
case "cant_open":
return ` An error occurred when trying to open the IndexedDB database:<br>
<code class="_label">${exception.name}: ${exception.message}</code><br>
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.<br>
case "version":
return ` The IndexedDB database was modified with a newer version of the app.<br>
<a href="#" data-behavior="reload">Reload the page</a> to use offline mode. `;
case 'empty':
return " The IndexedDB database appears to be corrupted. Try <a href=\"#\" data-behavior=\"reset\">resetting the app</a>. ";
} })();
case "empty":
return ' The IndexedDB database appears to be corrupted. Try <a href="#" data-behavior="reset">resetting the app</a>. ';
}
})();
return error('Offline mode is unavailable.', reason);
return error("Offline mode is unavailable.", reason);
};
app.templates.unsupportedBrowser = `\

@ -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 => `<p class="_notice-text">${text}</p>`;
const notice = (text) => `<p class="_notice-text">${text}</p>`;
app.templates.singleDocNotice = doc => notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to
<a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). `
);
app.templates.singleDocNotice = (doc) =>
notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to
<a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). `);
app.templates.disabledDocNotice = () => notice(` <strong>This documentation is disabled.</strong>
To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. `
);
app.templates.disabledDocNotice = () =>
notice(` <strong>This documentation is disabled.</strong>
To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. `);

@ -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(/<a /g, '<a class="_notif-link" ');
return ` <h5 class="_notif-title">${title}</h5>
${html}
@ -14,32 +14,56 @@ ${html}
`;
};
const textNotif = (title, message) => notif(title, `<p class="_notif-text">${message}`);
const textNotif = (title, message) =>
notif(title, `<p class="_notif-text">${message}`);
app.templates.notifUpdateReady = () => textNotif("<span data-behavior=\"reboot\">DevDocs has been updated.</span>",
"<span data-behavior=\"reboot\"><a href=\"#\" data-behavior=\"reboot\">Reload the page</a> to use the new version.</span>");
app.templates.notifUpdateReady = () =>
textNotif(
'<span data-behavior="reboot">DevDocs has been updated.</span>',
'<span data-behavior="reboot"><a href="#" data-behavior="reboot">Reload the page</a> to use the new version.</span>',
);
app.templates.notifError = () => textNotif(" Oops, an error occurred. ",
` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists,
app.templates.notifError = () =>
textNotif(
" Oops, an error occurred. ",
` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists,
<a href="#" data-behavior="reset">resetting the app</a>.<br>
You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. `
);
You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. `,
);
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', `<div class="_notif-content _notif-news">${app.templates.newsList(news, {years: false})}</div>`);
app.templates.notifNews = (news) =>
notif(
"Changelog",
`<div class="_notif-content _notif-news">${app.templates.newsList(news, {
years: false,
})}</div>`,
);
app.templates.notifUpdates = function(docs, disabledDocs) {
app.templates.notifUpdates = function (docs, disabledDocs) {
let doc;
let html = '<div class="_notif-content _notif-news">';
@ -48,9 +72,11 @@ app.templates.notifUpdates = function(docs, disabledDocs) {
html += '<ul class="_notif-list">';
for (doc of Array.from(docs)) {
html += `<li>${doc.name}`;
if (doc.release) { html += ` <code>&rarr;</code> ${doc.release}`; }
if (doc.release) {
html += ` <code>&rarr;</code> ${doc.release}`;
}
}
html += '</ul></div>';
html += "</ul></div>";
}
if (disabledDocs.length > 0) {
@ -58,26 +84,35 @@ app.templates.notifUpdates = function(docs, disabledDocs) {
html += '<ul class="_notif-list">';
for (doc of Array.from(disabledDocs)) {
html += `<li>${doc.name}`;
if (doc.release) { html += ` <code>&rarr;</code> ${doc.release}`; }
html += "<span class=\"_notif-info\"><a href=\"/settings\">Enable</a></span>";
if (doc.release) {
html += ` <code>&rarr;</code> ${doc.release}`;
}
html += '<span class="_notif-info"><a href="/settings">Enable</a></span>';
}
html += '</ul></div>';
html += "</ul></div>";
}
return notif('Updates', `${html}</div>`);
return notif("Updates", `${html}</div>`);
};
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
<a href="https://out.devdocs.io/s/tw" target="_blank" rel="noopener">Twitter</a>, <a href="https://out.devdocs.io/s/fb" target="_blank" rel="noopener">Facebook</a>,
<a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) `
);
<a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) `,
);
app.templates.notifUpdateDocs = () => textNotif(" Documentation updates available. ",
" <a href=\"/offline\">Install them</a> as soon as possible to avoid broken pages. ");
app.templates.notifUpdateDocs = () =>
textNotif(
" Documentation updates available. ",
' <a href="/offline">Install them</a> 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.
<br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> `
);
<br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> `,
);

@ -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 `\
<nav class="_toc" role="directory">
<h3 class="_toc-title">Table of Contents</h3>
@ -79,18 +85,23 @@ app.templates.aboutPage = function() {
<th>Documentation
<th>Copyright/License
<th>Source code
${((() => {
const result = [];
for (doc of Array.from(docs)) { result.push(`<tr> \
<td><a href=\"${(doc.links != null ? doc.links.home : undefined)}\">${doc.name}</a></td> \
${(() => {
const result = [];
for (doc of Array.from(docs)) {
result.push(`<tr> \
<td><a href=\"${doc.links != null ? doc.links.home : undefined}\">${
doc.name
}</a></td> \
<td>${doc.attribution}</td> \
<td><a href=\"${(doc.links != null ? doc.links.code : undefined)}\">Source code</a></td> \
<td><a href=\"${
doc.links != null ? doc.links.code : undefined
}\">Source code</a></td> \
</tr>`);
}
return result;
})()).join('')}
}
return result;
})().join("")}
</table>
</div>

@ -6,11 +6,11 @@
* DS205: Consider reworking code to avoid use of IIFEs
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
app.templates.helpPage = function() {
app.templates.helpPage = function () {
let key, value;
const ctrlKey = $.isMac() ? 'cmd' : 'ctrl';
const navKey = $.isMac() ? 'cmd' : 'alt';
const arrowScroll = app.settings.get('arrowScroll');
const ctrlKey = $.isMac() ? "cmd" : "ctrl";
const navKey = $.isMac() ? "cmd" : "alt";
const arrowScroll = app.settings.get("arrowScroll");
const aliases_one = {};
const aliases_two = {};
@ -18,7 +18,8 @@ app.templates.helpPage = function() {
const middle = Math.ceil(keys.length / 2) - 1;
for (let i = 0; i < keys.length; i++) {
key = keys[i];
(i > middle ? aliases_two : aliases_one)[key] = app.models.Entry.ALIASES[key];
(i > middle ? aliases_two : aliases_one)[key] =
app.models.Entry.ALIASES[key];
}
return `\
@ -79,12 +80,12 @@ app.templates.helpPage = function() {
<h3 class="_shortcuts-title">Sidebar</h3>
<dl class="_shortcuts-dl">
<dt class="_shortcuts-dt">
${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''}
${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""}
<code class="_shortcut-code">&darr;</code>
<code class="_shortcut-code">&uarr;</code>
<dd class="_shortcuts-dd">Move selection
<dt class="_shortcuts-dt">
${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''}
${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""}
<code class="_shortcut-code">&rarr;</code>
<code class="_shortcut-code">&larr;</code>
<dd class="_shortcuts-dd">Show/hide sub-list
@ -105,15 +106,16 @@ app.templates.helpPage = function() {
<code class="_shortcut-code">${navKey} + &rarr;</code>
<dd class="_shortcuts-dd">Go back/forward
<dt class="_shortcuts-dt">
${arrowScroll ?
'<code class="_shortcut-code">&darr;</code> ' +
${
arrowScroll
? '<code class="_shortcut-code">&darr;</code> ' +
'<code class="_shortcut-code">&uarr;</code>'
:
'<code class="_shortcut-code">alt + &darr;</code> ' +
: '<code class="_shortcut-code">alt + &darr;</code> ' +
'<code class="_shortcut-code">alt + &uarr;</code>' +
'<br>' +
"<br>" +
'<code class="_shortcut-code">shift + &darr;</code> ' +
'<code class="_shortcut-code">shift + &uarr;</code>'}
'<code class="_shortcut-code">shift + &uarr;</code>'
}
<dd class="_shortcuts-dd">Scroll step by step<br><br>
<dt class="_shortcuts-dt">
<code class="_shortcut-code">space</code>
@ -167,27 +169,31 @@ app.templates.helpPage = function() {
<tr>
<th>Word
<th>Alias
${((() => {
const result = [];
for (key in aliases_one) {
value = aliases_one[key];
result.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`);
}
return result;
})()).join('')}
${(() => {
const result = [];
for (key in aliases_one) {
value = aliases_one[key];
result.push(
`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`,
);
}
return result;
})().join("")}
</table>
<table>
<tr>
<th>Word
<th>Alias
${((() => {
const result1 = [];
for (key in aliases_two) {
value = aliases_two[key];
result1.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`);
}
return result1;
})()).join('')}
${(() => {
const result1 = [];
for (key in aliases_two) {
value = aliases_two[key];
result1.push(
`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`,
);
}
return result1;
})().join("")}
</table>
</div>
<p>Feel free to suggest new aliases on <a href="https://github.com/freeCodeCamp/devdocs/issues/new">GitHub</a>.\

@ -5,12 +5,14 @@
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
app.templates.offlinePage = docs => `\
app.templates.offlinePage = (docs) => `\
<h1 class="_lined-heading">Offline Documentation</h1>
<div class="_docs-tools">
<label>
<input type="checkbox" name="autoUpdate" value="1" ${app.settings.get('manualUpdate') ? '' : 'checked'}>Install updates automatically
<input type="checkbox" name="autoUpdate" value="1" ${
app.settings.get("manualUpdate") ? "" : "checked"
}>Install updates automatically
</label>
<div class="_docs-links">
<button type="button" class="_btn-link" data-action-all="install">Install all</button><button type="button" class="_btn-link" data-action-all="update"><strong>Update all</strong></button><button type="button" class="_btn-link" data-action-all="uninstall">Uninstall all</button>
@ -47,14 +49,15 @@ app.templates.offlinePage = docs => `\
</dl>\
`;
var canICloseTheTab = function() {
var canICloseTheTab = function () {
if (app.ServiceWorker.isEnabled()) {
return " Yes! Even offline, you can open a new tab, go to <a href=\"//devdocs.io\">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). ";
return ' Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). ';
} else {
let reason = "aren't available in your browser (or are disabled)";
if (app.config.env !== 'production') {
reason = "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)";
if (app.config.env !== "production") {
reason =
"are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)";
}
return ` No. Service Workers ${reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br>
@ -62,30 +65,31 @@ The current tab will continue to function even when you go offline (provided you
}
};
app.templates.offlineDoc = function(doc, status) {
app.templates.offlineDoc = function (doc, status) {
const outdated = doc.isOutdated(status);
let html = `\
<tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ''}>
<tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ""}>
<td class="_docs-name _icon-${doc.icon}">${doc.fullName}</td>
<td class="_docs-size">${Math.ceil(doc.db_size / 100000) / 10}&nbsp;<small>MB</small></td>\
<td class="_docs-size">${
Math.ceil(doc.db_size / 100000) / 10
}&nbsp;<small>MB</small></td>\
`;
html += !(status && status.installed) ?
`\
html += !(status && status.installed)
? `\
<td>-</td>
<td><button type="button" class="_btn-link" data-action="install">Install</button></td>\
`
: outdated ?
`\
: outdated
? `\
<td><strong>Outdated</strong></td>
<td><button type="button" class="_btn-link _bold" data-action="update">Update</button> - <button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\
`
:
`\
: `\
<td>Up&#8209;to&#8209;date</td>
<td><button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\
`;
return html + '</tr>';
return html + "</tr>";
};

@ -7,23 +7,32 @@
*/
const themeOption = ({ label, value }, settings) => `\
<label class="_settings-label _theme-label">
<input type="radio" name="theme" value="${value}"${settings.theme === value ? ' checked' : ''}>
<input type="radio" name="theme" value="${value}"${
settings.theme === value ? " checked" : ""
}>
${label}
</label>\
`;
app.templates.settingsPage = settings => `\
app.templates.settingsPage = (settings) => `\
<h1 class="_lined-heading">Preferences</h1>
<div class="_settings-fieldset">
<h2 class="_settings-legend">Theme:</h2>
<div class="_settings-inputs">
${settings.autoSupported ?
themeOption({label: "Automatic <small>Matches system setting</small>", value: "auto"}, settings)
:
""}
${themeOption({label: "Light", value: "default"}, settings)}
${themeOption({label: "Dark", value: "dark"}, settings)}
${
settings.autoSupported
? themeOption(
{
label: "Automatic <small>Matches system setting</small>",
value: "auto",
},
settings,
)
: ""
}
${themeOption({ label: "Light", value: "default" }, settings)}
${themeOption({ label: "Dark", value: "dark" }, settings)}
</div>
</div>
@ -32,24 +41,36 @@ app.templates.settingsPage = settings => `\
<div class="_settings-inputs">
<label class="_settings-label _setting-max-width">
<input type="checkbox" form="settings" name="layout" value="_max-width"${settings['_max-width'] ? ' checked' : ''}>Enable fixed-width layout
<input type="checkbox" form="settings" name="layout" value="_max-width"${
settings["_max-width"] ? " checked" : ""
}>Enable fixed-width layout
</label>
<label class="_settings-label _setting-text-justify-hyphenate">
<input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${settings['_text-justify-hyphenate'] ? ' checked' : ''}>Enable justified layout and automatic hyphenation
<input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${
settings["_text-justify-hyphenate"] ? " checked" : ""
}>Enable justified layout and automatic hyphenation
</label>
<label class="_settings-label _hide-on-mobile">
<input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${settings['_sidebar-hidden'] ? ' checked' : ''}>Automatically hide and show the sidebar
<input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${
settings["_sidebar-hidden"] ? " checked" : ""
}>Automatically hide and show the sidebar
<small>Tip: drag the edge of the sidebar to resize it.</small>
</label>
<label class="_settings-label _hide-on-mobile">
<input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${settings.noAutofocus ? ' checked' : ''}>Disable autofocus of search input
<input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${
settings.noAutofocus ? " checked" : ""
}>Disable autofocus of search input
</label>
<label class="_settings-label">
<input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${settings.autoInstall ? ' checked' : ''}>Automatically download documentation for offline use
<input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${
settings.autoInstall ? " checked" : ""
}>Automatically download documentation for offline use
<small>Only enable this when bandwidth isn't a concern to you.</small>
</label>
<label class="_settings-label _hide-in-development">
<input type="checkbox" form="settings" name="analyticsConsent"${settings.analyticsConsent ? ' checked' : ''}>Enable tracking cookies
<input type="checkbox" form="settings" name="analyticsConsent"${
settings.analyticsConsent ? " checked" : ""
}>Enable tracking cookies
<small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small>
</label>
</div>
@ -60,20 +81,30 @@ app.templates.settingsPage = settings => `\
<div class="_settings-inputs">
<label class="_settings-label">
<input type="checkbox" form="settings" name="smoothScroll" value="1"${settings.smoothScroll ? ' checked' : ''}>Use smooth scrolling
<input type="checkbox" form="settings" name="smoothScroll" value="1"${
settings.smoothScroll ? " checked" : ""
}>Use smooth scrolling
</label>
<label class="_settings-label _setting-native-scrollbar">
<input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${settings['_native-scrollbars'] ? ' checked' : ''}>Use native scrollbars
<input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${
settings["_native-scrollbars"] ? " checked" : ""
}>Use native scrollbars
</label>
<label class="_settings-label">
<input type="checkbox" form="settings" name="arrowScroll" value="1"${settings.arrowScroll ? ' checked' : ''}>Use arrow keys to scroll the main content area
<input type="checkbox" form="settings" name="arrowScroll" value="1"${
settings.arrowScroll ? " checked" : ""
}>Use arrow keys to scroll the main content area
<small>With this checked, use <code class="_label">shift</code> + <code class="_label">&uarr;</code><code class="_label">&darr;</code><code class="_label">&larr;</code><code class="_label">&rarr;</code> to navigate the sidebar.</small>
</label>
<label class="_settings-label">
<input type="checkbox" form="settings" name="spaceScroll" value="1"${settings.spaceScroll ? ' checked' : ''}>Use spacebar to scroll during search
<input type="checkbox" form="settings" name="spaceScroll" value="1"${
settings.spaceScroll ? " checked" : ""
}>Use spacebar to scroll during search
</label>
<label class="_settings-label">
<input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${settings.spaceTimeout}"> Delay until you can scroll by pressing space
<input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${
settings.spaceTimeout
}"> Delay until you can scroll by pressing space
<small>Time in seconds</small>
</label>
</div>

@ -5,7 +5,13 @@
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
app.templates.typePage = type => ` <h1>${type.doc.fullName} / ${type.name}</h1>
<ul class="_entry-list">${app.templates.render('typePageEntry', type.entries())}</ul> `;
app.templates.typePage = (type) => ` <h1>${type.doc.fullName} / ${
type.name
}</h1>
<ul class="_entry-list">${app.templates.render(
"typePageEntry",
type.entries(),
)}</ul> `;
app.templates.typePageEntry = entry => `<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`;
app.templates.typePageEntry = (entry) =>
`<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`;

@ -5,11 +5,19 @@
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const arrow = "<svg class=\"_path-arrow\"><use xlink:href=\"#icon-dir\"/></svg>";
const arrow = '<svg class="_path-arrow"><use xlink:href="#icon-dir"/></svg>';
app.templates.path = function(doc, type, entry) {
let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${doc.icon}">${doc.fullName}</a>`;
if (type) { html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${type.name}</a>`; }
if (entry) { html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`; }
app.templates.path = function (doc, type, entry) {
let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${
doc.icon
}">${doc.fullName}</a>`;
if (type) {
html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${
type.name
}</a>`;
}
if (entry) {
html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`;
}
return html;
};

@ -6,74 +6,112 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const {
templates
} = app;
const { templates } = app;
const arrow = "<svg class=\"_list-arrow\"><use xlink:href=\"#icon-dir\"/></svg>";
const arrow = '<svg class="_list-arrow"><use xlink:href="#icon-dir"/></svg>';
templates.sidebarDoc = function(doc, options) {
if (options == null) { options = {}; }
let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `;
link += options.disabled ? '_list-disabled' : '_list-dir';
templates.sidebarDoc = function (doc, options) {
if (options == null) {
options = {};
}
let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `;
link += options.disabled ? "_list-disabled" : "_list-dir";
link += `" data-slug="${doc.slug}" title="${doc.fullName}" tabindex="-1">`;
if (options.disabled) {
link += `<span class="_list-enable" data-enable="${doc.slug}">Enable</span>`;
} else {
link += arrow;
}
if (doc.release) { link += `<span class="_list-count">${doc.release}</span>`; }
if (doc.release) {
link += `<span class="_list-count">${doc.release}</span>`;
}
link += `<span class="_list-text">${doc.name}`;
if (options.fullName || (options.disabled && doc.version)) { link += ` ${doc.version}`; }
if (options.fullName || (options.disabled && doc.version)) {
link += ` ${doc.version}`;
}
return link + "</span></a>";
};
templates.sidebarType = type => `<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${type.slug}" tabindex="-1">${arrow}<span class="_list-count">${type.count}</span><span class="_list-text">${$.escape(type.name)}</span></a>`;
templates.sidebarType = (type) =>
`<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${
type.slug
}" tabindex="-1">${arrow}<span class="_list-count">${
type.count
}</span><span class="_list-text">${$.escape(type.name)}</span></a>`;
templates.sidebarEntry = entry => `<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape(entry.name)}</a>`;
templates.sidebarEntry = (entry) =>
`<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape(
entry.name,
)}</a>`;
templates.sidebarResult = function(entry) {
let addons = entry.isIndex() && app.disabledDocs.contains(entry.doc) ?
`<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>`
:
"<span class=\"_list-reveal\" data-reset-list title=\"Reveal in list\"></span>";
if (entry.doc.version && !entry.isIndex()) { addons += `<span class="_list-count">${entry.doc.short_version}</span>`; }
return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${entry.doc.icon}" tabindex="-1">${addons}<span class="_list-text">${$.escape(entry.name)}</span></a>`;
templates.sidebarResult = function (entry) {
let addons =
entry.isIndex() && app.disabledDocs.contains(entry.doc)
? `<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>`
: '<span class="_list-reveal" data-reset-list title="Reveal in list"></span>';
if (entry.doc.version && !entry.isIndex()) {
addons += `<span class="_list-count">${entry.doc.short_version}</span>`;
}
return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${
entry.doc.icon
}" tabindex="-1">${addons}<span class="_list-text">${$.escape(
entry.name,
)}</span></a>`;
};
templates.sidebarNoResults = function() {
let html = " <div class=\"_list-note\">No results.</div> ";
if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) { html += `\
templates.sidebarNoResults = function () {
let html = ' <div class="_list-note">No results.</div> ';
if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) {
html += `\
<div class="_list-note">Note: documentations must be <a href="/settings" class="_list-note-link">enabled</a> to appear in the search.</div>\
`; }
`;
}
return html;
};
templates.sidebarPageLink = count => `<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`;
templates.sidebarPageLink = (count) =>
`<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`;
templates.sidebarLabel = function(doc, options) {
if (options == null) { options = {}; }
let label = "<label class=\"_list-item";
if (!doc.version) { label += ` _icon-${doc.icon}`; }
templates.sidebarLabel = function (doc, options) {
if (options == null) {
options = {};
}
let label = '<label class="_list-item';
if (!doc.version) {
label += ` _icon-${doc.icon}`;
}
label += `"><input type="checkbox" name="${doc.slug}" class="_list-checkbox" `;
if (options.checked) { label += "checked"; }
if (options.checked) {
label += "checked";
}
return label + `><span class="_list-text">${doc.fullName}</span></label>`;
};
templates.sidebarVersionedDoc = function(doc, versions, options) {
if (options == null) { options = {}; }
templates.sidebarVersionedDoc = function (doc, versions, options) {
if (options == null) {
options = {};
}
let html = `<div class="_list-item _list-dir _list-rdir _icon-${doc.icon}`;
if (options.open) { html += " open"; }
return html + `" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>`;
if (options.open) {
html += " open";
}
return (
html +
`" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>`
);
};
templates.sidebarDisabled = options => `<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`;
templates.sidebarDisabled = (options) =>
`<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`;
templates.sidebarDisabledList = html => `<div class="_disabled-list">${html}</div>`;
templates.sidebarDisabledList = (html) =>
`<div class="_disabled-list">${html}</div>`;
templates.sidebarDisabledVersionedDoc = (doc, versions) => `<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`;
templates.sidebarDisabledVersionedDoc = (doc, versions) =>
`<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`;
templates.docPickerHeader = "<div class=\"_list-picker-head\"><span>Documentation</span> <span>Enable</span></div>";
templates.docPickerHeader =
'<div class="_list-picker-head"><span>Documentation</span> <span>Enable</span></div>';
templates.docPickerNote = `\
<div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>

@ -10,8 +10,14 @@ app.templates.tipKeyNav = () => `\
<strong>ProTip</strong>
<span class="_notif-info">(click to dismiss)</span>
<p class="_notif-text">
Hit ${app.settings.get('arrowScroll') ? '<code class="_label">shift</code> +' : ''} <code class="_label">&darr;</code> <code class="_label">&uarr;</code> <code class="_label">&larr;</code> <code class="_label">&rarr;</code> to navigate the sidebar.<br>
Hit <code class="_label">space / shift space</code>${app.settings.get('arrowScroll') ? ' or <code class="_label">&darr;/&uarr;</code>' : ', <code class="_label">alt &darr;/&uarr;</code> or <code class="_label">shift &darr;/&uarr;</code>'} to scroll the page.
Hit ${
app.settings.get("arrowScroll") ? '<code class="_label">shift</code> +' : ""
} <code class="_label">&darr;</code> <code class="_label">&uarr;</code> <code class="_label">&larr;</code> <code class="_label">&rarr;</code> to navigate the sidebar.<br>
Hit <code class="_label">space / shift space</code>${
app.settings.get("arrowScroll")
? ' or <code class="_label">&darr;/&uarr;</code>'
: ', <code class="_label">alt &darr;/&uarr;</code> or <code class="_label">shift &darr;/&uarr;</code>'
} to scroll the page.
<p class="_notif-text">
<a href="/help#shortcuts" class="_notif-link">See all keyboard shortcuts</a>\
`;

@ -1,32 +1,55 @@
try {
if (app.config.env === 'production') {
if (Cookies.get('analyticsConsent') === '1') {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-5544833-12', 'devdocs.io');
page.track(function() {
ga('send', 'pageview', {
if (app.config.env === "production") {
if (Cookies.get("analyticsConsent") === "1") {
(function (i, s, o, g, r, a, m) {
i["GoogleAnalyticsObject"] = r;
(i[r] =
i[r] ||
function () {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(
window,
document,
"script",
"https://www.google-analytics.com/analytics.js",
"ga",
);
ga("create", "UA-5544833-12", "devdocs.io");
page.track(function () {
ga("send", "pageview", {
page: location.pathname + location.search + location.hash,
dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version
dimension1:
app.router.context &&
app.router.context.doc &&
app.router.context.doc.slug_without_version,
});
});
page.track(function() {
if (window._gauges)
_gauges.push(['track']);
page.track(function () {
if (window._gauges) _gauges.push(["track"]);
else
(function() {
var _gauges=_gauges||[];!function(){var a=document.createElement("script");
a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
b.parentNode.insertBefore(a,b)}();
(function () {
var _gauges = _gauges || [];
!(function () {
var a = document.createElement("script");
(a.type = "text/javascript"),
(a.async = !0),
(a.id = "gauges-tracker"),
a.setAttribute("data-site-id", "51c15f82613f5d7819000067"),
(a.src = "https://secure.gaug.es/track.js");
var b = document.getElementsByTagName("script")[0];
b.parentNode.insertBefore(a, b);
})();
})();
});
} else {
resetAnalytics();
}
}
} catch(e) { }
} catch (e) {}

@ -5,171 +5,204 @@
* This is free and unencumbered software released into the public domain.
*/
(function (global, undefined) {
'use strict';
"use strict";
var factory = function (window) {
if (typeof window.document !== 'object') {
throw new Error('Cookies.js requires a `window` with a `document` object');
var factory = function (window) {
if (typeof window.document !== "object") {
throw new Error(
"Cookies.js requires a `window` with a `document` object",
);
}
var Cookies = function (key, value, options) {
return arguments.length === 1
? Cookies.get(key)
: Cookies.set(key, value, options);
};
// Allows for setter injection in unit tests
Cookies._document = window.document;
// Used to ensure cookie keys do not collide with
// built-in `Object` properties
Cookies._cacheKeyPrefix = "cookey."; // Hurr hurr, :)
Cookies._maxExpireDate = new Date("Fri, 31 Dec 9999 23:59:59 UTC");
Cookies.defaults = {
path: "/",
SameSite: "Strict",
secure: true,
};
Cookies.get = function (key) {
if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
Cookies._renewCache();
}
var value = Cookies._cache[Cookies._cacheKeyPrefix + key];
return value === undefined ? undefined : decodeURIComponent(value);
};
Cookies.set = function (key, value, options) {
options = Cookies._getExtendedOptions(options);
options.expires = Cookies._getExpiresDate(
value === undefined ? -1 : options.expires,
);
Cookies._document.cookie = Cookies._generateCookieString(
key,
value,
options,
);
return Cookies;
};
Cookies.expire = function (key, options) {
return Cookies.set(key, undefined, options);
};
Cookies._getExtendedOptions = function (options) {
return {
path: (options && options.path) || Cookies.defaults.path,
domain: (options && options.domain) || Cookies.defaults.domain,
SameSite: (options && options.SameSite) || Cookies.defaults.SameSite,
expires: (options && options.expires) || Cookies.defaults.expires,
secure:
options && options.secure !== undefined
? options.secure
: Cookies.defaults.secure,
};
};
Cookies._isValidDate = function (date) {
return (
Object.prototype.toString.call(date) === "[object Date]" &&
!isNaN(date.getTime())
);
};
Cookies._getExpiresDate = function (expires, now) {
now = now || new Date();
if (typeof expires === "number") {
expires =
expires === Infinity
? Cookies._maxExpireDate
: new Date(now.getTime() + expires * 1000);
} else if (typeof expires === "string") {
expires = new Date(expires);
}
if (expires && !Cookies._isValidDate(expires)) {
throw new Error(
"`expires` parameter cannot be converted to a valid Date instance",
);
}
return expires;
};
Cookies._generateCookieString = function (key, value, options) {
key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
key = key.replace(/\(/g, "%28").replace(/\)/g, "%29");
value = (value + "").replace(
/[^!#$&-+\--:<-\[\]-~]/g,
encodeURIComponent,
);
options = options || {};
var cookieString = key + "=" + value;
cookieString += options.path ? ";path=" + options.path : "";
cookieString += options.domain ? ";domain=" + options.domain : "";
cookieString += options.SameSite ? ";SameSite=" + options.SameSite : "";
cookieString += options.expires
? ";expires=" + options.expires.toUTCString()
: "";
cookieString += options.secure ? ";secure" : "";
return cookieString;
};
Cookies._getCacheFromString = function (documentCookie) {
var cookieCache = {};
var cookiesArray = documentCookie ? documentCookie.split("; ") : [];
for (var i = 0; i < cookiesArray.length; i++) {
var cookieKvp = Cookies._getKeyValuePairFromCookieString(
cookiesArray[i],
);
if (
cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined
) {
cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] =
cookieKvp.value;
}
}
var Cookies = function (key, value, options) {
return arguments.length === 1 ?
Cookies.get(key) : Cookies.set(key, value, options);
};
return cookieCache;
};
// Allows for setter injection in unit tests
Cookies._document = window.document;
// Used to ensure cookie keys do not collide with
// built-in `Object` properties
Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :)
Cookies._getKeyValuePairFromCookieString = function (cookieString) {
// "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
var separatorIndex = cookieString.indexOf("=");
// IE omits the "=" when the cookie value is an empty string
separatorIndex =
separatorIndex < 0 ? cookieString.length : separatorIndex;
var key = cookieString.substr(0, separatorIndex);
var decodedKey;
try {
decodedKey = decodeURIComponent(key);
} catch (e) {
if (console && typeof console.error === "function") {
console.error('Could not decode cookie with key "' + key + '"', e);
}
}
Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC');
return {
key: decodedKey,
value: cookieString.substr(separatorIndex + 1), // Defer decoding value until accessed
};
};
Cookies.defaults = {
path: '/',
SameSite: 'Strict',
secure: true
};
Cookies.get = function (key) {
if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
Cookies._renewCache();
}
var value = Cookies._cache[Cookies._cacheKeyPrefix + key];
return value === undefined ? undefined : decodeURIComponent(value);
};
Cookies.set = function (key, value, options) {
options = Cookies._getExtendedOptions(options);
options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);
Cookies._document.cookie = Cookies._generateCookieString(key, value, options);
return Cookies;
};
Cookies.expire = function (key, options) {
return Cookies.set(key, undefined, options);
};
Cookies._getExtendedOptions = function (options) {
return {
path: options && options.path || Cookies.defaults.path,
domain: options && options.domain || Cookies.defaults.domain,
SameSite: options && options.SameSite || Cookies.defaults.SameSite,
expires: options && options.expires || Cookies.defaults.expires,
secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure
};
};
Cookies._isValidDate = function (date) {
return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
};
Cookies._getExpiresDate = function (expires, now) {
now = now || new Date();
if (typeof expires === 'number') {
expires = expires === Infinity ?
Cookies._maxExpireDate : new Date(now.getTime() + expires * 1000);
} else if (typeof expires === 'string') {
expires = new Date(expires);
}
if (expires && !Cookies._isValidDate(expires)) {
throw new Error('`expires` parameter cannot be converted to a valid Date instance');
}
return expires;
};
Cookies._generateCookieString = function (key, value, options) {
key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
options = options || {};
var cookieString = key + '=' + value;
cookieString += options.path ? ';path=' + options.path : '';
cookieString += options.domain ? ';domain=' + options.domain : '';
cookieString += options.SameSite ? ';SameSite=' + options.SameSite : '';
cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
cookieString += options.secure ? ';secure' : '';
return cookieString;
};
Cookies._getCacheFromString = function (documentCookie) {
var cookieCache = {};
var cookiesArray = documentCookie ? documentCookie.split('; ') : [];
for (var i = 0; i < cookiesArray.length; i++) {
var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);
if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) {
cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value;
}
}
return cookieCache;
};
Cookies._getKeyValuePairFromCookieString = function (cookieString) {
// "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
var separatorIndex = cookieString.indexOf('=');
// IE omits the "=" when the cookie value is an empty string
separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;
var key = cookieString.substr(0, separatorIndex);
var decodedKey;
try {
decodedKey = decodeURIComponent(key);
} catch (e) {
if (console && typeof console.error === 'function') {
console.error('Could not decode cookie with key "' + key + '"', e);
}
}
return {
key: decodedKey,
value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed
};
};
Cookies._renewCache = function () {
Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
Cookies._cachedDocumentCookie = Cookies._document.cookie;
};
Cookies._areEnabled = function () {
var testKey = 'cookies.js';
var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
Cookies.expire(testKey);
return areEnabled;
};
Cookies.enabled = Cookies._areEnabled();
return Cookies;
Cookies._renewCache = function () {
Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
Cookies._cachedDocumentCookie = Cookies._document.cookie;
};
var cookiesExport = (global && typeof global.document === 'object') ? factory(global) : factory;
// AMD support
if (typeof define === 'function' && define.amd) {
define(function () { return cookiesExport; });
Cookies._areEnabled = function () {
var testKey = "cookies.js";
var areEnabled = Cookies.set(testKey, 1).get(testKey) === "1";
Cookies.expire(testKey);
return areEnabled;
};
Cookies.enabled = Cookies._areEnabled();
return Cookies;
};
var cookiesExport =
global && typeof global.document === "object" ? factory(global) : factory;
// AMD support
if (typeof define === "function" && define.amd) {
define(function () {
return cookiesExport;
});
// CommonJS/Node.js support
} else if (typeof exports === 'object') {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module === 'object' && typeof module.exports === 'object') {
exports = module.exports = cookiesExport;
}
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.Cookies = cookiesExport;
} else {
global.Cookies = cookiesExport;
} else if (typeof exports === "object") {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module === "object" && typeof module.exports === "object") {
exports = module.exports = cookiesExport;
}
})(typeof window === 'undefined' ? this : window);
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.Cookies = cookiesExport;
} else {
global.Cookies = cookiesExport;
}
})(typeof window === "undefined" ? this : window);

@ -4,17 +4,22 @@
* Adapted from: https://github.com/fred-wang/mathml.css */
(function () {
window.addEventListener("load", function() {
window.addEventListener("load", function () {
var box, div, link, namespaceURI;
// First check whether the page contains any <math> element.
namespaceURI = "http://www.w3.org/1998/Math/MathML";
// Create a div to test mspace, using Kuma's "offscreen" CSS
document.body.insertAdjacentHTML("afterbegin", "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" + namespaceURI + "'><mspace height='23px' width='77px'></mspace></math></div>");
document.body.insertAdjacentHTML(
"afterbegin",
"<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" +
namespaceURI +
"'><mspace height='23px' width='77px'></mspace></math></div>",
);
div = document.body.firstChild;
box = div.firstChild.firstChild.getBoundingClientRect();
document.body.removeChild(div);
if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) {
if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) {
window.supportsMathML = false;
}
});
}());
})();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -32,51 +32,46 @@ const Cls = (app.views.Content = class Content extends app.View {
}
static initClass() {
this.el = '._content';
this.loadingClass = '_content-loading';
this.events =
{click: 'onClick'};
this.el = "._content";
this.loadingClass = "_content-loading";
this.events = { click: "onClick" };
this.shortcuts = {
altUp: 'scrollStepUp',
altDown: 'scrollStepDown',
pageUp: 'scrollPageUp',
pageDown: 'scrollPageDown',
pageTop: 'scrollToTop',
pageBottom: 'scrollToBottom',
altF: 'onAltF'
altUp: "scrollStepUp",
altDown: "scrollStepDown",
pageUp: "scrollPageUp",
pageDown: "scrollPageDown",
pageTop: "scrollToTop",
pageBottom: "scrollToBottom",
altF: "onAltF",
};
this.routes = {
before: 'beforeRoute',
after: 'afterRoute'
before: "beforeRoute",
after: "afterRoute",
};
}
init() {
this.scrollEl = app.isMobile() ?
(document.scrollingElement || document.body)
:
this.el;
this.scrollEl = app.isMobile()
? document.scrollingElement || document.body
: this.el;
this.scrollMap = {};
this.scrollStack = [];
this.rootPage = new app.views.RootPage;
this.staticPage = new app.views.StaticPage;
this.settingsPage = new app.views.SettingsPage;
this.offlinePage = new app.views.OfflinePage;
this.typePage = new app.views.TypePage;
this.entryPage = new app.views.EntryPage;
this.rootPage = new app.views.RootPage();
this.staticPage = new app.views.StaticPage();
this.settingsPage = new app.views.SettingsPage();
this.offlinePage = new app.views.OfflinePage();
this.typePage = new app.views.TypePage();
this.entryPage = new app.views.EntryPage();
this.entryPage
.on('loading', this.onEntryLoading)
.on('loaded', this.onEntryLoaded);
app
.on('ready', this.onReady)
.on('bootError', this.onBootError);
.on("loading", this.onEntryLoading)
.on("loaded", this.onEntryLoaded);
app.on("ready", this.onReady).on("bootError", this.onBootError);
}
show(view) {
@ -85,7 +80,7 @@ const Cls = (app.views.Content = class Content extends app.View {
if (this.view != null) {
this.view.deactivate();
}
this.html(this.view = view);
this.html((this.view = view));
this.view.activate();
}
}
@ -107,7 +102,7 @@ const Cls = (app.views.Content = class Content extends app.View {
}
smoothScrollTo(value) {
if (app.settings.get('fastScroll')) {
if (app.settings.get("fastScroll")) {
this.scrollTo(value);
} else {
$.smoothScroll(this.scrollEl, value || 0);
@ -144,10 +139,14 @@ const Cls = (app.views.Content = class Content extends app.View {
scrollToTarget() {
let el;
if (this.routeCtx.hash && (el = this.findTargetByHash(this.routeCtx.hash))) {
$.scrollToWithImageLock(el, this.scrollEl, 'top',
{margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top});
$.highlight(el, {className: '_highlight'});
if (
this.routeCtx.hash &&
(el = this.findTargetByHash(this.routeCtx.hash))
) {
$.scrollToWithImageLock(el, this.scrollEl, "top", {
margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top,
});
$.highlight(el, { className: "_highlight" });
} else {
this.scrollTo(this.scrollMap[this.routeCtx.state.id]);
}
@ -159,7 +158,7 @@ const Cls = (app.views.Content = class Content extends app.View {
onBootError() {
this.hideLoading();
this.html(this.tmpl('bootError'));
this.html(this.tmpl("bootError"));
}
onEntryLoading() {
@ -186,8 +185,12 @@ const Cls = (app.views.Content = class Content extends app.View {
}
cacheScrollPosition() {
if (!this.routeCtx || this.routeCtx.hash) { return; }
if (this.routeCtx.path === '/') { return; }
if (!this.routeCtx || this.routeCtx.hash) {
return;
}
if (this.routeCtx.path === "/") {
return;
}
if (this.scrollMap[this.routeCtx.state.id] == null) {
this.scrollStack.push(this.routeCtx.state.id);
@ -200,24 +203,24 @@ const Cls = (app.views.Content = class Content extends app.View {
}
afterRoute(route, context) {
if ((route !== 'entry') && (route !== 'type')) {
if (route !== "entry" && route !== "type") {
resetFavicon();
}
switch (route) {
case 'root':
case "root":
this.show(this.rootPage);
break;
case 'entry':
case "entry":
this.show(this.entryPage);
break;
case 'type':
case "type":
this.show(this.typePage);
break;
case 'settings':
case "settings":
this.show(this.settingsPage);
break;
case 'offline':
case "offline":
this.show(this.offlinePage);
break;
default:
@ -225,37 +228,59 @@ const Cls = (app.views.Content = class Content extends app.View {
}
this.view.onRoute(context);
app.document.setTitle(typeof this.view.getTitle === 'function' ? this.view.getTitle() : undefined);
app.document.setTitle(
typeof this.view.getTitle === "function"
? this.view.getTitle()
: undefined,
);
}
onClick(event) {
const link = $.closestLink($.eventTarget(event), this.el);
if (link && this.isExternalUrl(link.getAttribute('href'))) {
if (link && this.isExternalUrl(link.getAttribute("href"))) {
$.stopEvent(event);
$.popup(link);
}
}
onAltF(event) {
if (!document.activeElement || !$.hasChild(this.el, document.activeElement)) {
__guard__(this.find('a:not(:empty)'), x => x.focus());
if (
!document.activeElement ||
!$.hasChild(this.el, document.activeElement)
) {
__guard__(this.find("a:not(:empty)"), (x) => x.focus());
return $.stopEvent(event);
}
}
findTargetByHash(hash) {
let el = (() => { try { return $.id(decodeURIComponent(hash)); } catch (error) {} })();
if (!el) { el = (() => { try { return $.id(hash); } catch (error1) {} })(); }
let el = (() => {
try {
return $.id(decodeURIComponent(hash));
} catch (error) {}
})();
if (!el) {
el = (() => {
try {
return $.id(hash);
} catch (error1) {}
})();
}
return el;
}
isExternalUrl(url) {
let needle;
return (needle = __guard__(url, x => x.slice(0, 6)), ['http:/', 'https:'].includes(needle));
return (
(needle = __guard__(url, (x) => x.slice(0, 6))),
["http:/", "https:"].includes(needle)
);
}
});
Cls.initClass();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -10,7 +10,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let LINKS = undefined;
const Cls = (app.views.EntryPage = class EntryPage extends app.View {
constructor(...args) {
@ -24,23 +24,21 @@
}
static initClass() {
this.className = '_page';
this.errorClass = '_page-error';
this.events =
{click: 'onClick'};
this.className = "_page";
this.errorClass = "_page-error";
this.events = { click: "onClick" };
this.shortcuts = {
altC: 'onAltC',
altO: 'onAltO'
altC: "onAltC",
altO: "onAltO",
};
this.routes =
{before: 'beforeRoute'};
this.routes = { before: "beforeRoute" };
LINKS = {
home: 'Homepage',
code: 'Source code'
home: "Homepage",
code: "Source code",
};
}
@ -58,61 +56,84 @@
loading() {
this.empty();
this.trigger('loading');
this.trigger("loading");
}
render(content, fromCache) {
if (content == null) { content = ''; }
if (fromCache == null) { fromCache = false; }
if (!this.activated) { return; }
if (content == null) {
content = "";
}
if (fromCache == null) {
fromCache = false;
}
if (!this.activated) {
return;
}
this.empty();
this.subview = new (this.subViewClass())(this.el, this.entry);
$.batchUpdate(this.el, () => {
this.subview.render(content, fromCache);
if (!fromCache) { this.addCopyButtons(); }
if (!fromCache) {
this.addCopyButtons();
}
});
if (app.disabledDocs.findBy('slug', this.entry.doc.slug)) {
if (app.disabledDocs.findBy("slug", this.entry.doc.slug)) {
this.hiddenView = new app.views.HiddenPage(this.el, this.entry);
}
setFaviconForDoc(this.entry.doc);
this.delay(this.polyfillMathML);
this.trigger('loaded');
this.trigger("loaded");
}
addCopyButtons() {
if (!this.copyButton) {
this.copyButton = document.createElement('button');
this.copyButton = document.createElement("button");
this.copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>';
this.copyButton.type = 'button';
this.copyButton.className = '_pre-clip';
this.copyButton.title = 'Copy to clipboard';
this.copyButton.setAttribute('aria-label', 'Copy to clipboard');
this.copyButton.type = "button";
this.copyButton.className = "_pre-clip";
this.copyButton.title = "Copy to clipboard";
this.copyButton.setAttribute("aria-label", "Copy to clipboard");
}
for (var el of Array.from(this.findAllByTag("pre"))) {
el.appendChild(this.copyButton.cloneNode(true));
}
for (var el of Array.from(this.findAllByTag('pre'))) { el.appendChild(this.copyButton.cloneNode(true)); }
}
polyfillMathML() {
if ((window.supportsMathML !== false) || !!this.polyfilledMathML || !this.findByTag('math')) { return; }
if (
window.supportsMathML !== false ||
!!this.polyfilledMathML ||
!this.findByTag("math")
) {
return;
}
this.polyfilledMathML = true;
$.append(document.head, `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`);
$.append(
document.head,
`<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`,
);
}
prepareContent(content) {
if (!this.entry.isIndex() || !this.entry.doc.links) { return content; }
if (!this.entry.isIndex() || !this.entry.doc.links) {
return content;
}
const links = (() => {
const result = [];
for (var link in this.entry.doc.links) {
var url = this.entry.doc.links[link];
result.push(`<a href="${url}" class="_links-link">${LINKS[link]}</a>`);
result.push(
`<a href="${url}" class="_links-link">${LINKS[link]}</a>`,
);
}
return result;
})();
return `<p class="_links">${links.join('')}</p>${content}`;
return `<p class="_links">${links.join("")}</p>${content}`;
}
empty() {
@ -131,11 +152,17 @@
}
subViewClass() {
return app.views[`${$.classify(this.entry.doc.type)}Page`] || app.views.BasePage;
return (
app.views[`${$.classify(this.entry.doc.type)}Page`] ||
app.views.BasePage
);
}
getTitle() {
return this.entry.doc.fullName + (this.entry.isIndex() ? ' documentation' : ` / ${this.entry.name}`);
return (
this.entry.doc.fullName +
(this.entry.isIndex() ? " documentation" : ` / ${this.entry.name}`)
);
}
beforeRoute() {
@ -144,9 +171,13 @@
}
onRoute(context) {
const isSameFile = context.entry.filePath() === (this.entry != null ? this.entry.filePath() : undefined);
const isSameFile =
context.entry.filePath() ===
(this.entry != null ? this.entry.filePath() : undefined);
this.entry = context.entry;
if (!isSameFile) { this.restore() || this.load(); }
if (!isSameFile) {
this.restore() || this.load();
}
}
load() {
@ -157,19 +188,21 @@
abort() {
if (this.xhr) {
this.xhr.abort();
this.xhr = (this.entry = null);
this.xhr = this.entry = null;
}
}
onSuccess(response) {
if (!this.activated) { return; }
if (!this.activated) {
return;
}
this.xhr = null;
this.render(this.prepareContent(response));
}
onError() {
this.xhr = null;
this.render(this.tmpl('pageLoadError'));
this.render(this.tmpl("pageLoadError"));
this.resetClass();
this.addClass(this.constructor.errorClass);
if (app.serviceWorker != null) {
@ -179,7 +212,13 @@
cache() {
let path;
if (this.xhr || !this.entry || this.cacheMap[(path = this.entry.filePath())]) { return; }
if (
this.xhr ||
!this.entry ||
this.cacheMap[(path = this.entry.filePath())]
) {
return;
}
this.cacheMap[path] = this.el.innerHTML;
this.cacheStack.push(path);
@ -199,26 +238,34 @@
onClick(event) {
const target = $.eventTarget(event);
if (target.hasAttribute('data-retry')) {
if (target.hasAttribute("data-retry")) {
$.stopEvent(event);
this.load();
} else if (target.classList.contains('_pre-clip')) {
} else if (target.classList.contains("_pre-clip")) {
$.stopEvent(event);
target.classList.add($.copyToClipboard(target.parentNode.textContent) ? '_pre-clip-success' : '_pre-clip-error');
setTimeout((() => target.className = '_pre-clip'), 2000);
target.classList.add(
$.copyToClipboard(target.parentNode.textContent)
? "_pre-clip-success"
: "_pre-clip-error",
);
setTimeout(() => (target.className = "_pre-clip"), 2000);
}
}
onAltC() {
let link;
if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
if (!(link = this.find("._attribution:last-child ._attribution-link"))) {
return;
}
console.log(link.href + location.hash);
navigator.clipboard.writeText(link.href + location.hash);
}
onAltO() {
let link;
if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; }
if (!(link = this.find("._attribution:last-child ._attribution-link"))) {
return;
}
this.delay(() => $.popup(link.href + location.hash));
}
});

@ -15,11 +15,11 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View {
}
static initClass() {
this.className = '_static';
this.className = "_static";
this.events = {
click: 'onClick',
change: 'onChange'
click: "onClick",
change: "onChange",
};
}
@ -31,41 +31,49 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View {
render() {
if (app.cookieBlocked) {
this.html(this.tmpl('offlineError', 'cookie_blocked'));
this.html(this.tmpl("offlineError", "cookie_blocked"));
return;
}
app.docs.getInstallStatuses(statuses => {
if (!this.activated) { return; }
app.docs.getInstallStatuses((statuses) => {
if (!this.activated) {
return;
}
if (statuses === false) {
this.html(this.tmpl('offlineError', app.db.reason, app.db.error));
this.html(this.tmpl("offlineError", app.db.reason, app.db.error));
} else {
let html = '';
for (var doc of Array.from(app.docs.all())) { html += this.renderDoc(doc, statuses[doc.slug]); }
this.html(this.tmpl('offlinePage', html));
let html = "";
for (var doc of Array.from(app.docs.all())) {
html += this.renderDoc(doc, statuses[doc.slug]);
}
this.html(this.tmpl("offlinePage", html));
this.refreshLinks();
}
});
}
renderDoc(doc, status) {
return app.templates.render('offlineDoc', doc, status);
return app.templates.render("offlineDoc", doc, status);
}
getTitle() {
return 'Offline';
return "Offline";
}
refreshLinks() {
for (var action of ['install', 'update', 'uninstall']) {
this.find(`[data-action-all='${action}']`).classList[this.find(`[data-action='${action}']`) ? 'add' : 'remove']('_show');
for (var action of ["install", "update", "uninstall"]) {
this.find(`[data-action-all='${action}']`).classList[
this.find(`[data-action='${action}']`) ? "add" : "remove"
]("_show");
}
}
docByEl(el) {
let slug;
while (!(slug = el.getAttribute('data-slug'))) { el = el.parentNode; }
return app.docs.findBy('slug', slug);
while (!(slug = el.getAttribute("data-slug"))) {
el = el.parentNode;
}
return app.docs.findBy("slug", slug);
}
docEl(doc) {
@ -79,26 +87,44 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View {
onClick(event) {
let action;
let el = $.eventTarget(event);
if (action = el.getAttribute('data-action')) {
if ((action = el.getAttribute("data-action"))) {
const doc = this.docByEl(el);
if (action === 'update') { action = 'install'; }
doc[action](this.onInstallSuccess.bind(this, doc), this.onInstallError.bind(this, doc), this.onInstallProgress.bind(this, doc));
el.parentNode.innerHTML = `${el.textContent.replace(/e$/, '')}ing…`;
} else if (action = el.getAttribute('data-action-all') || el.parentElement.getAttribute('data-action-all')) {
if ((action === 'uninstall') && !window.confirm('Uninstall all docs?')) { return; }
if (action === "update") {
action = "install";
}
doc[action](
this.onInstallSuccess.bind(this, doc),
this.onInstallError.bind(this, doc),
this.onInstallProgress.bind(this, doc),
);
el.parentNode.innerHTML = `${el.textContent.replace(/e$/, "")}ing…`;
} else if (
(action =
el.getAttribute("data-action-all") ||
el.parentElement.getAttribute("data-action-all"))
) {
if (action === "uninstall" && !window.confirm("Uninstall all docs?")) {
return;
}
app.db.migrate();
for (el of Array.from(this.findAll(`[data-action='${action}']`))) { $.click(el); }
for (el of Array.from(this.findAll(`[data-action='${action}']`))) {
$.click(el);
}
}
}
onInstallSuccess(doc) {
if (!this.activated) { return; }
doc.getInstallStatus(status => {
if (!this.activated) {
return;
}
doc.getInstallStatus((status) => {
let el;
if (!this.activated) { return; }
if (el = this.docEl(doc)) {
if (!this.activated) {
return;
}
if ((el = this.docEl(doc))) {
el.outerHTML = this.renderDoc(doc, status);
$.highlight(el, {className: '_highlight'});
$.highlight(el, { className: "_highlight" });
this.refreshLinks();
}
});
@ -106,24 +132,31 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View {
onInstallError(doc) {
let el;
if (!this.activated) { return; }
if (el = this.docEl(doc)) {
el.lastElementChild.textContent = 'Error';
if (!this.activated) {
return;
}
if ((el = this.docEl(doc))) {
el.lastElementChild.textContent = "Error";
}
}
onInstallProgress(doc, event) {
let el;
if (!this.activated || !event.lengthComputable) { return; }
if (el = this.docEl(doc)) {
if (!this.activated || !event.lengthComputable) {
return;
}
if ((el = this.docEl(doc))) {
const percentage = Math.round((event.loaded * 100) / event.total);
el.lastElementChild.textContent = el.lastElementChild.textContent.replace(/(\s.+)?$/, ` (${percentage}%)`);
el.lastElementChild.textContent = el.lastElementChild.textContent.replace(
/(\s.+)?$/,
` (${percentage}%)`,
);
}
}
onChange(event) {
if (event.target.name === 'autoUpdate') {
app.settings.set('manualUpdate', !event.target.checked);
if (event.target.name === "autoUpdate") {
app.settings.set("manualUpdate", !event.target.checked);
}
}
});

@ -14,26 +14,26 @@ const Cls = (app.views.RootPage = class RootPage extends app.View {
}
static initClass() {
this.events =
{click: 'onClick'};
this.events = { click: "onClick" };
}
init() {
if (!this.isHidden()) { this.setHidden(false); } // reserve space in local storage
if (!this.isHidden()) {
this.setHidden(false);
} // reserve space in local storage
this.render();
}
render() {
this.empty();
const tmpl = app.isAndroidWebview() ?
'androidWarning'
: this.isHidden() ?
'splash'
: app.isMobile() ?
'mobileIntro'
:
'intro';
const tmpl = app.isAndroidWebview()
? "androidWarning"
: this.isHidden()
? "splash"
: app.isMobile()
? "mobileIntro"
: "intro";
this.append(this.tmpl(tmpl));
}
@ -44,17 +44,17 @@ const Cls = (app.views.RootPage = class RootPage extends app.View {
}
setHidden(value) {
app.settings.set('hideIntro', value);
app.settings.set("hideIntro", value);
}
isHidden() {
return app.isSingleDoc() || app.settings.get('hideIntro');
return app.isSingleDoc() || app.settings.get("hideIntro");
}
onRoute() {}
onClick(event) {
if ($.eventTarget(event).hasAttribute('data-hide-intro')) {
if ($.eventTarget(event).hasAttribute("data-hide-intro")) {
$.stopEvent(event);
this.hideIntro();
}

@ -17,39 +17,41 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View {
}
static initClass() {
this.className = '_static';
this.className = "_static";
this.events = {
click: 'onClick',
change: 'onChange'
click: "onClick",
change: "onChange",
};
}
render() {
this.html(this.tmpl('settingsPage', this.currentSettings()));
this.html(this.tmpl("settingsPage", this.currentSettings()));
}
currentSettings() {
const settings = {};
settings.theme = app.settings.get('theme');
settings.smoothScroll = !app.settings.get('fastScroll');
settings.arrowScroll = app.settings.get('arrowScroll');
settings.noAutofocus = app.settings.get('noAutofocus');
settings.autoInstall = app.settings.get('autoInstall');
settings.analyticsConsent = app.settings.get('analyticsConsent');
settings.spaceScroll = app.settings.get('spaceScroll');
settings.spaceTimeout = app.settings.get('spaceTimeout');
settings.theme = app.settings.get("theme");
settings.smoothScroll = !app.settings.get("fastScroll");
settings.arrowScroll = app.settings.get("arrowScroll");
settings.noAutofocus = app.settings.get("noAutofocus");
settings.autoInstall = app.settings.get("autoInstall");
settings.analyticsConsent = app.settings.get("analyticsConsent");
settings.spaceScroll = app.settings.get("spaceScroll");
settings.spaceTimeout = app.settings.get("spaceTimeout");
settings.autoSupported = app.settings.autoSupported;
for (var layout of Array.from(app.settings.LAYOUTS)) { settings[layout] = app.settings.hasLayout(layout); }
for (var layout of Array.from(app.settings.LAYOUTS)) {
settings[layout] = app.settings.hasLayout(layout);
}
return settings;
}
getTitle() {
return 'Preferences';
return "Preferences";
}
setTheme(value) {
app.settings.set('theme', value);
app.settings.set("theme", value);
}
toggleLayout(layout, enable) {
@ -57,20 +59,22 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View {
}
toggleSmoothScroll(enable) {
app.settings.set('fastScroll', !enable);
app.settings.set("fastScroll", !enable);
}
toggleAnalyticsConsent(enable) {
app.settings.set('analyticsConsent', enable ? '1' : '0');
if (!enable) { resetAnalytics(); }
app.settings.set("analyticsConsent", enable ? "1" : "0");
if (!enable) {
resetAnalytics();
}
}
toggleSpaceScroll(enable) {
app.settings.set('spaceScroll', enable ? 1 : 0);
app.settings.set("spaceScroll", enable ? 1 : 0);
}
setScrollTimeout(value) {
return app.settings.set('spaceTimeout', value);
return app.settings.set("spaceTimeout", value);
}
toggle(name, enable) {
@ -78,31 +82,37 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View {
}
export() {
const data = new Blob([JSON.stringify(app.settings.export())], {type: 'application/json'});
const link = document.createElement('a');
const data = new Blob([JSON.stringify(app.settings.export())], {
type: "application/json",
});
const link = document.createElement("a");
link.href = URL.createObjectURL(data);
link.download = 'devdocs.json';
link.style.display = 'none';
link.download = "devdocs.json";
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
import(file, input) {
if (!file || (file.type !== 'application/json')) {
new app.views.Notif('ImportInvalid', {autoHide: false});
if (!file || file.type !== "application/json") {
new app.views.Notif("ImportInvalid", { autoHide: false });
return;
}
const reader = new FileReader();
reader.onloadend = function() {
const data = (() => { try { return JSON.parse(reader.result); } catch (error) {} })();
if (!data || (data.constructor !== Object)) {
new app.views.Notif('ImportInvalid', {autoHide: false});
reader.onloadend = function () {
const data = (() => {
try {
return JSON.parse(reader.result);
} catch (error) {}
})();
if (!data || data.constructor !== Object) {
new app.views.Notif("ImportInvalid", { autoHide: false });
return;
}
app.settings.import(data);
$.trigger(input.form, 'import');
$.trigger(input.form, "import");
};
reader.readAsText(file);
}
@ -110,25 +120,25 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View {
onChange(event) {
const input = event.target;
switch (input.name) {
case 'theme':
case "theme":
this.setTheme(input.value);
break;
case 'layout':
case "layout":
this.toggleLayout(input.value, input.checked);
break;
case 'smoothScroll':
case "smoothScroll":
this.toggleSmoothScroll(input.checked);
break;
case 'import':
case "import":
this.import(input.files[0], input);
break;
case 'analyticsConsent':
case "analyticsConsent":
this.toggleAnalyticsConsent(input.checked);
break;
case 'spaceScroll':
case "spaceScroll":
this.toggleSpaceScroll(input.checked);
break;
case 'spaceTimeout':
case "spaceTimeout":
this.setScrollTimeout(input.value);
break;
default:
@ -138,8 +148,8 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View {
onClick(event) {
const target = $.eventTarget(event);
switch (target.getAttribute('data-action')) {
case 'export':
switch (target.getAttribute("data-action")) {
case "export":
$.stopEvent(event);
this.export();
break;

@ -8,13 +8,13 @@
*/
const Cls = (app.views.StaticPage = class StaticPage extends app.View {
static initClass() {
this.className = '_static';
this.className = "_static";
this.titles = {
about: 'About',
news: 'News',
help: 'User Guide',
notFound: '404'
about: "About",
news: "News",
help: "User Guide",
notFound: "404",
};
}
@ -35,7 +35,7 @@ const Cls = (app.views.StaticPage = class StaticPage extends app.View {
}
onRoute(context) {
this.render(context.page || 'notFound');
this.render(context.page || "notFound");
}
});
Cls.initClass();

@ -8,7 +8,7 @@
*/
const Cls = (app.views.TypePage = class TypePage extends app.View {
static initClass() {
this.className = '_page';
this.className = "_page";
}
deactivate() {
@ -20,7 +20,7 @@ const Cls = (app.views.TypePage = class TypePage extends app.View {
render(type) {
this.type = type;
this.html(this.tmpl('typePage', this.type));
this.html(this.tmpl("typePage", this.type));
setFaviconForDoc(this.type.doc);
}

@ -17,41 +17,49 @@ const Cls = (app.views.Document = class Document extends app.View {
static initClass() {
this.el = document;
this.events =
{visibilitychange: 'onVisibilityChange'};
this.events = { visibilitychange: "onVisibilityChange" };
this.shortcuts = {
help: 'onHelp',
preferences: 'onPreferences',
escape: 'onEscape',
superLeft: 'onBack',
superRight: 'onForward'
help: "onHelp",
preferences: "onPreferences",
escape: "onEscape",
superLeft: "onBack",
superRight: "onForward",
};
this.routes =
{after: 'afterRoute'};
this.routes = { after: "afterRoute" };
}
init() {
this.addSubview((this.menu = new app.views.Menu),
this.addSubview(this.sidebar = new app.views.Sidebar));
if (app.views.Resizer.isSupported()) { this.addSubview(this.resizer = new app.views.Resizer); }
this.addSubview(this.content = new app.views.Content);
if (!app.isSingleDoc() && !app.isMobile()) { this.addSubview(this.path = new app.views.Path); }
if (!app.isSingleDoc()) { this.settings = new app.views.Settings; }
this.addSubview(
(this.menu = new app.views.Menu()),
this.addSubview((this.sidebar = new app.views.Sidebar())),
);
if (app.views.Resizer.isSupported()) {
this.addSubview((this.resizer = new app.views.Resizer()));
}
this.addSubview((this.content = new app.views.Content()));
if (!app.isSingleDoc() && !app.isMobile()) {
this.addSubview((this.path = new app.views.Path()));
}
if (!app.isSingleDoc()) {
this.settings = new app.views.Settings();
}
$.on(document.body, 'click', this.onClick);
$.on(document.body, "click", this.onClick);
this.activate();
}
setTitle(title) {
return this.el.title = title ? `${title} — DevDocs` : 'DevDocs API Documentation';
return (this.el.title = title
? `${title} — DevDocs`
: "DevDocs API Documentation");
}
afterRoute(route) {
if (route === 'settings') {
if (route === "settings") {
if (this.settings != null) {
this.settings.activate();
}
@ -63,26 +71,29 @@ const Cls = (app.views.Document = class Document extends app.View {
}
onVisibilityChange() {
if (this.el.visibilityState !== 'visible') { return; }
this.delay(function() {
if (app.isMobile() !== app.views.Mobile.detect()) { location.reload(); }
if (this.el.visibilityState !== "visible") {
return;
}
, 300);
this.delay(function () {
if (app.isMobile() !== app.views.Mobile.detect()) {
location.reload();
}
}, 300);
}
onHelp() {
app.router.show('/help#shortcuts');
app.router.show("/help#shortcuts");
}
onPreferences() {
app.router.show('/settings');
app.router.show("/settings");
}
onEscape() {
const path = !app.isSingleDoc() || (location.pathname === app.doc.fullPath()) ?
'/'
:
app.doc.fullPath();
const path =
!app.isSingleDoc() || location.pathname === app.doc.fullPath()
? "/"
: app.doc.fullPath();
app.router.show(path);
}
@ -97,16 +108,34 @@ const Cls = (app.views.Document = class Document extends app.View {
onClick(event) {
const target = $.eventTarget(event);
if (!target.hasAttribute('data-behavior')) { return; }
if (!target.hasAttribute("data-behavior")) {
return;
}
$.stopEvent(event);
switch (target.getAttribute('data-behavior')) {
case 'back': history.back(); break;
case 'reload': window.location.reload(); break;
case 'reboot': app.reboot(); break;
case 'hard-reload': app.reload(); break;
case 'reset': if (confirm('Are you sure you want to reset DevDocs?')) { app.reset(); } break;
case 'accept-analytics': Cookies.set('analyticsConsent', '1', {expires: 1e8}) && app.reboot(); break;
case 'decline-analytics': Cookies.set('analyticsConsent', '0', {expires: 1e8}) && app.reboot(); break;
switch (target.getAttribute("data-behavior")) {
case "back":
history.back();
break;
case "reload":
window.location.reload();
break;
case "reboot":
app.reboot();
break;
case "hard-reload":
app.reload();
break;
case "reset":
if (confirm("Are you sure you want to reset DevDocs?")) {
app.reset();
}
break;
case "accept-analytics":
Cookies.set("analyticsConsent", "1", { expires: 1e8 }) && app.reboot();
break;
case "decline-analytics":
Cookies.set("analyticsConsent", "0", { expires: 1e8 }) && app.reboot();
break;
}
}
});

@ -13,25 +13,32 @@ const Cls = (app.views.Menu = class Menu extends app.View {
}
static initClass() {
this.el = '._menu';
this.activeClass = 'active';
this.events =
{click: 'onClick'};
this.el = "._menu";
this.activeClass = "active";
this.events = { click: "onClick" };
}
init() {
$.on(document.body, 'click', this.onGlobalClick);
$.on(document.body, "click", this.onGlobalClick);
}
onClick(event) {
const target = $.eventTarget(event);
if (target.tagName === 'A') { target.blur(); }
if (target.tagName === "A") {
target.blur();
}
}
onGlobalClick(event) {
if (event.which !== 1) { return; }
if (typeof event.target.hasAttribute === 'function' ? event.target.hasAttribute('data-toggle-menu') : undefined) {
if (event.which !== 1) {
return;
}
if (
typeof event.target.hasAttribute === "function"
? event.target.hasAttribute("data-toggle-menu")
: undefined
) {
this.toggleClass(this.constructor.activeClass);
} else if (this.hasClass(this.constructor.activeClass)) {
this.removeClass(this.constructor.activeClass);

@ -10,34 +10,36 @@
*/
const Cls = (app.views.Mobile = class Mobile extends app.View {
static initClass() {
this.className = '_mobile';
this.className = "_mobile";
this.elements = {
body: 'body',
content: '._container',
sidebar: '._sidebar',
docPicker: '._settings ._sidebar'
body: "body",
content: "._container",
sidebar: "._sidebar",
docPicker: "._settings ._sidebar",
};
this.shortcuts =
{escape: 'onEscape'};
this.routes =
{after: 'afterRoute'};
this.shortcuts = { escape: "onEscape" };
this.routes = { after: "afterRoute" };
}
static detect() {
if (Cookies.get('override-mobile-detect') != null) {
return JSON.parse(Cookies.get('override-mobile-detect'));
if (Cookies.get("override-mobile-detect") != null) {
return JSON.parse(Cookies.get("override-mobile-detect"));
}
try {
return (window.matchMedia('(max-width: 480px)').matches) ||
(window.matchMedia('(max-width: 767px)').matches) ||
(window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) ||
// Need to sniff the user agent because some Android and Windows Phone devices don't take
// resolution (dpi) into account when reporting device width/height.
((navigator.userAgent.indexOf('Android') !== -1) && (navigator.userAgent.indexOf('Mobile') !== -1)) ||
(navigator.userAgent.indexOf('IEMobile') !== -1);
return (
window.matchMedia("(max-width: 480px)").matches ||
window.matchMedia("(max-width: 767px)").matches ||
window.matchMedia("(max-height: 767px) and (max-width: 1024px)")
.matches ||
// Need to sniff the user agent because some Android and Windows Phone devices don't take
// resolution (dpi) into account when reporting device width/height.
(navigator.userAgent.indexOf("Android") !== -1 &&
navigator.userAgent.indexOf("Mobile") !== -1) ||
navigator.userAgent.indexOf("IEMobile") !== -1
);
} catch (error) {
return false;
}
@ -67,30 +69,29 @@ const Cls = (app.views.Mobile = class Mobile extends app.View {
}
init() {
$.on($('._search'), 'touchend', this.onTapSearch);
$.on($("._search"), "touchend", this.onTapSearch);
this.toggleSidebar = $('button[data-toggle-sidebar]');
this.toggleSidebar.removeAttribute('hidden');
$.on(this.toggleSidebar, 'click', this.onClickToggleSidebar);
this.toggleSidebar = $("button[data-toggle-sidebar]");
this.toggleSidebar.removeAttribute("hidden");
$.on(this.toggleSidebar, "click", this.onClickToggleSidebar);
this.back = $('button[data-back]');
this.back.removeAttribute('hidden');
$.on(this.back, 'click', this.onClickBack);
this.back = $("button[data-back]");
this.back.removeAttribute("hidden");
$.on(this.back, "click", this.onClickBack);
this.forward = $('button[data-forward]');
this.forward.removeAttribute('hidden');
$.on(this.forward, 'click', this.onClickForward);
this.forward = $("button[data-forward]");
this.forward.removeAttribute("hidden");
$.on(this.forward, "click", this.onClickForward);
this.docPickerTab = $('button[data-tab="doc-picker"]');
this.docPickerTab.removeAttribute('hidden');
$.on(this.docPickerTab, 'click', this.onClickDocPickerTab);
this.docPickerTab.removeAttribute("hidden");
$.on(this.docPickerTab, "click", this.onClickDocPickerTab);
this.settingsTab = $('button[data-tab="settings"]');
this.settingsTab.removeAttribute('hidden');
$.on(this.settingsTab, 'click', this.onClickSettingsTab);
this.settingsTab.removeAttribute("hidden");
$.on(this.settingsTab, "click", this.onClickSettingsTab);
app.document.sidebar.search
.on('searching', this.showSidebar);
app.document.sidebar.search.on("searching", this.showSidebar);
this.activate();
}
@ -103,27 +104,36 @@ const Cls = (app.views.Mobile = class Mobile extends app.View {
}
this.contentTop = window.scrollY;
this.content.style.display = 'none';
this.sidebar.style.display = 'block';
if (selection = this.findByClass(app.views.ListSelect.activeClass)) {
const scrollContainer = window.scrollY === this.body.scrollTop ? this.body : document.documentElement;
$.scrollTo(selection, scrollContainer, 'center');
this.content.style.display = "none";
this.sidebar.style.display = "block";
if ((selection = this.findByClass(app.views.ListSelect.activeClass))) {
const scrollContainer =
window.scrollY === this.body.scrollTop
? this.body
: document.documentElement;
$.scrollTo(selection, scrollContainer, "center");
} else {
window.scrollTo(0, (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || 0);
window.scrollTo(
0,
(this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) ||
0,
);
}
}
hideSidebar() {
if (!this.isSidebarShown()) { return; }
if (!this.isSidebarShown()) {
return;
}
this.sidebarTop = window.scrollY;
this.sidebar.style.display = 'none';
this.content.style.display = 'block';
this.sidebar.style.display = "none";
this.content.style.display = "block";
window.scrollTo(0, this.contentTop || 0);
}
isSidebarShown() {
return this.sidebar.style.display !== 'none';
return this.sidebar.style.display !== "none";
}
onClickBack() {
@ -135,7 +145,11 @@ const Cls = (app.views.Mobile = class Mobile extends app.View {
}
onClickToggleSidebar() {
if (this.isSidebarShown()) { this.hideSidebar(); } else { this.showSidebar(); }
if (this.isSidebarShown()) {
this.hideSidebar();
} else {
this.showSidebar();
}
}
onClickDocPickerTab(event) {
@ -150,18 +164,18 @@ const Cls = (app.views.Mobile = class Mobile extends app.View {
showDocPicker() {
window.scrollTo(0, 0);
this.docPickerTab.classList.add('active');
this.settingsTab.classList.remove('active');
this.docPicker.style.display = 'block';
this.content.style.display = 'none';
this.docPickerTab.classList.add("active");
this.settingsTab.classList.remove("active");
this.docPicker.style.display = "block";
this.content.style.display = "none";
}
showSettings() {
window.scrollTo(0, 0);
this.docPickerTab.classList.remove('active');
this.settingsTab.classList.add('active');
this.docPicker.style.display = 'none';
this.content.style.display = 'block';
this.docPickerTab.classList.remove("active");
this.settingsTab.classList.add("active");
this.docPicker.style.display = "none";
this.content.style.display = "block";
}
onTapSearch() {
@ -175,22 +189,22 @@ const Cls = (app.views.Mobile = class Mobile extends app.View {
afterRoute(route) {
this.hideSidebar();
if (route === 'settings') {
if (route === "settings") {
this.showDocPicker();
} else {
this.content.style.display = 'block';
this.content.style.display = "block";
}
if (page.canGoBack()) {
this.back.removeAttribute('disabled');
this.back.removeAttribute("disabled");
} else {
this.back.setAttribute('disabled', 'disabled');
this.back.setAttribute("disabled", "disabled");
}
if (page.canGoForward()) {
this.forward.removeAttribute('disabled');
this.forward.removeAttribute("disabled");
} else {
this.forward.setAttribute('disabled', 'disabled');
this.forward.setAttribute("disabled", "disabled");
}
}
});

@ -15,33 +15,36 @@ const Cls = (app.views.Path = class Path extends app.View {
}
static initClass() {
this.className = '_path';
this.attributes =
{role: 'complementary'};
this.events =
{click: 'onClick'};
this.routes =
{after: 'afterRoute'};
this.className = "_path";
this.attributes = { role: "complementary" };
this.events = { click: "onClick" };
this.routes = { after: "afterRoute" };
}
render(...args) {
this.html(this.tmpl('path', ...Array.from(args)));
this.html(this.tmpl("path", ...Array.from(args)));
this.show();
}
show() {
if (!this.el.parentNode) { this.prependTo(app.el); }
if (!this.el.parentNode) {
this.prependTo(app.el);
}
}
hide() {
if (this.el.parentNode) { $.remove(this.el); }
if (this.el.parentNode) {
$.remove(this.el);
}
}
onClick(event) {
let link;
if (link = $.closestLink(event.target, this.el)) { this.clicked = true; }
if ((link = $.closestLink(event.target, this.el))) {
this.clicked = true;
}
}
afterRoute(route, context) {

@ -7,7 +7,7 @@
* DS206: Consider reworking classes to avoid initClass
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let MIN = undefined;
let MAX = undefined;
const Cls = (app.views.Resizer = class Resizer extends app.View {
@ -19,54 +19,66 @@
}
static initClass() {
this.className = '_resizer';
this.className = "_resizer";
this.events = {
dragstart: 'onDragStart',
dragend: 'onDragEnd'
dragstart: "onDragStart",
dragend: "onDragEnd",
};
MIN = 260;
MAX = 600;
}
static isSupported() {
return 'ondragstart' in document.createElement('div') && !app.isMobile();
return "ondragstart" in document.createElement("div") && !app.isMobile();
}
init() {
this.el.setAttribute('draggable', 'true');
this.appendTo($('._app'));
this.el.setAttribute("draggable", "true");
this.appendTo($("._app"));
}
resize(value, save) {
value -= app.el.offsetLeft;
if (!(value > 0)) { return; }
if (!(value > 0)) {
return;
}
value = Math.min(Math.max(Math.round(value), MIN), MAX);
const newSize = `${value}px`;
document.documentElement.style.setProperty('--sidebarWidth', newSize);
if (save) { app.settings.setSize(value); }
document.documentElement.style.setProperty("--sidebarWidth", newSize);
if (save) {
app.settings.setSize(value);
}
}
onDragStart(event) {
event.dataTransfer.effectAllowed = 'link';
event.dataTransfer.setData('Text', '');
$.on(window, 'dragover', this.onDrag);
event.dataTransfer.effectAllowed = "link";
event.dataTransfer.setData("Text", "");
$.on(window, "dragover", this.onDrag);
}
onDrag(event) {
const value = event.pageX;
if (!(value > 0)) { return; }
if (!(value > 0)) {
return;
}
this.lastDragValue = value;
if (this.lastDrag && (this.lastDrag > (Date.now() - 50))) { return; }
if (this.lastDrag && this.lastDrag > Date.now() - 50) {
return;
}
this.lastDrag = Date.now();
this.resize(value, false);
}
onDragEnd(event) {
$.off(window, 'dragover', this.onDrag);
let value = event.pageX || (event.screenX - window.screenX);
if (this.lastDragValue && !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)) { // https://github.com/freeCodeCamp/devdocs/issues/265
$.off(window, "dragover", this.onDrag);
let value = event.pageX || event.screenX - window.screenX;
if (
this.lastDragValue &&
!(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)
) {
// https://github.com/freeCodeCamp/devdocs/issues/265
value = this.lastDragValue;
}
this.resize(value, true);

@ -10,7 +10,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let SIDEBAR_HIDDEN_LAYOUT = undefined;
const Cls = (app.views.Settings = class Settings extends app.View {
constructor(...args) {
@ -23,29 +23,28 @@
}
static initClass() {
SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden';
this.el = '._settings';
SIDEBAR_HIDDEN_LAYOUT = "_sidebar-hidden";
this.el = "._settings";
this.elements = {
sidebar: '._sidebar',
sidebar: "._sidebar",
saveBtn: 'button[type="submit"]',
backBtn: 'button[data-back]'
backBtn: "button[data-back]",
};
this.events = {
import: 'onImport',
change: 'onChange',
submit: 'onSubmit',
click: 'onClick'
import: "onImport",
change: "onChange",
submit: "onSubmit",
click: "onClick",
};
this.shortcuts =
{enter: 'onEnter'};
this.shortcuts = { enter: "onEnter" };
}
init() {
this.addSubview(this.docPicker = new app.views.DocPicker);
this.addSubview((this.docPicker = new app.views.DocPicker()));
}
activate() {
@ -59,18 +58,22 @@
if (super.deactivate(...arguments)) {
this.resetClass();
this.docPicker.detach();
if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) { document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT); }
if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) {
document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT);
}
}
}
render() {
this.docPicker.appendTo(this.sidebar);
this.refreshElements();
this.addClass('_in');
this.addClass("_in");
}
save(options) {
if (options == null) { options = {}; }
if (options == null) {
options = {};
}
if (!this.saving) {
let docs;
this.saving = true;
@ -82,16 +85,19 @@
app.settings.setDocs(docs);
}
this.saveBtn.textContent = 'Saving\u2026';
const disabledDocs = new app.collections.Docs((() => {
const result = [];
for (var doc of Array.from(app.docs.all())) { if (docs.indexOf(doc.slug) === -1) {
result.push(doc);
this.saveBtn.textContent = "Saving\u2026";
const disabledDocs = new app.collections.Docs(
(() => {
const result = [];
for (var doc of Array.from(app.docs.all())) {
if (docs.indexOf(doc.slug) === -1) {
result.push(doc);
}
}
}
return result;
})());
disabledDocs.uninstall(function() {
return result;
})(),
);
disabledDocs.uninstall(function () {
app.db.migrate();
return app.reload();
});
@ -99,7 +105,7 @@
}
onChange() {
this.addClass('_dirty');
this.addClass("_dirty");
}
onEnter() {
@ -112,15 +118,17 @@
}
onImport() {
this.addClass('_dirty');
this.save({import: true});
this.addClass("_dirty");
this.save({ import: true });
}
onClick(event) {
if (event.which !== 1) { return; }
if (event.which !== 1) {
return;
}
if (event.target === this.backBtn) {
$.stopEvent(event);
app.router.show('/');
app.router.show("/");
}
}
});

@ -10,18 +10,17 @@
*/
const Cls = (app.views.ListFocus = class ListFocus extends app.View {
static initClass() {
this.activeClass = 'focus';
this.events =
{click: 'onClick'};
this.activeClass = "focus";
this.events = { click: "onClick" };
this.shortcuts = {
up: 'onUp',
down: 'onDown',
left: 'onLeft',
enter: 'onEnter',
superEnter: 'onSuperEnter',
escape: 'blur'
up: "onUp",
down: "onDown",
left: "onLeft",
enter: "onEnter",
superEnter: "onSuperEnter",
escape: "blur",
};
}
@ -39,41 +38,51 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
}
focus(el, options) {
if (options == null) { options = {}; }
if (options == null) {
options = {};
}
if (el && !el.classList.contains(this.constructor.activeClass)) {
this.blur();
el.classList.add(this.constructor.activeClass);
if (options.silent !== true) { $.trigger(el, 'focus'); }
if (options.silent !== true) {
$.trigger(el, "focus");
}
}
}
blur() {
let cursor;
if (cursor = this.getCursor()) {
if ((cursor = this.getCursor())) {
cursor.classList.remove(this.constructor.activeClass);
$.trigger(cursor, 'blur');
$.trigger(cursor, "blur");
}
}
getCursor() {
return this.findByClass(this.constructor.activeClass) || this.findByClass(app.views.ListSelect.activeClass);
return (
this.findByClass(this.constructor.activeClass) ||
this.findByClass(app.views.ListSelect.activeClass)
);
}
findNext(cursor) {
let next;
if (next = cursor.nextSibling) {
if (next.tagName === 'A') {
if ((next = cursor.nextSibling)) {
if (next.tagName === "A") {
return next;
} else if (next.tagName === 'SPAN') { // pagination link
} else if (next.tagName === "SPAN") {
// pagination link
$.click(next);
return this.findNext(cursor);
} else if (next.tagName === 'DIV') { // sub-list
if (cursor.className.indexOf(' open') >= 0) {
} else if (next.tagName === "DIV") {
// sub-list
if (cursor.className.indexOf(" open") >= 0) {
return this.findFirst(next) || this.findNext(next);
} else {
return this.findNext(next);
}
} else if (next.tagName === 'H6') { // title
} else if (next.tagName === "H6") {
// title
return this.findNext(next);
}
} else if (cursor.parentNode !== this.el) {
@ -83,11 +92,14 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
findFirst(cursor) {
let first;
if (!(first = cursor.firstChild)) { return; }
if (!(first = cursor.firstChild)) {
return;
}
if (first.tagName === 'A') {
if (first.tagName === "A") {
return first;
} else if (first.tagName === 'SPAN') { // pagination link
} else if (first.tagName === "SPAN") {
// pagination link
$.click(first);
return this.findFirst(cursor);
}
@ -95,19 +107,22 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
findPrev(cursor) {
let prev;
if (prev = cursor.previousSibling) {
if (prev.tagName === 'A') {
if ((prev = cursor.previousSibling)) {
if (prev.tagName === "A") {
return prev;
} else if (prev.tagName === 'SPAN') { // pagination link
} else if (prev.tagName === "SPAN") {
// pagination link
$.click(prev);
return this.findPrev(cursor);
} else if (prev.tagName === 'DIV') { // sub-list
if (prev.previousSibling.className.indexOf('open') >= 0) {
} else if (prev.tagName === "DIV") {
// sub-list
if (prev.previousSibling.className.indexOf("open") >= 0) {
return this.findLast(prev) || this.findPrev(prev);
} else {
return this.findPrev(prev);
}
} else if (prev.tagName === 'H6') { // title
} else if (prev.tagName === "H6") {
// title
return this.findPrev(prev);
}
} else if (cursor.parentNode !== this.el) {
@ -117,13 +132,17 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
findLast(cursor) {
let last;
if (!(last = cursor.lastChild)) { return; }
if (!(last = cursor.lastChild)) {
return;
}
if (last.tagName === 'A') {
if (last.tagName === "A") {
return last;
} else if ((last.tagName === 'SPAN') || (last.tagName === 'H6')) { // pagination link or title
} else if (last.tagName === "SPAN" || last.tagName === "H6") {
// pagination link or title
return this.findPrev(last);
} else if (last.tagName === 'DIV') { // sub-list
} else if (last.tagName === "DIV") {
// sub-list
return this.findLast(last);
}
}
@ -133,7 +152,7 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
if ((cursor = this.getCursor())) {
this.focusOnNextFrame(this.findNext(cursor));
} else {
this.focusOnNextFrame(this.findByTag('a'));
this.focusOnNextFrame(this.findByTag("a"));
}
}
@ -142,37 +161,45 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View {
if ((cursor = this.getCursor())) {
this.focusOnNextFrame(this.findPrev(cursor));
} else {
this.focusOnNextFrame(this.findLastByTag('a'));
this.focusOnNextFrame(this.findLastByTag("a"));
}
}
onLeft() {
const cursor = this.getCursor();
if (cursor && !cursor.classList.contains(app.views.ListFold.activeClass) && (cursor.parentNode !== this.el)) {
if (
cursor &&
!cursor.classList.contains(app.views.ListFold.activeClass) &&
cursor.parentNode !== this.el
) {
const prev = cursor.parentNode.previousSibling;
if (prev && prev.classList.contains(app.views.ListFold.targetClass)) { this.focusOnNextFrame(cursor.parentNode.previousSibling); }
if (prev && prev.classList.contains(app.views.ListFold.targetClass)) {
this.focusOnNextFrame(cursor.parentNode.previousSibling);
}
}
}
onEnter() {
let cursor;
if (cursor = this.getCursor()) {
if ((cursor = this.getCursor())) {
$.click(cursor);
}
}
onSuperEnter() {
let cursor;
if (cursor = this.getCursor()) {
if ((cursor = this.getCursor())) {
$.popup(cursor);
}
}
onClick(event) {
if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
if (event.which !== 1 || event.metaKey || event.ctrlKey) {
return;
}
const target = $.eventTarget(event);
if (target.tagName === 'A') {
this.focus(target, {silent: true});
if (target.tagName === "A") {
this.focus(target, { silent: true });
}
}
});

@ -10,32 +10,37 @@
*/
const Cls = (app.views.ListFold = class ListFold extends app.View {
static initClass() {
this.targetClass = '_list-dir';
this.handleClass = '_list-arrow';
this.activeClass = 'open';
this.events =
{click: 'onClick'};
this.targetClass = "_list-dir";
this.handleClass = "_list-arrow";
this.activeClass = "open";
this.events = { click: "onClick" };
this.shortcuts = {
left: 'onLeft',
right: 'onRight'
left: "onLeft",
right: "onRight",
};
}
constructor(el) { this.onLeft = this.onLeft.bind(this); this.onRight = this.onRight.bind(this); this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); }
constructor(el) {
this.onLeft = this.onLeft.bind(this);
this.onRight = this.onRight.bind(this);
this.onClick = this.onClick.bind(this);
this.el = el;
super(...arguments);
}
open(el) {
if (el && !el.classList.contains(this.constructor.activeClass)) {
el.classList.add(this.constructor.activeClass);
$.trigger(el, 'open');
$.trigger(el, "open");
}
}
close(el) {
if (el && el.classList.contains(this.constructor.activeClass)) {
el.classList.remove(this.constructor.activeClass);
$.trigger(el, 'close');
$.trigger(el, "close");
}
}
@ -55,36 +60,55 @@ const Cls = (app.views.ListFold = class ListFold extends app.View {
}
getCursor() {
return this.findByClass(app.views.ListFocus.activeClass) || this.findByClass(app.views.ListSelect.activeClass);
return (
this.findByClass(app.views.ListFocus.activeClass) ||
this.findByClass(app.views.ListSelect.activeClass)
);
}
onLeft() {
const cursor = this.getCursor();
if (cursor != null ? cursor.classList.contains(this.constructor.activeClass) : undefined) {
if (
cursor != null
? cursor.classList.contains(this.constructor.activeClass)
: undefined
) {
this.close(cursor);
}
}
onRight() {
const cursor = this.getCursor();
if (cursor != null ? cursor.classList.contains(this.constructor.targetClass) : undefined) {
if (
cursor != null
? cursor.classList.contains(this.constructor.targetClass)
: undefined
) {
this.open(cursor);
}
}
onClick(event) {
if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
if (!event.pageY) { return; } // ignore fabricated clicks
if (event.which !== 1 || event.metaKey || event.ctrlKey) {
return;
}
if (!event.pageY) {
return;
} // ignore fabricated clicks
let el = $.eventTarget(event);
if (el.parentNode.tagName.toUpperCase() === 'SVG') { el = el.parentNode; }
if (el.parentNode.tagName.toUpperCase() === "SVG") {
el = el.parentNode;
}
if (el.classList.contains(this.constructor.handleClass)) {
$.stopEvent(event);
this.toggle(el.parentNode);
} else if (el.classList.contains(this.constructor.targetClass)) {
if (el.hasAttribute('href')) {
if (el.hasAttribute("href")) {
if (el.classList.contains(this.constructor.activeClass)) {
if (el.classList.contains(app.views.ListSelect.activeClass)) { this.close(el); }
if (el.classList.contains(app.views.ListSelect.activeClass)) {
this.close(el);
}
} else {
this.open(el);
}

@ -10,36 +10,43 @@
*/
const Cls = (app.views.ListSelect = class ListSelect extends app.View {
static initClass() {
this.activeClass = 'active';
this.events =
{click: 'onClick'};
this.activeClass = "active";
this.events = { click: "onClick" };
}
constructor(el) { this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); }
constructor(el) {
this.onClick = this.onClick.bind(this);
this.el = el;
super(...arguments);
}
deactivate() {
if (super.deactivate(...arguments)) { this.deselect(); }
if (super.deactivate(...arguments)) {
this.deselect();
}
}
select(el) {
this.deselect();
if (el) {
el.classList.add(this.constructor.activeClass);
$.trigger(el, 'select');
$.trigger(el, "select");
}
}
deselect() {
let selection;
if (selection = this.getSelection()) {
if ((selection = this.getSelection())) {
selection.classList.remove(this.constructor.activeClass);
$.trigger(selection, 'deselect');
$.trigger(selection, "deselect");
}
}
selectByHref(href) {
if (__guard__(this.getSelection(), x => x.getAttribute('href')) !== href) {
if (
__guard__(this.getSelection(), (x) => x.getAttribute("href")) !== href
) {
this.select(this.find(`a[href='${href}']`));
}
}
@ -53,9 +60,11 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View {
}
onClick(event) {
if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; }
if (event.which !== 1 || event.metaKey || event.ctrlKey) {
return;
}
const target = $.eventTarget(event);
if (target.tagName === 'A') {
if (target.tagName === "A") {
this.select(target);
}
}
@ -63,5 +72,7 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View {
Cls.initClass();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -10,7 +10,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let PER_PAGE = undefined;
const Cls = (app.views.PaginatedList = class PaginatedList extends app.View {
static initClass() {
@ -21,7 +21,12 @@
let base;
this.onClick = this.onClick.bind(this);
this.data = data;
if (((base = this.constructor.events || (this.constructor.events = {}))).click == null) { base.click = 'onClick'; }
if (
(base = this.constructor.events || (this.constructor.events = {}))
.click == null
) {
base.click = "onClick";
}
super(...arguments);
}
@ -42,11 +47,13 @@
}
renderPage(page) {
return this.render(this.data.slice(((page - 1) * PER_PAGE), (page * PER_PAGE)));
return this.render(
this.data.slice((page - 1) * PER_PAGE, page * PER_PAGE),
);
}
renderPageLink(count) {
return this.tmpl('sidebarPageLink', count);
return this.tmpl("sidebarPageLink", count);
}
renderPrevLink(page) {
@ -54,7 +61,7 @@
}
renderNextLink(page) {
return this.renderPageLink(this.data.length - (page * PER_PAGE));
return this.renderPageLink(this.data.length - page * PER_PAGE);
}
totalPages() {
@ -64,17 +71,27 @@
paginate(link) {
$.lockScroll(link.nextSibling || link.previousSibling, () => {
$.batchUpdate(this.el, () => {
if (link.nextSibling) { this.paginatePrev(link); } else { this.paginateNext(link); }
if (link.nextSibling) {
this.paginatePrev(link);
} else {
this.paginateNext(link);
}
});
});
}
paginateNext() {
if (this.el.lastChild) { this.remove(this.el.lastChild); } // remove link
if (this.page >= 2) { this.hideTopPage(); } // keep previous page into view
if (this.el.lastChild) {
this.remove(this.el.lastChild);
} // remove link
if (this.page >= 2) {
this.hideTopPage();
} // keep previous page into view
this.page++;
this.append(this.renderPage(this.page));
if (this.page < this.totalPages()) { this.append(this.renderNextLink(this.page)); }
if (this.page < this.totalPages()) {
this.append(this.renderNextLink(this.page));
}
}
paginatePrev() {
@ -82,37 +99,55 @@
this.hideBottomPage();
this.page--;
this.prepend(this.renderPage(this.page - 1)); // previous page is offset by one
if (this.page >= 3) { this.prepend(this.renderPrevLink(this.page - 1)); }
if (this.page >= 3) {
this.prepend(this.renderPrevLink(this.page - 1));
}
}
paginateTo(object) {
const index = this.data.indexOf(object);
if (index >= PER_PAGE) {
for (let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.paginateNext(); }
for (
let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end;
asc ? i < end : i > end;
asc ? i++ : i--
) {
this.paginateNext();
}
}
}
hideTopPage() {
const n = this.page <= 2 ?
PER_PAGE
:
PER_PAGE + 1; // remove link
for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.firstChild); }
const n = this.page <= 2 ? PER_PAGE : PER_PAGE + 1; // remove link
for (
let i = 0, end = n, asc = 0 <= end;
asc ? i < end : i > end;
asc ? i++ : i--
) {
this.remove(this.el.firstChild);
}
this.prepend(this.renderPrevLink(this.page));
}
hideBottomPage() {
const n = this.page === this.totalPages() ?
(this.data.length % PER_PAGE) || PER_PAGE
:
PER_PAGE + 1; // remove link
for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.lastChild); }
const n =
this.page === this.totalPages()
? this.data.length % PER_PAGE || PER_PAGE
: PER_PAGE + 1; // remove link
for (
let i = 0, end = n, asc = 0 <= end;
asc ? i < end : i > end;
asc ? i++ : i--
) {
this.remove(this.el.lastChild);
}
this.append(this.renderNextLink(this.page - 1));
}
onClick(event) {
const target = $.eventTarget(event);
if (target.tagName === 'SPAN') { // link
if (target.tagName === "SPAN") {
// link
$.stopEvent(event);
this.paginate(target);
}

@ -12,15 +12,16 @@
const Cls = (app.views.News = class News extends app.views.Notif {
static initClass() {
this.className += ' _notif-news';
this.defautOptions =
{autoHide: 30000};
this.className += " _notif-news";
this.defautOptions = { autoHide: 30000 };
}
init() {
this.unreadNews = this.getUnreadNews();
if (this.unreadNews.length) { this.show(); }
if (this.unreadNews.length) {
this.show();
}
this.markAllAsRead();
}
@ -30,12 +31,16 @@ const Cls = (app.views.News = class News extends app.views.Notif {
getUnreadNews() {
let time;
if (!(time = this.getLastReadTime())) { return []; }
if (!(time = this.getLastReadTime())) {
return [];
}
return (() => {
const result = [];
for (var news of Array.from(app.news)) {
if (new Date(news[0]).getTime() <= time) { break; }
if (new Date(news[0]).getTime() <= time) {
break;
}
result.push(news);
}
return result;
@ -47,11 +52,11 @@ const Cls = (app.views.News = class News extends app.views.Notif {
}
getLastReadTime() {
return app.settings.get('news');
return app.settings.get("news");
}
markAllAsRead() {
app.settings.set('news', this.getLastNewsTime());
app.settings.set("news", this.getLastNewsTime());
}
});
Cls.initClass();

@ -9,23 +9,30 @@
*/
const Cls = (app.views.Notice = class Notice extends app.View {
static initClass() {
this.className = '_notice';
this.attributes =
{role: 'alert'};
this.className = "_notice";
this.attributes = { role: "alert" };
}
constructor(type, ...rest) { this.type = type; [...this.args] = Array.from(rest); super(...arguments); }
constructor(type, ...rest) {
this.type = type;
[...this.args] = Array.from(rest);
super(...arguments);
}
init() {
this.activate();
}
activate() {
if (super.activate(...arguments)) { this.show(); }
if (super.activate(...arguments)) {
this.show();
}
}
deactivate() {
if (super.deactivate(...arguments)) { this.hide(); }
if (super.deactivate(...arguments)) {
this.hide();
}
}
show() {

@ -9,22 +9,21 @@
*/
const Cls = (app.views.Notif = class Notif extends app.View {
static initClass() {
this.className = '_notif';
this.activeClass = '_in';
this.attributes =
{role: 'alert'};
this.defautOptions =
{autoHide: 15000};
this.events =
{click: 'onClick'};
this.className = "_notif";
this.activeClass = "_in";
this.attributes = { role: "alert" };
this.defautOptions = { autoHide: 15000 };
this.events = { click: "onClick" };
}
constructor(type, options) {
this.onClick = this.onClick.bind(this);
this.type = type;
if (options == null) { options = {}; }
if (options == null) {
options = {};
}
this.options = options;
this.options = $.extend({}, this.constructor.defautOptions, this.options);
super(...arguments);
@ -45,7 +44,9 @@ const Cls = (app.views.Notif = class Notif extends app.View {
this.appendTo(document.body);
this.el.offsetWidth; // force reflow
this.addClass(this.constructor.activeClass);
if (this.options.autoHide) { this.timeout = this.delay(this.hide, this.options.autoHide); }
if (this.options.autoHide) {
this.timeout = this.delay(this.hide, this.options.autoHide);
}
}
}
@ -63,15 +64,20 @@ const Cls = (app.views.Notif = class Notif extends app.View {
const notifications = $$(`.${app.views.Notif.className}`);
if (notifications.length) {
const lastNotif = notifications[notifications.length - 1];
this.el.style.top = lastNotif.offsetTop + lastNotif.offsetHeight + 16 + 'px';
this.el.style.top =
lastNotif.offsetTop + lastNotif.offsetHeight + 16 + "px";
}
}
onClick(event) {
if (event.which !== 1) { return; }
if (event.which !== 1) {
return;
}
const target = $.eventTarget(event);
if (target.hasAttribute('data-behavior')) { return; }
if ((target.tagName !== 'A') || target.classList.contains('_notif-close')) {
if (target.hasAttribute("data-behavior")) {
return;
}
if (target.tagName !== "A" || target.classList.contains("_notif-close")) {
$.stopEvent(event);
this.hide();
}

@ -9,10 +9,9 @@
const Cls = (app.views.Tip = class Tip extends app.views.Notif {
static initClass() {
this.className = '_notif _notif-tip';
this.defautOptions =
{autoHide: false};
this.className = "_notif _notif-tip";
this.defautOptions = { autoHide: false };
}
render() {

@ -12,34 +12,47 @@
const Cls = (app.views.Updates = class Updates extends app.views.Notif {
static initClass() {
this.className += ' _notif-news';
this.defautOptions =
{autoHide: 30000};
this.className += " _notif-news";
this.defautOptions = { autoHide: 30000 };
}
init() {
this.lastUpdateTime = this.getLastUpdateTime();
this.updatedDocs = this.getUpdatedDocs();
this.updatedDisabledDocs = this.getUpdatedDisabledDocs();
if ((this.updatedDocs.length > 0) || (this.updatedDisabledDocs.length > 0)) { this.show(); }
if (this.updatedDocs.length > 0 || this.updatedDisabledDocs.length > 0) {
this.show();
}
this.markAllAsRead();
}
render() {
this.html(app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs));
this.html(
app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs),
);
}
getUpdatedDocs() {
if (!this.lastUpdateTime) { return []; }
return Array.from(app.docs.all()).filter((doc) => doc.mtime > this.lastUpdateTime);
if (!this.lastUpdateTime) {
return [];
}
return Array.from(app.docs.all()).filter(
(doc) => doc.mtime > this.lastUpdateTime,
);
}
getUpdatedDisabledDocs() {
if (!this.lastUpdateTime) { return []; }
if (!this.lastUpdateTime) {
return [];
}
return (() => {
const result = [];
for (var doc of Array.from(app.disabledDocs.all())) { if ((doc.mtime > this.lastUpdateTime) && app.docs.findBy('slug_without_version', doc.slug_without_version)) {
for (var doc of Array.from(app.disabledDocs.all())) {
if (
doc.mtime > this.lastUpdateTime &&
app.docs.findBy("slug_without_version", doc.slug_without_version)
) {
result.push(doc);
}
}
@ -48,11 +61,16 @@ const Cls = (app.views.Updates = class Updates extends app.views.Notif {
}
getLastUpdateTime() {
return app.settings.get('version');
return app.settings.get("version");
}
markAllAsRead() {
app.settings.set('version', app.config.env === 'production' ? app.config.version : Math.floor(Date.now() / 1000));
app.settings.set(
"version",
app.config.env === "production"
? app.config.version
: Math.floor(Date.now() / 1000),
);
}
});
Cls.initClass();

@ -9,31 +9,44 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
app.views.BasePage = class BasePage extends app.View {
constructor(el, entry) { this.paintCode = this.paintCode.bind(this); this.el = el; this.entry = entry; super(...arguments); }
constructor(el, entry) {
this.paintCode = this.paintCode.bind(this);
this.el = el;
this.entry = entry;
super(...arguments);
}
deactivate() {
if (super.deactivate(...arguments)) {
return this.highlightNodes = [];
return (this.highlightNodes = []);
}
}
render(content, fromCache) {
if (fromCache == null) { fromCache = false; }
if (fromCache == null) {
fromCache = false;
}
this.highlightNodes = [];
this.previousTiming = null;
if (!this.constructor.className) { this.addClass(`_${this.entry.doc.type}`); }
if (!this.constructor.className) {
this.addClass(`_${this.entry.doc.type}`);
}
this.html(content);
if (!fromCache) { this.highlightCode(); }
if (!fromCache) {
this.highlightCode();
}
this.activate();
if (this.afterRender) { this.delay(this.afterRender); }
if (this.afterRender) {
this.delay(this.afterRender);
}
if (this.highlightNodes.length > 0) {
$.requestAnimationFrame(() => $.requestAnimationFrame(this.paintCode));
}
}
highlightCode() {
for (var el of Array.from(this.findAll('pre[data-language]'))) {
var language = el.getAttribute('data-language');
for (var el of Array.from(this.findAll("pre[data-language]"))) {
var language = el.getAttribute("data-language");
el.classList.add(`language-${language}`);
this.highlightNodes.push(el);
}
@ -41,23 +54,34 @@ app.views.BasePage = class BasePage extends app.View {
paintCode(timing) {
if (this.previousTiming) {
if (Math.round(1000 / (timing - this.previousTiming)) > 50) { // fps
this.nodesPerFrame = Math.round(Math.min(this.nodesPerFrame * 1.25, 50));
if (Math.round(1000 / (timing - this.previousTiming)) > 50) {
// fps
this.nodesPerFrame = Math.round(
Math.min(this.nodesPerFrame * 1.25, 50),
);
} else {
this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * .8, 10));
this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * 0.8, 10));
}
} else {
this.nodesPerFrame = 10;
}
for (var el of Array.from(this.highlightNodes.splice(0, this.nodesPerFrame))) {
for (var el of Array.from(
this.highlightNodes.splice(0, this.nodesPerFrame),
)) {
var clipEl;
if (clipEl = el.lastElementChild) { $.remove(clipEl); }
if ((clipEl = el.lastElementChild)) {
$.remove(clipEl);
}
Prism.highlightElement(el);
if (clipEl) { $.append(el, clipEl); }
if (clipEl) {
$.append(el, clipEl);
}
}
if (this.highlightNodes.length > 0) { $.requestAnimationFrame(this.paintCode); }
if (this.highlightNodes.length > 0) {
$.requestAnimationFrame(this.paintCode);
}
this.previousTiming = timing;
}
};

@ -8,20 +8,24 @@
*/
const Cls = (app.views.HiddenPage = class HiddenPage extends app.View {
static initClass() {
this.events =
{click: 'onClick'};
this.events = { click: "onClick" };
}
constructor(el, entry) { this.onClick = this.onClick.bind(this); this.el = el; this.entry = entry; super(...arguments); }
constructor(el, entry) {
this.onClick = this.onClick.bind(this);
this.el = el;
this.entry = entry;
super(...arguments);
}
init() {
this.addSubview(this.notice = new app.views.Notice('disabledDoc'));
this.addSubview((this.notice = new app.views.Notice("disabledDoc")));
this.activate();
}
onClick(event) {
let link;
if (link = $.closestLink(event.target, this.el)) {
if ((link = $.closestLink(event.target, this.el))) {
$.stopEvent(event);
$.popup(link);
}

@ -10,46 +10,54 @@
*/
//= require views/pages/base
const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage {
const Cls = (app.views.JqueryPage = class JqueryPage extends (
app.views.BasePage
) {
constructor(...args) {
this.onIframeLoaded = this.onIframeLoaded.bind(this);
super(...args);
}
static initClass() {
this.demoClassName = '_jquery-demo';
this.demoClassName = "_jquery-demo";
}
afterRender() {
// Prevent jQuery Mobile's demo iframes from scrolling the page
for (var iframe of Array.from(this.findAllByTag('iframe'))) {
iframe.style.display = 'none';
$.on(iframe, 'load', this.onIframeLoaded);
for (var iframe of Array.from(this.findAllByTag("iframe"))) {
iframe.style.display = "none";
$.on(iframe, "load", this.onIframeLoaded);
}
return this.runExamples();
}
onIframeLoaded(event) {
event.target.style.display = '';
$.off(event.target, 'load', this.onIframeLoaded);
event.target.style.display = "";
$.off(event.target, "load", this.onIframeLoaded);
}
runExamples() {
for (var el of Array.from(this.findAllByClass('entry-example'))) {
try { this.runExample(el); } catch (error) {}
for (var el of Array.from(this.findAllByClass("entry-example"))) {
try {
this.runExample(el);
} catch (error) {}
}
}
runExample(el) {
let iframe;
const source = el.getElementsByClassName('syntaxhighlighter')[0];
if (!source || (source.innerHTML.indexOf('!doctype') === -1)) { return; }
const source = el.getElementsByClassName("syntaxhighlighter")[0];
if (!source || source.innerHTML.indexOf("!doctype") === -1) {
return;
}
if (!(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0])) {
iframe = document.createElement('iframe');
if (
!(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0])
) {
iframe = document.createElement("iframe");
iframe.className = this.constructor.demoClassName;
iframe.width = '100%';
iframe.width = "100%";
iframe.height = 200;
el.appendChild(iframe);
}
@ -60,8 +68,13 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage
}
fixIframeSource(source) {
source = source.replace('"/resources/', '"https://api.jquery.com/resources/'); // attr(), keydown()
source = source.replace('</head>', `\
source = source.replace(
'"/resources/',
'"https://api.jquery.com/resources/',
); // attr(), keydown()
source = source.replace(
"</head>",
`\
<style>
html, body { border: 0; margin: 0; padding: 0; }
body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; }
@ -75,7 +88,7 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage
});
</script>
</head>\
`
`,
);
return source.replace(/<script>/gi, '<script nonce="devdocs">');
}

@ -10,19 +10,23 @@
const Cls = (app.views.RdocPage = class RdocPage extends app.views.BasePage {
static initClass() {
this.events =
{click: 'onClick'};
this.events = { click: "onClick" };
}
onClick(event) {
if (!event.target.classList.contains('method-click-advice')) { return; }
if (!event.target.classList.contains("method-click-advice")) {
return;
}
$.stopEvent(event);
const source = $('.method-source-code', event.target.closest('.method-detail'));
const isShown = source.style.display === 'block';
const source = $(
".method-source-code",
event.target.closest(".method-detail"),
);
const isShown = source.style.display === "block";
source.style.display = isShown ? 'none' : 'block';
return event.target.textContent = isShown ? 'Show source' : 'Hide source';
source.style.display = isShown ? "none" : "block";
return (event.target.textContent = isShown ? "Show source" : "Hide source");
}
});
Cls.initClass();

@ -8,28 +8,33 @@
*/
//= require views/pages/base
const Cls = (app.views.SqlitePage = class SqlitePage extends app.views.BasePage {
const Cls = (app.views.SqlitePage = class SqlitePage extends (
app.views.BasePage
) {
constructor(...args) {
this.onClick = this.onClick.bind(this);
super(...args);
}
static initClass() {
this.events =
{click: 'onClick'};
this.events = { click: "onClick" };
}
onClick(event) {
let el, id;
if (!(id = event.target.getAttribute('data-toggle'))) { return; }
if (!(el = this.find(`#${id}`))) { return; }
if (!(id = event.target.getAttribute("data-toggle"))) {
return;
}
if (!(el = this.find(`#${id}`))) {
return;
}
$.stopEvent(event);
if (el.style.display === 'none') {
el.style.display = 'block';
event.target.textContent = 'hide';
if (el.style.display === "none") {
el.style.display = "block";
event.target.textContent = "hide";
} else {
el.style.display = 'none';
event.target.textContent = 'show';
el.style.display = "none";
event.target.textContent = "show";
}
}
});

@ -7,19 +7,24 @@
*/
//= require views/pages/base
const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends app.views.BasePage {
const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends (
app.views.BasePage
) {
static initClass() {
this.events =
{click: 'onClick'};
this.events = { click: "onClick" };
}
onClick(event) {
if (!event.target.classList.contains('show-all')) { return; }
if (!event.target.classList.contains("show-all")) {
return;
}
$.stopEvent(event);
let el = event.target;
while (el.tagName !== 'TABLE') { el = el.parentNode; }
el.classList.add('show-all');
while (el.tagName !== "TABLE") {
el = el.parentNode;
}
el.classList.add("show-all");
}
});
Cls.initClass();

@ -9,7 +9,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let SEARCH_PARAM = undefined;
let HASH_RGX = undefined;
const Cls = (app.views.Search = class Search extends app.View {
@ -33,86 +33,106 @@
static initClass() {
SEARCH_PARAM = app.config.search_param;
this.el = '._search';
this.activeClass = '_search-active';
this.el = "._search";
this.activeClass = "_search-active";
this.elements = {
input: '._search-input',
resetLink: '._search-clear'
input: "._search-input",
resetLink: "._search-clear",
};
this.events = {
input: 'onInput',
click: 'onClick',
submit: 'onSubmit'
input: "onInput",
click: "onClick",
submit: "onSubmit",
};
this.shortcuts = {
typing: 'focus',
altG: 'google',
altS: 'stackoverflow',
altD: 'duckduckgo'
typing: "focus",
altG: "google",
altS: "stackoverflow",
altD: "duckduckgo",
};
this.routes =
{after: 'afterRoute'};
this.routes = { after: "afterRoute" };
HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.*)`);
}
init() {
this.addSubview(this.scope = new app.views.SearchScope(this.el));
this.addSubview((this.scope = new app.views.SearchScope(this.el)));
this.searcher = new app.Searcher;
this.searcher
.on('results', this.onResults)
.on('end', this.onEnd);
this.searcher = new app.Searcher();
this.searcher.on("results", this.onResults).on("end", this.onEnd);
this.scope
.on('change', this.onScopeChange);
this.scope.on("change", this.onScopeChange);
app.on('ready', this.onReady);
$.on(window, 'hashchange', this.searchUrl);
$.on(window, 'focus', this.onWindowFocus);
app.on("ready", this.onReady);
$.on(window, "hashchange", this.searchUrl);
$.on(window, "focus", this.onWindowFocus);
}
focus() {
if (document.activeElement === this.input) { return; }
if (app.settings.get('noAutofocus')) { return; }
this.input.focus();
if (document.activeElement === this.input) {
return;
}
if (app.settings.get("noAutofocus")) {
return;
}
this.input.focus();
}
autoFocus() {
if (app.isMobile() || $.isAndroid() || $.isIOS()) { return; }
if ((document.activeElement != null ? document.activeElement.tagName : undefined) === 'INPUT') { return; }
if (app.settings.get('noAutofocus')) { return; }
if (app.isMobile() || $.isAndroid() || $.isIOS()) {
return;
}
if (
(document.activeElement != null
? document.activeElement.tagName
: undefined) === "INPUT"
) {
return;
}
if (app.settings.get("noAutofocus")) {
return;
}
this.input.focus();
}
onWindowFocus(event) {
if (event.target === window) { return this.autoFocus(); }
if (event.target === window) {
return this.autoFocus();
}
}
getScopeDoc() {
if (this.scope.isActive()) { return this.scope.getScope(); }
if (this.scope.isActive()) {
return this.scope.getScope();
}
}
reset(force) {
if (force || !this.input.value) { this.scope.reset(); }
if (force || !this.input.value) {
this.scope.reset();
}
this.el.reset();
this.onInput();
this.autoFocus();
}
onReady() {
this.value = '';
this.value = "";
this.delay(this.onInput);
}
onInput() {
if ((this.value == null) || // ignore events pre-"ready"
(this.value === this.input.value)) { return; }
if (
this.value == null || // ignore events pre-"ready"
this.value === this.input.value
) {
return;
}
this.value = this.input.value;
if (this.value.length) {
@ -123,25 +143,33 @@
}
search(url) {
if (url == null) { url = false; }
if (url == null) {
url = false;
}
this.addClass(this.constructor.activeClass);
this.trigger('searching');
this.trigger("searching");
this.hasResults = null;
this.flags = {urlSearch: url, initialResults: true};
this.searcher.find(this.scope.getScope().entries.all(), 'text', this.value);
this.flags = { urlSearch: url, initialResults: true };
this.searcher.find(
this.scope.getScope().entries.all(),
"text",
this.value,
);
}
searchUrl() {
let value;
if (location.pathname === '/') {
if (location.pathname === "/") {
this.scope.searchUrl();
} else if (!app.router.isIndex()) {
return;
}
if (!(value = this.extractHashValue())) { return; }
this.input.value = (this.value = value);
if (!(value = this.extractHashValue())) {
return;
}
this.input.value = this.value = value;
this.input.setSelectionRange(value.length, value.length);
this.search(true);
return true;
@ -149,13 +177,15 @@
clear() {
this.removeClass(this.constructor.activeClass);
this.trigger('clear');
this.trigger("clear");
}
externalSearch(url) {
let value;
if (value = this.value) {
if (this.scope.name()) { value = `${this.scope.name()} ${value}`; }
if ((value = this.value)) {
if (this.scope.name()) {
value = `${this.scope.name()} ${value}`;
}
$.popup(`${url}${encodeURIComponent(value)}`);
this.reset();
}
@ -174,13 +204,17 @@
}
onResults(results) {
if (results.length) { this.hasResults = true; }
this.trigger('results', results, this.flags);
if (results.length) {
this.hasResults = true;
}
this.trigger("results", results, this.flags);
this.flags.initialResults = false;
}
onEnd() {
if (!this.hasResults) { this.trigger('noresults'); }
if (!this.hasResults) {
this.trigger("noresults");
}
}
onClick(event) {
@ -195,14 +229,24 @@
}
onScopeChange() {
this.value = '';
this.value = "";
this.onInput();
}
afterRoute(name, context) {
if ((app.shortcuts.eventInProgress != null ? app.shortcuts.eventInProgress.name : undefined) === 'escape') { return; }
if (!context.init && app.router.isIndex()) { this.reset(true); }
if (context.hash) { this.delay(this.searchUrl); }
if (
(app.shortcuts.eventInProgress != null
? app.shortcuts.eventInProgress.name
: undefined) === "escape"
) {
return;
}
if (!context.init && app.router.isIndex()) {
this.reset(true);
}
if (context.hash) {
this.delay(this.searchUrl);
}
$.requestAnimationFrame(this.autoFocus);
}
@ -215,7 +259,12 @@
}
getHashValue() {
try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {}
try {
return __guard__(
HASH_RGX.exec($.urlDecode(location.hash)),
(x) => x[1],
);
} catch (error) {}
}
});
Cls.initClass();
@ -223,5 +272,7 @@
})();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -9,41 +9,49 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
(function() {
(function () {
let SEARCH_PARAM = undefined;
let HASH_RGX = undefined;
const Cls = (app.views.SearchScope = class SearchScope extends app.View {
static initClass() {
SEARCH_PARAM = app.config.search_param;
this.elements = {
input: '._search-input',
tag: '._search-tag'
input: "._search-input",
tag: "._search-tag",
};
this.events = {
click: 'onClick',
keydown: 'onKeydown',
textInput: 'onTextInput'
click: "onClick",
keydown: "onKeydown",
textInput: "onTextInput",
};
this.routes =
{after: 'afterRoute'};
this.routes = { after: "afterRoute" };
HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.+?) .`);
}
constructor(el) { this.onResults = this.onResults.bind(this); this.reset = this.reset.bind(this); this.doScopeSearch = this.doScopeSearch.bind(this); this.onClick = this.onClick.bind(this); this.onKeydown = this.onKeydown.bind(this); this.onTextInput = this.onTextInput.bind(this); this.afterRoute = this.afterRoute.bind(this); this.el = el; super(...arguments); }
constructor(el) {
this.onResults = this.onResults.bind(this);
this.reset = this.reset.bind(this);
this.doScopeSearch = this.doScopeSearch.bind(this);
this.onClick = this.onClick.bind(this);
this.onKeydown = this.onKeydown.bind(this);
this.onTextInput = this.onTextInput.bind(this);
this.afterRoute = this.afterRoute.bind(this);
this.el = el;
super(...arguments);
}
init() {
this.placeholder = this.input.getAttribute('placeholder');
this.placeholder = this.input.getAttribute("placeholder");
this.searcher = new app.SynchronousSearcher({
fuzzy_min_length: 2,
max_results: 1
max_results: 1,
});
this.searcher.on('results', this.onResults);
this.searcher.on("results", this.onResults);
}
getScope() {
@ -55,26 +63,34 @@
}
name() {
return (this.doc != null ? this.doc.name : undefined);
return this.doc != null ? this.doc.name : undefined;
}
search(value, searchDisabled) {
if (searchDisabled == null) { searchDisabled = false; }
if (this.doc) { return; }
this.searcher.find(app.docs.all(), 'text', value);
if (!this.doc && searchDisabled) { this.searcher.find(app.disabledDocs.all(), 'text', value); }
if (searchDisabled == null) {
searchDisabled = false;
}
if (this.doc) {
return;
}
this.searcher.find(app.docs.all(), "text", value);
if (!this.doc && searchDisabled) {
this.searcher.find(app.disabledDocs.all(), "text", value);
}
}
searchUrl() {
let value;
if (value = this.extractHashValue()) {
if ((value = this.extractHashValue())) {
this.search(value, true);
}
}
onResults(results) {
let doc;
if (!(doc = results[0])) { return; }
if (!(doc = results[0])) {
return;
}
if (app.docs.contains(doc)) {
this.selectDoc(doc);
} else {
@ -84,45 +100,49 @@
selectDoc(doc) {
const previousDoc = this.doc;
if (doc === previousDoc) { return; }
if (doc === previousDoc) {
return;
}
this.doc = doc;
this.tag.textContent = doc.fullName;
this.tag.style.display = 'block';
this.tag.style.display = "block";
this.input.removeAttribute('placeholder');
this.input.removeAttribute("placeholder");
this.input.value = this.input.value.slice(this.input.selectionStart);
this.input.style.paddingLeft = this.tag.offsetWidth + 10 + 'px';
this.input.style.paddingLeft = this.tag.offsetWidth + 10 + "px";
$.trigger(this.input, 'input');
this.trigger('change', this.doc, previousDoc);
$.trigger(this.input, "input");
this.trigger("change", this.doc, previousDoc);
}
redirectToDoc(doc) {
const {
hash
} = location;
app.router.replaceHash('');
const { hash } = location;
app.router.replaceHash("");
location.assign(doc.fullPath() + hash);
}
reset() {
if (!this.doc) { return; }
if (!this.doc) {
return;
}
const previousDoc = this.doc;
this.doc = null;
this.tag.textContent = '';
this.tag.style.display = 'none';
this.tag.textContent = "";
this.tag.style.display = "none";
this.input.setAttribute('placeholder', this.placeholder);
this.input.style.paddingLeft = '';
this.input.setAttribute("placeholder", this.placeholder);
this.input.style.paddingLeft = "";
this.trigger('change', null, previousDoc);
this.trigger("change", null, previousDoc);
}
doScopeSearch(event) {
this.search(this.input.value.slice(0, this.input.selectionStart));
if (this.doc) { $.stopEvent(event); }
if (this.doc) {
$.stopEvent(event);
}
}
onClick(event) {
@ -133,38 +153,54 @@
}
onKeydown(event) {
if (event.which === 8) { // backspace
if (this.doc && (this.input.selectionEnd === 0)) {
if (event.which === 8) {
// backspace
if (this.doc && this.input.selectionEnd === 0) {
this.reset();
$.stopEvent(event);
}
} else if (!this.doc && this.input.value && !$.isChromeForAndroid()) {
if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) { return; }
if ((event.which === 9) || // tab
((event.which === 32) && app.isMobile())) { // space
if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) {
return;
}
if (
event.which === 9 || // tab
(event.which === 32 && app.isMobile())
) {
// space
this.doScopeSearch(event);
}
}
}
onTextInput(event) {
if (!$.isChromeForAndroid()) { return; }
if (!this.doc && this.input.value && (event.data === ' ')) {
if (!$.isChromeForAndroid()) {
return;
}
if (!this.doc && this.input.value && event.data === " ") {
this.doScopeSearch(event);
}
}
extractHashValue() {
let value;
if (value = this.getHashValue()) {
const newHash = $.urlDecode(location.hash).replace(`#${SEARCH_PARAM}=${value} `, `#${SEARCH_PARAM}=`);
if ((value = this.getHashValue())) {
const newHash = $.urlDecode(location.hash).replace(
`#${SEARCH_PARAM}=${value} `,
`#${SEARCH_PARAM}=`,
);
app.router.replaceHash(newHash);
return value;
}
}
getHashValue() {
try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {}
try {
return __guard__(
HASH_RGX.exec($.urlDecode(location.hash)),
(x) => x[1],
);
} catch (error) {}
}
afterRoute(name, context) {
@ -178,5 +214,7 @@
})();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -20,65 +20,75 @@ const Cls = (app.views.DocList = class DocList extends app.View {
}
static initClass() {
this.className = '_list';
this.attributes =
{role: 'navigation'};
this.className = "_list";
this.attributes = { role: "navigation" };
this.events = {
open: 'onOpen',
close: 'onClose',
click: 'onClick'
open: "onOpen",
close: "onClose",
click: "onClick",
};
this.routes =
{after: 'afterRoute'};
this.routes = { after: "afterRoute" };
this.elements = {
disabledTitle: '._list-title',
disabledList: '._disabled-list'
disabledTitle: "._list-title",
disabledList: "._disabled-list",
};
}
init() {
this.lists = {};
this.addSubview(this.listFocus = new app.views.ListFocus(this.el));
this.addSubview(this.listFold = new app.views.ListFold(this.el));
this.addSubview(this.listSelect = new app.views.ListSelect(this.el));
this.addSubview((this.listFocus = new app.views.ListFocus(this.el)));
this.addSubview((this.listFold = new app.views.ListFold(this.el)));
this.addSubview((this.listSelect = new app.views.ListSelect(this.el)));
app.on('ready', this.render);
app.on("ready", this.render);
}
activate() {
if (super.activate(...arguments)) {
for (var slug in this.lists) { var list = this.lists[slug]; list.activate(); }
for (var slug in this.lists) {
var list = this.lists[slug];
list.activate();
}
this.listSelect.selectCurrent();
}
}
deactivate() {
if (super.deactivate(...arguments)) {
for (var slug in this.lists) { var list = this.lists[slug]; list.deactivate(); }
for (var slug in this.lists) {
var list = this.lists[slug];
list.deactivate();
}
}
}
render() {
let html = '';
let html = "";
for (var doc of Array.from(app.docs.all())) {
html += this.tmpl('sidebarDoc', doc, {fullName: app.docs.countAllBy('name', doc.name) > 1});
html += this.tmpl("sidebarDoc", doc, {
fullName: app.docs.countAllBy("name", doc.name) > 1,
});
}
this.html(html);
if (!app.isSingleDoc() && (app.disabledDocs.size() !== 0)) { this.renderDisabled(); }
if (!app.isSingleDoc() && app.disabledDocs.size() !== 0) {
this.renderDisabled();
}
}
renderDisabled() {
this.append(this.tmpl('sidebarDisabled', {count: app.disabledDocs.size()}));
this.append(
this.tmpl("sidebarDisabled", { count: app.disabledDocs.size() }),
);
this.refreshElements();
this.renderDisabledList();
}
renderDisabledList() {
if (app.settings.get('hideDisabled')) {
if (app.settings.get("hideDisabled")) {
this.removeDisabledList();
} else {
this.appendDisabledList();
@ -87,60 +97,67 @@ const Cls = (app.views.DocList = class DocList extends app.View {
appendDisabledList() {
let doc;
let html = '';
let html = "";
const docs = [].concat(...Array.from(app.disabledDocs.all() || []));
while ((doc = docs.shift())) {
if (doc.version != null) {
var versions = '';
var versions = "";
while (true) {
versions += this.tmpl('sidebarDoc', doc, {disabled: true});
if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) { break; }
versions += this.tmpl("sidebarDoc", doc, { disabled: true });
if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) {
break;
}
doc = docs.shift();
}
html += this.tmpl('sidebarDisabledVersionedDoc', doc, versions);
html += this.tmpl("sidebarDisabledVersionedDoc", doc, versions);
} else {
html += this.tmpl('sidebarDoc', doc, {disabled: true});
html += this.tmpl("sidebarDoc", doc, { disabled: true });
}
}
this.append(this.tmpl('sidebarDisabledList', html));
this.disabledTitle.classList.add('open-title');
this.append(this.tmpl("sidebarDisabledList", html));
this.disabledTitle.classList.add("open-title");
this.refreshElements();
}
removeDisabledList() {
if (this.disabledList) { $.remove(this.disabledList); }
this.disabledTitle.classList.remove('open-title');
if (this.disabledList) {
$.remove(this.disabledList);
}
this.disabledTitle.classList.remove("open-title");
this.refreshElements();
}
reset(options) {
if (options == null) { options = {}; }
if (options == null) {
options = {};
}
this.listSelect.deselect();
if (this.listFocus != null) {
this.listFocus.blur();
}
this.listFold.reset();
if (options.revealCurrent || app.isSingleDoc()) { this.revealCurrent(); }
if (options.revealCurrent || app.isSingleDoc()) {
this.revealCurrent();
}
}
onOpen(event) {
$.stopEvent(event);
const doc = app.docs.findBy('slug', event.target.getAttribute('data-slug'));
const doc = app.docs.findBy("slug", event.target.getAttribute("data-slug"));
if (doc && !this.lists[doc.slug]) {
this.lists[doc.slug] = doc.types.isEmpty() ?
new app.views.EntryList(doc.entries.all())
:
new app.views.TypeList(doc);
this.lists[doc.slug] = doc.types.isEmpty()
? new app.views.EntryList(doc.entries.all())
: new app.views.TypeList(doc);
$.after(event.target, this.lists[doc.slug].el);
}
}
onClose(event) {
$.stopEvent(event);
const doc = app.docs.findBy('slug', event.target.getAttribute('data-slug'));
const doc = app.docs.findBy("slug", event.target.getAttribute("data-slug"));
if (doc && this.lists[doc.slug]) {
this.lists[doc.slug].detach();
@ -154,7 +171,9 @@ const Cls = (app.views.DocList = class DocList extends app.View {
reveal(model) {
this.openDoc(model.doc);
if (model.type) { this.openType(model.getType()); }
if (model.type) {
this.openType(model.getType());
}
this.focus(model);
this.paginateTo(model);
this.scrollTo(model);
@ -168,14 +187,18 @@ const Cls = (app.views.DocList = class DocList extends app.View {
revealCurrent() {
let model;
if (model = app.router.context.type || app.router.context.entry) {
if ((model = app.router.context.type || app.router.context.entry)) {
this.reveal(model);
this.select(model);
}
}
openDoc(doc) {
if (app.disabledDocs.contains(doc) && doc.version) { this.listFold.open(this.find(`[data-slug='${doc.slug_without_version}']`)); }
if (app.disabledDocs.contains(doc) && doc.version) {
this.listFold.open(
this.find(`[data-slug='${doc.slug_without_version}']`),
);
}
this.listFold.open(this.find(`[data-slug='${doc.slug}']`));
}
@ -184,7 +207,9 @@ const Cls = (app.views.DocList = class DocList extends app.View {
}
openType(type) {
this.listFold.open(this.lists[type.doc.slug].find(`[data-slug='${type.slug}']`));
this.listFold.open(
this.lists[type.doc.slug].find(`[data-slug='${type.slug}']`),
);
}
paginateTo(model) {
@ -194,29 +219,37 @@ const Cls = (app.views.DocList = class DocList extends app.View {
}
scrollTo(model) {
$.scrollTo(this.find(`a[href='${model.fullPath()}']`), null, 'top', {margin: app.isMobile() ? 48 : 0});
$.scrollTo(this.find(`a[href='${model.fullPath()}']`), null, "top", {
margin: app.isMobile() ? 48 : 0,
});
}
toggleDisabled() {
if (this.disabledTitle.classList.contains('open-title')) {
if (this.disabledTitle.classList.contains("open-title")) {
this.removeDisabledList();
app.settings.set('hideDisabled', true);
app.settings.set("hideDisabled", true);
} else {
this.appendDisabledList();
app.settings.set('hideDisabled', false);
app.settings.set("hideDisabled", false);
}
}
onClick(event) {
let slug;
const target = $.eventTarget(event);
if (this.disabledTitle && $.hasChild(this.disabledTitle, target) && (target.tagName !== 'A')) {
if (
this.disabledTitle &&
$.hasChild(this.disabledTitle, target) &&
target.tagName !== "A"
) {
$.stopEvent(event);
this.toggleDisabled();
} else if (slug = target.getAttribute('data-enable')) {
} else if ((slug = target.getAttribute("data-enable"))) {
$.stopEvent(event);
const doc = app.disabledDocs.findBy('slug', slug);
if (doc) { app.enableDoc(doc, this.onEnabled, this.onEnabled); }
const doc = app.disabledDocs.findBy("slug", slug);
if (doc) {
app.enableDoc(doc, this.onEnabled, this.onEnabled);
}
}
}
@ -227,7 +260,9 @@ const Cls = (app.views.DocList = class DocList extends app.View {
afterRoute(route, context) {
if (context.init) {
if (this.activated) { this.reset({revealCurrent: true}); }
if (this.activated) {
this.reset({ revealCurrent: true });
}
} else {
this.select(context.type || context.entry);
}

@ -19,56 +19,71 @@ const Cls = (app.views.DocPicker = class DocPicker extends app.View {
}
static initClass() {
this.className = '_list _list-picker';
this.className = "_list _list-picker";
this.events = {
mousedown: 'onMouseDown',
mouseup: 'onMouseUp'
mousedown: "onMouseDown",
mouseup: "onMouseUp",
};
}
init() {
this.addSubview(this.listFold = new app.views.ListFold(this.el));
this.addSubview((this.listFold = new app.views.ListFold(this.el)));
}
activate() {
if (super.activate(...arguments)) {
this.render();
$.on(this.el, 'focus', this.onDOMFocus, true);
$.on(this.el, "focus", this.onDOMFocus, true);
}
}
deactivate() {
if (super.deactivate(...arguments)) {
this.empty();
$.off(this.el, 'focus', this.onDOMFocus, true);
$.off(this.el, "focus", this.onDOMFocus, true);
this.focusEl = null;
}
}
render() {
let doc;
let html = this.tmpl('docPickerHeader');
let docs = app.docs.all().concat(...Array.from(app.disabledDocs.all() || []));
let html = this.tmpl("docPickerHeader");
let docs = app.docs
.all()
.concat(...Array.from(app.disabledDocs.all() || []));
while ((doc = docs.shift())) {
if (doc.version != null) {
var versions;
[docs, versions] = Array.from(this.extractVersions(docs, doc));
html += this.tmpl('sidebarVersionedDoc', doc, this.renderVersions(versions), {open: app.docs.contains(doc)});
html += this.tmpl(
"sidebarVersionedDoc",
doc,
this.renderVersions(versions),
{ open: app.docs.contains(doc) },
);
} else {
html += this.tmpl('sidebarLabel', doc, {checked: app.docs.contains(doc)});
html += this.tmpl("sidebarLabel", doc, {
checked: app.docs.contains(doc),
});
}
}
this.html(html + this.tmpl('docPickerNote'));
this.html(html + this.tmpl("docPickerNote"));
$.requestAnimationFrame(() => __guard__(this.findByTag('input'), x => x.focus()));
$.requestAnimationFrame(() =>
__guard__(this.findByTag("input"), (x) => x.focus()),
);
}
renderVersions(docs) {
let html = '';
for (var doc of Array.from(docs)) { html += this.tmpl('sidebarLabel', doc, {checked: app.docs.contains(doc)}); }
let html = "";
for (var doc of Array.from(docs)) {
html += this.tmpl("sidebarLabel", doc, {
checked: app.docs.contains(doc),
});
}
return html;
}
@ -87,8 +102,9 @@ const Cls = (app.views.DocPicker = class DocPicker extends app.View {
}
getSelectedDocs() {
return Array.from(this.findAllByTag('input')).filter((input) => (input != null ? input.checked : undefined)).map((input) =>
input.name);
return Array.from(this.findAllByTag("input"))
.filter((input) => (input != null ? input.checked : undefined))
.map((input) => input.name);
}
onMouseDown() {
@ -100,25 +116,37 @@ const Cls = (app.views.DocPicker = class DocPicker extends app.View {
}
onDOMFocus(event) {
const {
target
} = event;
if (target.tagName === 'INPUT') {
if ((!this.mouseDown || !(Date.now() < (this.mouseDown + 100))) && (!this.mouseUp || !(Date.now() < (this.mouseUp + 100)))) {
$.scrollTo(target.parentNode, null, 'continuous');
const { target } = event;
if (target.tagName === "INPUT") {
if (
(!this.mouseDown || !(Date.now() < this.mouseDown + 100)) &&
(!this.mouseUp || !(Date.now() < this.mouseUp + 100))
) {
$.scrollTo(target.parentNode, null, "continuous");
}
} else if (target.classList.contains(app.views.ListFold.targetClass)) {
target.blur();
if (!this.mouseDown || !(Date.now() < (this.mouseDown + 100))) {
if (this.focusEl === $('input', target.nextElementSibling)) {
if (target.classList.contains(app.views.ListFold.activeClass)) { this.listFold.close(target); }
if (!this.mouseDown || !(Date.now() < this.mouseDown + 100)) {
if (this.focusEl === $("input", target.nextElementSibling)) {
if (target.classList.contains(app.views.ListFold.activeClass)) {
this.listFold.close(target);
}
let prev = target.previousElementSibling;
while ((prev.tagName !== 'LABEL') && !prev.classList.contains(app.views.ListFold.targetClass)) { prev = prev.previousElementSibling; }
if (prev.classList.contains(app.views.ListFold.activeClass)) { prev = $.makeArray($$('input', prev.nextElementSibling)).pop(); }
while (
prev.tagName !== "LABEL" &&
!prev.classList.contains(app.views.ListFold.targetClass)
) {
prev = prev.previousElementSibling;
}
if (prev.classList.contains(app.views.ListFold.activeClass)) {
prev = $.makeArray($$("input", prev.nextElementSibling)).pop();
}
this.delay(() => prev.focus());
} else {
if (!target.classList.contains(app.views.ListFold.activeClass)) { this.listFold.open(target); }
this.delay(() => $('input', target.nextElementSibling).focus());
if (!target.classList.contains(app.views.ListFold.activeClass)) {
this.listFold.open(target);
}
this.delay(() => $("input", target.nextElementSibling).focus());
}
}
}
@ -128,5 +156,7 @@ const Cls = (app.views.DocPicker = class DocPicker extends app.View {
Cls.initClass();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -9,13 +9,18 @@
*/
//= require views/list/paginated_list
const Cls = (app.views.EntryList = class EntryList extends app.views.PaginatedList {
const Cls = (app.views.EntryList = class EntryList extends (
app.views.PaginatedList
) {
static initClass() {
this.tagName = 'div';
this.className = '_list _list-sub';
this.tagName = "div";
this.className = "_list _list-sub";
}
constructor(entries) { this.entries = entries; super(...arguments); }
constructor(entries) {
this.entries = entries;
super(...arguments);
}
init() {
this.renderPaginated();
@ -23,7 +28,7 @@ const Cls = (app.views.EntryList = class EntryList extends app.views.PaginatedLi
}
render(entries) {
return this.tmpl('sidebarEntry', entries);
return this.tmpl("sidebarEntry", entries);
}
});
Cls.initClass();

@ -10,16 +10,23 @@
*/
const Cls = (app.views.Results = class Results extends app.View {
static initClass() {
this.className = '_list';
this.events =
{click: 'onClick'};
this.routes =
{after: 'afterRoute'};
this.className = "_list";
this.events = { click: "onClick" };
this.routes = { after: "afterRoute" };
}
constructor(sidebar, search) { this.onResults = this.onResults.bind(this); this.onNoResults = this.onNoResults.bind(this); this.onClear = this.onClear.bind(this); this.afterRoute = this.afterRoute.bind(this); this.onClick = this.onClick.bind(this); this.sidebar = sidebar; this.search = search; super(...arguments); }
constructor(sidebar, search) {
this.onResults = this.onResults.bind(this);
this.onNoResults = this.onNoResults.bind(this);
this.onClear = this.onClear.bind(this);
this.afterRoute = this.afterRoute.bind(this);
this.onClick = this.onClick.bind(this);
this.sidebar = sidebar;
this.search = search;
super(...arguments);
}
deactivate() {
if (super.deactivate(...arguments)) {
@ -28,29 +35,37 @@ const Cls = (app.views.Results = class Results extends app.View {
}
init() {
this.addSubview(this.listFocus = new app.views.ListFocus(this.el));
this.addSubview(this.listSelect = new app.views.ListSelect(this.el));
this.addSubview((this.listFocus = new app.views.ListFocus(this.el)));
this.addSubview((this.listSelect = new app.views.ListSelect(this.el)));
this.search
.on('results', this.onResults)
.on('noresults', this.onNoResults)
.on('clear', this.onClear);
.on("results", this.onResults)
.on("noresults", this.onNoResults)
.on("clear", this.onClear);
}
onResults(entries, flags) {
if (flags.initialResults) { if (this.listFocus != null) {
this.listFocus.blur();
} }
if (flags.initialResults) { this.empty(); }
this.append(this.tmpl('sidebarResult', entries));
if (flags.initialResults) {
if (this.listFocus != null) {
this.listFocus.blur();
}
}
if (flags.initialResults) {
this.empty();
}
this.append(this.tmpl("sidebarResult", entries));
if (flags.initialResults) {
if (flags.urlSearch) { this.openFirst(); } else { this.focusFirst(); }
if (flags.urlSearch) {
this.openFirst();
} else {
this.focusFirst();
}
}
}
onNoResults() {
this.html(this.tmpl('sidebarNoResults'));
this.html(this.tmpl("sidebarNoResults"));
}
onClear() {
@ -58,9 +73,11 @@ const Cls = (app.views.Results = class Results extends app.View {
}
focusFirst() {
if (!app.isMobile()) { if (this.listFocus != null) {
this.listFocus.focusOnNextFrame(this.el.firstElementChild);
} }
if (!app.isMobile()) {
if (this.listFocus != null) {
this.listFocus.focusOnNextFrame(this.el.firstElementChild);
}
}
}
openFirst() {
@ -75,7 +92,7 @@ const Cls = (app.views.Results = class Results extends app.View {
}
afterRoute(route, context) {
if (route === 'entry') {
if (route === "entry") {
this.listSelect.selectByHref(context.entry.fullPath());
} else {
this.listSelect.deselect();
@ -84,11 +101,15 @@ const Cls = (app.views.Results = class Results extends app.View {
onClick(event) {
let slug;
if (event.which !== 1) { return; }
if (slug = $.eventTarget(event).getAttribute('data-enable')) {
if (event.which !== 1) {
return;
}
if ((slug = $.eventTarget(event).getAttribute("data-enable"))) {
$.stopEvent(event);
const doc = app.disabledDocs.findBy('slug', slug);
if (doc) { return app.enableDoc(doc, this.onDocEnabled.bind(this, doc), $.noop); }
const doc = app.disabledDocs.findBy("slug", slug);
if (doc) {
return app.enableDoc(doc, this.onDocEnabled.bind(this, doc), $.noop);
}
}
}
});

@ -28,68 +28,74 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
}
static initClass() {
this.el = '._sidebar';
this.el = "._sidebar";
this.events = {
focus: 'onFocus',
select: 'onSelect',
click: 'onClick'
focus: "onFocus",
select: "onSelect",
click: "onClick",
};
this.routes =
{after: 'afterRoute'};
this.routes = { after: "afterRoute" };
this.shortcuts = {
altR: 'onAltR',
escape: 'onEscape'
altR: "onAltR",
escape: "onEscape",
};
}
init() {
if (!app.isMobile()) { this.addSubview(this.hover = new app.views.SidebarHover(this.el)); }
this.addSubview(this.search = new app.views.Search);
if (!app.isMobile()) {
this.addSubview((this.hover = new app.views.SidebarHover(this.el)));
}
this.addSubview((this.search = new app.views.Search()));
this.search
.on('searching', this.onSearching)
.on('clear', this.onSearchClear)
.scope
.on('change', this.onScopeChange);
.on("searching", this.onSearching)
.on("clear", this.onSearchClear)
.scope.on("change", this.onScopeChange);
this.results = new app.views.Results(this, this.search);
this.docList = new app.views.DocList;
this.docList = new app.views.DocList();
app.on('ready', this.onReady);
app.on("ready", this.onReady);
$.on(document.documentElement, 'mouseleave', () => this.hide());
$.on(document.documentElement, 'mouseenter', () => this.resetDisplay({forceNoHover: false}));
$.on(document.documentElement, "mouseleave", () => this.hide());
$.on(document.documentElement, "mouseenter", () =>
this.resetDisplay({ forceNoHover: false }),
);
}
hide() {
this.removeClass('show');
this.removeClass("show");
}
display() {
this.addClass('show');
this.addClass("show");
}
resetDisplay(options) {
if (options == null) { options = {}; }
if (!this.hasClass('show')) { return; }
this.removeClass('show');
if (options == null) {
options = {};
}
if (!this.hasClass("show")) {
return;
}
this.removeClass("show");
if ((options.forceNoHover !== false) && !this.hasClass('no-hover')) {
this.addClass('no-hover');
$.on(window, 'mousemove', this.resetHoverOnMouseMove);
if (options.forceNoHover !== false && !this.hasClass("no-hover")) {
this.addClass("no-hover");
$.on(window, "mousemove", this.resetHoverOnMouseMove);
}
}
resetHoverOnMouseMove() {
$.off(window, 'mousemove', this.resetHoverOnMouseMove);
$.off(window, "mousemove", this.resetHoverOnMouseMove);
return $.requestAnimationFrame(this.resetHover);
}
resetHover() {
return this.removeClass('no-hover');
return this.removeClass("no-hover");
}
showView(view) {
@ -135,8 +141,14 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
}
onScopeChange(newDoc, previousDoc) {
if (previousDoc) { this.docList.closeDoc(previousDoc); }
if (newDoc) { this.docList.reveal(newDoc.toEntry()); } else { this.scrollToTop(); }
if (previousDoc) {
this.docList.closeDoc(previousDoc);
}
if (newDoc) {
this.docList.reveal(newDoc.toEntry());
} else {
this.scrollToTop();
}
}
saveScrollPosition() {
@ -146,7 +158,7 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
}
restoreScrollPosition() {
if ((this.view === this.docList) && this.scrollTop) {
if (this.view === this.docList && this.scrollTop) {
this.el.scrollTop = this.scrollTop;
this.scrollTop = null;
} else {
@ -169,7 +181,9 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
onFocus(event) {
this.display();
if (event.target !== this.el) { $.scrollTo(event.target, this.el, 'continuous', {bottomGap: 2}); }
if (event.target !== this.el) {
$.scrollTo(event.target, this.el, "continuous", { bottomGap: 2 });
}
}
onSelect() {
@ -177,8 +191,14 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
}
onClick(event) {
if (event.which !== 1) { return; }
if (__guardMethod__($.eventTarget(event), 'hasAttribute', o => o.hasAttribute('data-reset-list'))) {
if (event.which !== 1) {
return;
}
if (
__guardMethod__($.eventTarget(event), "hasAttribute", (o) =>
o.hasAttribute("data-reset-list"),
)
) {
$.stopEvent(event);
this.onAltR();
}
@ -186,7 +206,7 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
onAltR() {
this.reset();
this.docList.reset({revealCurrent: true});
this.docList.reset({ revealCurrent: true });
this.display();
}
@ -194,7 +214,11 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
let doc;
this.reset();
this.resetDisplay();
if ((doc = this.search.getScopeDoc())) { this.docList.reveal(doc.toEntry()); } else { this.scrollToTop(); }
if ((doc = this.search.getScopeDoc())) {
this.docList.reveal(doc.toEntry());
} else {
this.scrollToTop();
}
}
onDocEnabled() {
@ -203,17 +227,29 @@ const Cls = (app.views.Sidebar = class Sidebar extends app.View {
}
afterRoute(name, context) {
if ((app.shortcuts.eventInProgress != null ? app.shortcuts.eventInProgress.name : undefined) === 'escape') { return; }
if (!context.init && app.router.isIndex()) { this.reset(); }
if (
(app.shortcuts.eventInProgress != null
? app.shortcuts.eventInProgress.name
: undefined) === "escape"
) {
return;
}
if (!context.init && app.router.isIndex()) {
this.reset();
}
this.resetDisplay();
}
});
Cls.initClass();
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;
}
}
}

@ -11,19 +11,18 @@
*/
const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
static initClass() {
this.itemClass = '_list-hover';
this.itemClass = "_list-hover";
this.events = {
focus: 'onFocus',
blur: 'onBlur',
mouseover: 'onMouseover',
mouseout: 'onMouseout',
scroll: 'onScroll',
click: 'onClick'
focus: "onFocus",
blur: "onBlur",
mouseover: "onMouseover",
mouseout: "onMouseout",
scroll: "onScroll",
click: "onClick",
};
this.routes =
{after: 'onRoute'};
this.routes = { after: "onRoute" };
}
constructor(el) {
@ -49,7 +48,9 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
this.cursor = el;
this.clone = this.makeClone(this.cursor);
$.append(document.body, this.clone);
if (this.offsetTop == null) { this.offsetTop = this.el.offsetTop; }
if (this.offsetTop == null) {
this.offsetTop = this.el.offsetTop;
}
this.position();
}
}
@ -58,7 +59,7 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
hide() {
if (this.cursor) {
$.remove(this.clone);
this.cursor = (this.clone = null);
this.cursor = this.clone = null;
}
}
@ -66,8 +67,8 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
if (this.cursor) {
const rect = $.rect(this.cursor);
if (rect.top >= this.offsetTop) {
this.clone.style.top = rect.top + 'px';
this.clone.style.left = rect.left + 'px';
this.clone.style.top = rect.top + "px";
this.clone.style.left = rect.left + "px";
} else {
this.hide();
}
@ -76,16 +77,18 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
makeClone(el) {
const clone = el.cloneNode(true);
clone.classList.add('clone');
clone.classList.add("clone");
return clone;
}
isTarget(el) {
return __guard__(el != null ? el.classList : undefined, x => x.contains(this.constructor.itemClass));
return __guard__(el != null ? el.classList : undefined, (x) =>
x.contains(this.constructor.itemClass),
);
}
isSelected(el) {
return el.classList.contains('active');
return el.classList.contains("active");
}
isTruncated(el) {
@ -102,7 +105,11 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
}
onMouseover(event) {
if (this.isTarget(event.target) && !this.isSelected(event.target) && this.mouseActivated()) {
if (
this.isTarget(event.target) &&
!this.isSelected(event.target) &&
this.mouseActivated()
) {
this.show(event.target);
}
}
@ -115,7 +122,7 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
mouseActivated() {
// Skip mouse events caused by focus events scrolling the sidebar.
return !this.focusTime || ((Date.now() - this.focusTime) > 500);
return !this.focusTime || Date.now() - this.focusTime > 500;
}
onScroll() {
@ -134,12 +141,14 @@ const Cls = (app.views.SidebarHover = class SidebarHover extends app.View {
});
Cls.initClass();
var isPointerEventsSupported = function() {
const el = document.createElement('div');
el.style.cssText = 'pointer-events: auto';
return el.style.pointerEvents === 'auto';
var isPointerEventsSupported = function () {
const el = document.createElement("div");
el.style.cssText = "pointer-events: auto";
return el.style.pointerEvents === "auto";
};
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -11,16 +11,21 @@
*/
const Cls = (app.views.TypeList = class TypeList extends app.View {
static initClass() {
this.tagName = 'div';
this.className = '_list _list-sub';
this.tagName = "div";
this.className = "_list _list-sub";
this.events = {
open: 'onOpen',
close: 'onClose'
open: "onOpen",
close: "onClose",
};
}
constructor(doc) { this.onOpen = this.onOpen.bind(this); this.onClose = this.onClose.bind(this); this.doc = doc; super(...arguments); }
constructor(doc) {
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.doc = doc;
super(...arguments);
}
init() {
this.lists = {};
@ -30,25 +35,36 @@ const Cls = (app.views.TypeList = class TypeList extends app.View {
activate() {
if (super.activate(...arguments)) {
for (var slug in this.lists) { var list = this.lists[slug]; list.activate(); }
for (var slug in this.lists) {
var list = this.lists[slug];
list.activate();
}
}
}
deactivate() {
if (super.deactivate(...arguments)) {
for (var slug in this.lists) { var list = this.lists[slug]; list.deactivate(); }
for (var slug in this.lists) {
var list = this.lists[slug];
list.deactivate();
}
}
}
render() {
let html = '';
for (var group of Array.from(this.doc.types.groups())) { html += this.tmpl('sidebarType', group); }
let html = "";
for (var group of Array.from(this.doc.types.groups())) {
html += this.tmpl("sidebarType", group);
}
return this.html(html);
}
onOpen(event) {
$.stopEvent(event);
const type = this.doc.types.findBy('slug', event.target.getAttribute('data-slug'));
const type = this.doc.types.findBy(
"slug",
event.target.getAttribute("data-slug"),
);
if (type && !this.lists[type.slug]) {
this.lists[type.slug] = new app.views.EntryList(type.entries());
@ -58,7 +74,10 @@ const Cls = (app.views.TypeList = class TypeList extends app.View {
onClose(event) {
$.stopEvent(event);
const type = this.doc.types.findBy('slug', event.target.getAttribute('data-slug'));
const type = this.doc.types.findBy(
"slug",
event.target.getAttribute("data-slug"),
);
if (type && this.lists[type.slug]) {
this.lists[type.slug].detach();
@ -68,12 +87,14 @@ const Cls = (app.views.TypeList = class TypeList extends app.View {
paginateTo(model) {
if (model.type) {
__guard__(this.lists[model.getType().slug], x => x.paginateTo(model));
__guard__(this.lists[model.getType().slug], (x) => x.paginateTo(model));
}
}
});
Cls.initClass();
function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}
return typeof value !== "undefined" && value !== null
? transform(value)
: undefined;
}

@ -15,22 +15,28 @@ const Cls = (app.View = class View {
constructor() {
this.setupElement();
if (this.el.className) { this.originalClassName = this.el.className; }
if (this.constructor.className) { this.resetClass(); }
if (this.el.className) {
this.originalClassName = this.el.className;
}
if (this.constructor.className) {
this.resetClass();
}
this.refreshElements();
if (typeof this.init === 'function') {
if (typeof this.init === "function") {
this.init();
}
this.refreshElements();
}
setupElement() {
if (this.el == null) { this.el = typeof this.constructor.el === 'string' ?
$(this.constructor.el)
: this.constructor.el ?
this.constructor.el
:
document.createElement(this.constructor.tagName || 'div'); }
if (this.el == null) {
this.el =
typeof this.constructor.el === "string"
? $(this.constructor.el)
: this.constructor.el
? this.constructor.el
: document.createElement(this.constructor.tagName || "div");
}
if (this.constructor.attributes) {
for (var key in this.constructor.attributes) {
@ -42,7 +48,10 @@ const Cls = (app.View = class View {
refreshElements() {
if (this.constructor.elements) {
for (var name in this.constructor.elements) { var selector = this.constructor.elements[name]; this[name] = this.find(selector); }
for (var name in this.constructor.elements) {
var selector = this.constructor.elements[name];
this[name] = this.find(selector);
}
}
}
@ -63,9 +72,11 @@ const Cls = (app.View = class View {
}
resetClass() {
this.el.className = this.originalClassName || '';
this.el.className = this.originalClassName || "";
if (this.constructor.className) {
for (var name of Array.from(this.constructor.className.split(' '))) { this.addClass(name); }
for (var name of Array.from(this.constructor.className.split(" "))) {
this.addClass(name);
}
}
}
@ -146,7 +157,7 @@ const Cls = (app.View = class View {
}
delay(fn, ...args) {
const delay = typeof args[args.length - 1] === 'number' ? args.pop() : 0;
const delay = typeof args[args.length - 1] === "number" ? args.pop() : 0;
return setTimeout(fn.bind(this, ...Array.from(args)), delay);
}
@ -161,30 +172,48 @@ const Cls = (app.View = class View {
bindEvents() {
let method, name;
if (this.constructor.events) {
for (name in this.constructor.events) { method = this.constructor.events[name]; this.onDOM(name, this[method]); }
for (name in this.constructor.events) {
method = this.constructor.events[name];
this.onDOM(name, this[method]);
}
}
if (this.constructor.routes) {
for (name in this.constructor.routes) { method = this.constructor.routes[name]; app.router.on(name, this[method]); }
for (name in this.constructor.routes) {
method = this.constructor.routes[name];
app.router.on(name, this[method]);
}
}
if (this.constructor.shortcuts) {
for (name in this.constructor.shortcuts) { method = this.constructor.shortcuts[name]; app.shortcuts.on(name, this[method]); }
for (name in this.constructor.shortcuts) {
method = this.constructor.shortcuts[name];
app.shortcuts.on(name, this[method]);
}
}
}
unbindEvents() {
let method, name;
if (this.constructor.events) {
for (name in this.constructor.events) { method = this.constructor.events[name]; this.offDOM(name, this[method]); }
for (name in this.constructor.events) {
method = this.constructor.events[name];
this.offDOM(name, this[method]);
}
}
if (this.constructor.routes) {
for (name in this.constructor.routes) { method = this.constructor.routes[name]; app.router.off(name, this[method]); }
for (name in this.constructor.routes) {
method = this.constructor.routes[name];
app.router.off(name, this[method]);
}
}
if (this.constructor.shortcuts) {
for (name in this.constructor.shortcuts) { method = this.constructor.shortcuts[name]; app.shortcuts.off(name, this[method]); }
for (name in this.constructor.shortcuts) {
method = this.constructor.shortcuts[name];
app.shortcuts.off(name, this[method]);
}
}
}
@ -193,17 +222,29 @@ const Cls = (app.View = class View {
}
activate() {
if (this.activated) { return; }
if (this.activated) {
return;
}
this.bindEvents();
if (this.subviews) { for (var view of Array.from(this.subviews)) { view.activate(); } }
if (this.subviews) {
for (var view of Array.from(this.subviews)) {
view.activate();
}
}
this.activated = true;
return true;
}
deactivate() {
if (!this.activated) { return; }
if (!this.activated) {
return;
}
this.unbindEvents();
if (this.subviews) { for (var view of Array.from(this.subviews)) { view.deactivate(); } }
if (this.subviews) {
for (var view of Array.from(this.subviews)) {
view.deactivate();
}
}
this.activated = false;
return true;
}

Loading…
Cancel
Save