You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devdocs/assets/javascripts/views/content/content.js

244 lines
5.3 KiB

app.views.Content = class Content extends app.View {
static el = "._content";
static loadingClass = "_content-loading";
static events = { click: "onClick" };
static shortcuts = {
altUp: "scrollStepUp",
altDown: "scrollStepDown",
pageUp: "scrollPageUp",
pageDown: "scrollPageDown",
pageTop: "scrollToTop",
pageBottom: "scrollToBottom",
altF: "onAltF",
};
static routes = {
before: "beforeRoute",
after: "afterRoute",
};
init() {
1 year ago
this.scrollEl = app.isMobile()
? document.scrollingElement || document.body
: this.el;
this.scrollMap = {};
this.scrollStack = [];
1 year ago
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());
}
show(view) {
this.hideLoading();
if (view !== this.view) {
if (this.view != null) {
this.view.deactivate();
}
1 year ago
this.html((this.view = view));
this.view.activate();
}
}
showLoading() {
this.addClass(this.constructor.loadingClass);
}
isLoading() {
return this.el.classList.contains(this.constructor.loadingClass);
}
hideLoading() {
this.removeClass(this.constructor.loadingClass);
}
scrollTo(value) {
this.scrollEl.scrollTop = value || 0;
}
smoothScrollTo(value) {
1 year ago
if (app.settings.get("fastScroll")) {
this.scrollTo(value);
} else {
$.smoothScroll(this.scrollEl, value || 0);
}
}
scrollBy(n) {
this.smoothScrollTo(this.scrollEl.scrollTop + n);
}
scrollToTop() {
this.smoothScrollTo(0);
}
scrollToBottom() {
this.smoothScrollTo(this.scrollEl.scrollHeight);
}
scrollStepUp() {
this.scrollBy(-80);
}
scrollStepDown() {
this.scrollBy(80);
}
scrollPageUp() {
this.scrollBy(40 - this.scrollEl.clientHeight);
}
scrollPageDown() {
this.scrollBy(this.scrollEl.clientHeight - 40);
}
scrollToTarget() {
let el;
1 year ago
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]);
}
}
onReady() {
this.hideLoading();
}
onBootError() {
this.hideLoading();
1 year ago
this.html(this.tmpl("bootError"));
}
onEntryLoading() {
this.showLoading();
if (this.scrollToTargetTimeout) {
clearTimeout(this.scrollToTargetTimeout);
this.scrollToTargetTimeout = null;
}
}
onEntryLoaded() {
this.hideLoading();
if (this.scrollToTargetTimeout) {
clearTimeout(this.scrollToTargetTimeout);
this.scrollToTargetTimeout = null;
}
this.scrollToTarget();
}
beforeRoute(context) {
this.cacheScrollPosition();
this.routeCtx = context;
this.scrollToTargetTimeout = this.delay(this.scrollToTarget);
}
cacheScrollPosition() {
1 year ago
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);
while (this.scrollStack.length > app.config.history_cache_size) {
delete this.scrollMap[this.scrollStack.shift()];
}
}
this.scrollMap[this.routeCtx.state.id] = this.scrollEl.scrollTop;
}
afterRoute(route, context) {
1 year ago
if (route !== "entry" && route !== "type") {
resetFavicon();
}
switch (route) {
1 year ago
case "root":
this.show(this.rootPage);
break;
1 year ago
case "entry":
this.show(this.entryPage);
break;
1 year ago
case "type":
this.show(this.typePage);
break;
1 year ago
case "settings":
this.show(this.settingsPage);
break;
1 year ago
case "offline":
this.show(this.offlinePage);
break;
default:
this.show(this.staticPage);
}
this.view.onRoute(context);
1 year ago
app.document.setTitle(
typeof this.view.getTitle === "function"
? this.view.getTitle()
: undefined,
);
}
onClick(event) {
const link = $.closestLink($.eventTarget(event), this.el);
1 year ago
if (link && this.isExternalUrl(link.getAttribute("href"))) {
$.stopEvent(event);
$.popup(link);
}
}
onAltF(event) {
1 year ago
if (
!document.activeElement ||
!$.hasChild(this.el, document.activeElement)
) {
this.find("a:not(:empty)")?.focus();
return $.stopEvent(event);
}
}
findTargetByHash(hash) {
1 year ago
let el = (() => {
try {
return $.id(decodeURIComponent(hash));
} catch (error) {}
})();
if (!el) {
el = (() => {
try {
return $.id(hash);
} catch (error1) {}
})();
}
return el;
}
isExternalUrl(url) {
return url?.startsWith("http:") || url?.startsWith("https:");
}
};