mirror of https://github.com/freeCodeCamp/devdocs
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.
259 lines
6.7 KiB
259 lines
6.7 KiB
/*
|
|
* decaffeinate suggestions:
|
|
* DS002: Fix invalid constructor
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining
|
|
* DS104: Avoid inline assignments
|
|
* DS204: Change includes calls to have a more natural evaluation order
|
|
* DS205: Consider reworking code to avoid use of IIFEs
|
|
* DS206: Consider reworking classes to avoid initClass
|
|
* DS207: Consider shorter variations of null checks
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
|
*/
|
|
const Cls = (app.views.Content = class Content extends app.View {
|
|
constructor(...args) {
|
|
this.scrollToTop = this.scrollToTop.bind(this);
|
|
this.scrollToBottom = this.scrollToBottom.bind(this);
|
|
this.scrollStepUp = this.scrollStepUp.bind(this);
|
|
this.scrollStepDown = this.scrollStepDown.bind(this);
|
|
this.scrollPageUp = this.scrollPageUp.bind(this);
|
|
this.scrollPageDown = this.scrollPageDown.bind(this);
|
|
this.onReady = this.onReady.bind(this);
|
|
this.onBootError = this.onBootError.bind(this);
|
|
this.onEntryLoading = this.onEntryLoading.bind(this);
|
|
this.onEntryLoaded = this.onEntryLoaded.bind(this);
|
|
this.beforeRoute = this.beforeRoute.bind(this);
|
|
this.afterRoute = this.afterRoute.bind(this);
|
|
this.onClick = this.onClick.bind(this);
|
|
this.onAltF = this.onAltF.bind(this);
|
|
super(...args);
|
|
}
|
|
|
|
static initClass() {
|
|
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'
|
|
};
|
|
|
|
this.routes = {
|
|
before: 'beforeRoute',
|
|
after: 'afterRoute'
|
|
};
|
|
}
|
|
|
|
init() {
|
|
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.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();
|
|
}
|
|
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) {
|
|
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;
|
|
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();
|
|
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() {
|
|
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) {
|
|
if ((route !== 'entry') && (route !== 'type')) {
|
|
resetFavicon();
|
|
}
|
|
|
|
switch (route) {
|
|
case 'root':
|
|
this.show(this.rootPage);
|
|
break;
|
|
case 'entry':
|
|
this.show(this.entryPage);
|
|
break;
|
|
case 'type':
|
|
this.show(this.typePage);
|
|
break;
|
|
case 'settings':
|
|
this.show(this.settingsPage);
|
|
break;
|
|
case 'offline':
|
|
this.show(this.offlinePage);
|
|
break;
|
|
default:
|
|
this.show(this.staticPage);
|
|
}
|
|
|
|
this.view.onRoute(context);
|
|
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'))) {
|
|
$.stopEvent(event);
|
|
$.popup(link);
|
|
}
|
|
}
|
|
|
|
onAltF(event) {
|
|
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) {} })(); }
|
|
return el;
|
|
}
|
|
|
|
isExternalUrl(url) {
|
|
let 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;
|
|
} |