mirror of
https://github.com/McShelby/hugo-theme-relearn.git
synced 2025-01-18 19:00:24 +00:00
0505b36141
implements the first part, working for configured variants; for that, loading of stylesheets was overhauled by now merging all configured variants into one big stylesheet for each output format and loading this instead of one stylesheet per configured variant; previously @media print styles were applied on top of @media screen styles; this is now strictly isolated, which doesn't require styles like neon to revert styles for print anymore; this change was also used to implement the minify parameter, to shrink those generated stylesheets, making them shrink to 66% of their unminified size for the exampleSite; the theme switcher functionality is now completely independend of variant.js; the switch is now handled by in-page javascript which avoids waiting for external scripts to load, so switching can happen as early as possible; switching of themes is now implemented by just setting a data attribute to the html element, were previously a complex mechanism of dynamically loading of variant CSS files was implemented; the call for variant.js was removed in this changeset, making the generator broken and useless; this will be fixed with the next patch;
1869 lines
64 KiB
JavaScript
1869 lines
64 KiB
JavaScript
window.relearn = window.relearn || {};
|
|
|
|
var theme = true;
|
|
var isPrint = document.querySelector('body').classList.contains('print');
|
|
var isPrintPreview = false;
|
|
|
|
var isRtl = document.querySelector('html').getAttribute('dir') == 'rtl';
|
|
var lang = document.querySelector('html').getAttribute('lang');
|
|
var dir_padding_start = 'padding-left';
|
|
var dir_padding_end = 'padding-right';
|
|
var dir_key_start = 37;
|
|
var dir_key_end = 39;
|
|
var dir_scroll = 1;
|
|
if (isRtl) {
|
|
dir_padding_start = 'padding-right';
|
|
dir_padding_end = 'padding-left';
|
|
dir_key_start = 39;
|
|
dir_key_end = 37;
|
|
dir_scroll = -1;
|
|
}
|
|
|
|
var touchsupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
|
|
var formelements = 'button, datalist, fieldset, input, label, legend, meter, optgroup, option, output, progress, select, textarea';
|
|
|
|
// PerfectScrollbar
|
|
var psc;
|
|
var psm;
|
|
var pst = new Map();
|
|
var elc = document.querySelector('#R-body-inner');
|
|
|
|
function regexEscape(s) {
|
|
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
}
|
|
|
|
function documentFocus() {
|
|
elc.focus();
|
|
psc && psc.scrollbarY.focus();
|
|
}
|
|
|
|
function scrollbarWidth() {
|
|
// https://davidwalsh.name/detect-scrollbar-width
|
|
// Create the measurement node
|
|
var scrollDiv = document.createElement('div');
|
|
scrollDiv.className = 'scrollbar-measure';
|
|
document.body.appendChild(scrollDiv);
|
|
// Get the scrollbar width
|
|
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
// Delete the DIV
|
|
document.body.removeChild(scrollDiv);
|
|
return scrollbarWidth;
|
|
}
|
|
|
|
var scrollbarSize = scrollbarWidth();
|
|
function adjustContentWidth() {
|
|
var start = parseFloat(getComputedStyle(elc).getPropertyValue(dir_padding_start));
|
|
var end = start;
|
|
if (elc.scrollHeight > elc.clientHeight) {
|
|
// if we have a scrollbar reduce the end margin by the scrollbar width
|
|
end = Math.max(0, start - scrollbarSize);
|
|
}
|
|
elc.style[dir_padding_end] = '' + end + 'px';
|
|
}
|
|
|
|
function fixCodeTabs() {
|
|
/* if only a single code block is contained in the tab and no style was selected, treat it like style=code */
|
|
var codeTabContents = Array.from(document.querySelectorAll('.tab-content.tab-panel-style')).filter(function (tabContent) {
|
|
return tabContent.querySelector('*:scope > .tab-content-text > div.highlight:only-child, *:scope > .tab-content-text > pre:not(.mermaid).pre-code:only-child');
|
|
});
|
|
|
|
codeTabContents.forEach(function (tabContent) {
|
|
var tabId = tabContent.dataset.tabItem;
|
|
var tabPanel = tabContent.parentNode.parentNode;
|
|
var tabButton = tabPanel.querySelector('.tab-nav-button.tab-panel-style[data-tab-item="' + tabId + '"]');
|
|
if (tabContent.classList.contains('initial')) {
|
|
tabButton.classList.remove('initial');
|
|
tabButton.classList.add('code');
|
|
tabContent.classList.remove('initial');
|
|
tabContent.classList.add('code');
|
|
}
|
|
// mark code blocks for FF without :has()
|
|
tabContent.classList.add('codify');
|
|
});
|
|
}
|
|
|
|
function switchTab(tabGroup, tabId) {
|
|
var tabs = Array.from(document.querySelectorAll('.tab-panel[data-tab-group="' + tabGroup + '"]')).filter(function (e) {
|
|
return !!e.querySelector('[data-tab-item="' + tabId + '"]');
|
|
});
|
|
var allTabItems =
|
|
tabs &&
|
|
tabs.reduce(function (a, e) {
|
|
return a.concat(
|
|
Array.from(e.querySelectorAll('[data-tab-item]')).filter(function (es) {
|
|
return es.parentNode.parentNode == e;
|
|
})
|
|
);
|
|
}, []);
|
|
var targetTabItems =
|
|
tabs &&
|
|
tabs.reduce(function (a, e) {
|
|
return a.concat(
|
|
Array.from(e.querySelectorAll('[data-tab-item="' + tabId + '"]')).filter(function (es) {
|
|
return es.parentNode.parentNode == e;
|
|
})
|
|
);
|
|
}, []);
|
|
|
|
// if event is undefined then switchTab was called from restoreTabSelection
|
|
// so it's not a button event and we don't need to safe the selction or
|
|
// prevent page jump
|
|
var isButtonEvent = event && event.target && event.target.getBoundingClientRect;
|
|
if (isButtonEvent) {
|
|
// save button position relative to viewport
|
|
var yposButton = event.target.getBoundingClientRect().top;
|
|
}
|
|
|
|
allTabItems &&
|
|
allTabItems.forEach(function (e) {
|
|
e.classList.remove('active');
|
|
e.removeAttribute('tabindex');
|
|
});
|
|
targetTabItems &&
|
|
targetTabItems.forEach(function (e) {
|
|
e.classList.add('active');
|
|
e.setAttribute('tabindex', '-1');
|
|
});
|
|
|
|
if (isButtonEvent) {
|
|
initMermaid(true);
|
|
|
|
// reset screen to the same position relative to clicked button to prevent page jump
|
|
var yposButtonDiff = event.target.getBoundingClientRect().top - yposButton;
|
|
window.scrollTo(window.scrollX, window.scrollY + yposButtonDiff);
|
|
|
|
// Store the selection to make it persistent
|
|
if (window.localStorage) {
|
|
var selectionsJSON = window.localStorage.getItem(window.relearn.absBaseUri + '/tab-selections');
|
|
if (selectionsJSON) {
|
|
var tabSelections = JSON.parse(selectionsJSON);
|
|
} else {
|
|
var tabSelections = {};
|
|
}
|
|
tabSelections[tabGroup] = tabId;
|
|
window.localStorage.setItem(window.relearn.absBaseUri + '/tab-selections', JSON.stringify(tabSelections));
|
|
}
|
|
}
|
|
}
|
|
|
|
function restoreTabSelections() {
|
|
if (window.localStorage) {
|
|
var selectionsJSON = window.localStorage.getItem(window.relearn.absBaseUri + '/tab-selections');
|
|
if (selectionsJSON) {
|
|
var tabSelections = JSON.parse(selectionsJSON);
|
|
} else {
|
|
var tabSelections = {};
|
|
}
|
|
Object.keys(tabSelections).forEach(function (tabGroup) {
|
|
var tabItem = tabSelections[tabGroup];
|
|
switchTab(tabGroup, tabItem);
|
|
});
|
|
}
|
|
}
|
|
|
|
function initMermaid(update, attrs) {
|
|
var doBeside = true;
|
|
var isImageRtl = false;
|
|
|
|
// we are either in update or initialization mode;
|
|
// during initialization, we want to edit the DOM;
|
|
// during update we only want to execute if something changed
|
|
var decodeHTML = function (html) {
|
|
var txt = document.createElement('textarea');
|
|
txt.innerHTML = html;
|
|
return txt.value;
|
|
};
|
|
var encodeHTML = function (text) {
|
|
var html = document.createElement('textarea');
|
|
html.textContent = text;
|
|
return html.innerHTML;
|
|
};
|
|
|
|
var parseGraph = function (graph) {
|
|
// See https://github.com/mermaid-js/mermaid/blob/9a080bb975b03b2b1d4ef6b7927d09e6b6b62760/packages/mermaid/src/diagram-api/frontmatter.ts#L10
|
|
// for reference on the regex originally taken from jekyll
|
|
var YAML = 1;
|
|
var INIT = 2;
|
|
var GRAPH = 3;
|
|
var d = /^(?:\s*[\n\r])*(?:-{3}(\s*[\n\r](?:.*?)[\n\r])-{3}(?:\s*[\n\r]+)+)?(?:\s*(?:%%\s*\{\s*\w+\s*:([^%]*?)%%\s*[\n\r]?))?(.*)$/s;
|
|
var m = d.exec(graph);
|
|
var yaml = {};
|
|
var dir = {};
|
|
var content = graph;
|
|
if (m && m.length == 4) {
|
|
yaml = m[YAML] ? jsyaml.load(m[YAML]) : yaml;
|
|
dir = m[INIT] ? JSON.parse('{ "init": ' + m[INIT]).init : dir;
|
|
content = m[GRAPH] ? m[GRAPH] : content;
|
|
}
|
|
var ret = { yaml: yaml, dir: dir, content: content.trim() };
|
|
return ret;
|
|
};
|
|
|
|
var serializeGraph = function (graph) {
|
|
var yamlPart = '';
|
|
if (Object.keys(graph.yaml).length) {
|
|
yamlPart = '---\n' + jsyaml.dump(graph.yaml) + '---\n';
|
|
}
|
|
var dirPart = '';
|
|
if (Object.keys(graph.dir).length) {
|
|
dirPart = '%%{init: ' + JSON.stringify(graph.dir) + '}%%\n';
|
|
}
|
|
return yamlPart + dirPart + graph.content;
|
|
};
|
|
|
|
var init_func = function (attrs) {
|
|
var is_initialized = false;
|
|
var theme = attrs.theme;
|
|
document.querySelectorAll('.mermaid').forEach(function (element) {
|
|
var parse = parseGraph(decodeHTML(element.innerHTML));
|
|
|
|
if (parse.yaml.theme) {
|
|
parse.yaml.relearn_user_theme = true;
|
|
}
|
|
if (parse.dir.theme) {
|
|
parse.dir.relearn_user_theme = true;
|
|
}
|
|
if (!parse.yaml.relearn_user_theme && !parse.dir.relearn_user_theme) {
|
|
parse.yaml.theme = theme;
|
|
}
|
|
is_initialized = true;
|
|
|
|
var graph = encodeHTML(serializeGraph(parse));
|
|
var new_element = document.createElement('div');
|
|
Array.from(element.attributes).forEach(function (attr) {
|
|
new_element.setAttribute(attr.name, attr.value);
|
|
element.removeAttribute(attr.name);
|
|
});
|
|
new_element.classList.add('mermaid-container');
|
|
new_element.classList.remove('mermaid');
|
|
element.classList.add('mermaid');
|
|
|
|
element.innerHTML = graph;
|
|
if (element.offsetParent !== null) {
|
|
element.classList.add('mermaid-render');
|
|
}
|
|
new_element.innerHTML = '<div class="mermaid-code">' + graph + '</div>' + element.outerHTML;
|
|
element.parentNode.replaceChild(new_element, element);
|
|
});
|
|
return is_initialized;
|
|
};
|
|
|
|
var update_func = function (attrs) {
|
|
var is_initialized = false;
|
|
var theme = attrs.theme;
|
|
document.querySelectorAll('.mermaid-container').forEach(function (e) {
|
|
var element = e.querySelector('.mermaid');
|
|
var code = e.querySelector('.mermaid-code');
|
|
var parse = parseGraph(decodeHTML(code.innerHTML));
|
|
|
|
if (element.classList.contains('mermaid-render')) {
|
|
if (parse.yaml.relearn_user_theme || parse.dir.relearn_user_theme) {
|
|
return;
|
|
}
|
|
if (parse.yaml.theme == theme || parse.dir.theme == theme) {
|
|
return;
|
|
}
|
|
}
|
|
if (element.offsetParent !== null) {
|
|
element.classList.add('mermaid-render');
|
|
} else {
|
|
element.classList.remove('mermaid-render');
|
|
return;
|
|
}
|
|
is_initialized = true;
|
|
|
|
parse.yaml.theme = theme;
|
|
var graph = encodeHTML(serializeGraph(parse));
|
|
element.removeAttribute('data-processed');
|
|
element.innerHTML = graph;
|
|
code.innerHTML = graph;
|
|
});
|
|
return is_initialized;
|
|
};
|
|
|
|
var state = this;
|
|
if (update && !state.is_initialized) {
|
|
return;
|
|
}
|
|
if (typeof mermaid == 'undefined' || typeof mermaid.mermaidAPI == 'undefined') {
|
|
return;
|
|
}
|
|
|
|
if (!state.is_initialized) {
|
|
state.is_initialized = true;
|
|
window.addEventListener(
|
|
'beforeprint',
|
|
function () {
|
|
isPrintPreview = true;
|
|
initMermaid(true, {
|
|
theme: getColorValue('PRINT-MERMAID-theme'),
|
|
});
|
|
}.bind(this)
|
|
);
|
|
window.addEventListener(
|
|
'afterprint',
|
|
function () {
|
|
isPrintPreview = false;
|
|
initMermaid(true);
|
|
}.bind(this)
|
|
);
|
|
}
|
|
|
|
attrs = attrs || {
|
|
theme: getColorValue('MERMAID-theme'),
|
|
};
|
|
|
|
var search;
|
|
if (update) {
|
|
search = sessionStorage.getItem(window.relearn.absBaseUri + '/search-value');
|
|
unmark();
|
|
}
|
|
var is_initialized = update ? update_func(attrs) : init_func(attrs);
|
|
if (is_initialized) {
|
|
mermaid.initialize(Object.assign({ securityLevel: 'antiscript', startOnLoad: false }, window.relearn.mermaidConfig, { theme: attrs.theme }));
|
|
mermaid.run({
|
|
postRenderCallback: function (id) {
|
|
// zoom for Mermaid
|
|
// https://github.com/mermaid-js/mermaid/issues/1860#issuecomment-1345440607
|
|
var svgs = d3.selectAll('body:not(.print) .mermaid-container.zoomable > .mermaid > #' + id);
|
|
svgs.each(function () {
|
|
var parent = this.parentElement;
|
|
// we need to copy the maxWidth, otherwise our reset button will not align in the upper right
|
|
parent.style.maxWidth = this.style.maxWidth || this.getAttribute('width');
|
|
// if no unit is given for the width
|
|
parent.style.maxWidth = parent.style.maxWidth || 'calc( ' + this.getAttribute('width') + 'px + 1rem )';
|
|
var svg = d3.select(this);
|
|
svg.html('<g>' + svg.html() + '</g>');
|
|
var inner = svg.select('*:scope > g');
|
|
parent.insertAdjacentHTML('beforeend', '<span class="svg-reset-button" title="' + window.T_Reset_view + '"><i class="fas fa-undo-alt"></i></span>');
|
|
var button = parent.querySelector('.svg-reset-button');
|
|
var zoom = d3.zoom().on('zoom', function (e) {
|
|
inner.attr('transform', e.transform);
|
|
if (e.transform.k == 1 && e.transform.x == 0 && e.transform.y == 0) {
|
|
button.classList.remove('zoomed');
|
|
} else {
|
|
button.classList.add('zoomed');
|
|
}
|
|
});
|
|
button.addEventListener('click', function (event) {
|
|
svg.transition().duration(350).call(zoom.transform, d3.zoomIdentity);
|
|
this.setAttribute('aria-label', window.T_View_reset);
|
|
this.classList.add('tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's' + (isImageRtl ? 'e' : 'w')));
|
|
});
|
|
button.addEventListener('mouseleave', function () {
|
|
if (this.classList.contains('tooltipped')) {
|
|
this.classList.remove('tooltipped', 'tooltipped-w', 'tooltipped-se', 'tooltipped-sw');
|
|
this.removeAttribute('aria-label');
|
|
}
|
|
});
|
|
svg.call(zoom);
|
|
});
|
|
},
|
|
querySelector: '.mermaid.mermaid-render',
|
|
suppressErrors: true,
|
|
});
|
|
}
|
|
if (update && search && search.length) {
|
|
sessionStorage.setItem(window.relearn.absBaseUri + '/search-value', search);
|
|
mark();
|
|
}
|
|
}
|
|
|
|
function initOpenapi(update, attrs) {
|
|
var state = this;
|
|
if (update && !state.is_initialized) {
|
|
return;
|
|
}
|
|
|
|
if (!state.is_initialized) {
|
|
state.is_initialized = true;
|
|
window.addEventListener(
|
|
'beforeprint',
|
|
function () {
|
|
isPrintPreview = true;
|
|
initOpenapi(true);
|
|
}.bind(this)
|
|
);
|
|
window.addEventListener(
|
|
'afterprint',
|
|
function () {
|
|
isPrintPreview = false;
|
|
initOpenapi(true);
|
|
}.bind(this)
|
|
);
|
|
}
|
|
|
|
attrs = attrs || {};
|
|
|
|
function addFunctionToResizeEvent() {}
|
|
function getFirstAncestorByClass() {}
|
|
function renderOpenAPI(oc) {
|
|
var relBasePath = window.relearn.relBasePath;
|
|
var assetBuster = window.themeUseOpenapi.assetsBuster;
|
|
var print = isPrint || isPrintPreview ? 'PRINT-' : '';
|
|
var format = print ? `print` : `html`;
|
|
var min = window.relearn.min;
|
|
var theme = `${relBasePath}/css/format-${format}${min}.css${assetBuster}`;
|
|
var variant = document.documentElement.dataset.rThemeVariant;
|
|
var swagger_theme = getColorValue(print + 'OPENAPI-theme');
|
|
var swagger_code_theme = getColorValue(print + 'OPENAPI-CODE-theme');
|
|
|
|
const openapiId = 'relearn-swagger-ui';
|
|
const openapiIframeId = openapiId + '-iframe';
|
|
const openapiIframe = document.getElementById(openapiIframeId);
|
|
if (openapiIframe) {
|
|
openapiIframe.remove();
|
|
}
|
|
const openapiErrorId = openapiId + '-error';
|
|
const openapiError = document.getElementById(openapiErrorId);
|
|
if (openapiError) {
|
|
openapiError.remove();
|
|
}
|
|
const oi = document.createElement('iframe');
|
|
oi.id = openapiIframeId;
|
|
oi.classList.toggle('sc-openapi-iframe', true);
|
|
oi.srcdoc = '<!doctype html>' + '<html lang="' + lang + '" dir="' + (isRtl ? 'rtl' : 'ltr') + '" data-r-output-format="' + format + '" data-r-theme-variant="' + variant + '">' + '<head>' + '<link rel="stylesheet" href="' + window.themeUseOpenapi.css + '">' + '<link rel="stylesheet" href="' + relBasePath + `/css/swagger${min}.css` + assetBuster + '">' + '<link rel="stylesheet" href="' + relBasePath + '/css/swagger-' + swagger_theme + '.css' + assetBuster + '">' + '<link rel="stylesheet" href="' + theme + '">' + '</head>' + '<body>' + '<a class="relearn-expander" href="" onclick="return relearn_collapse_all()">Collapse all</a>' + '<a class="relearn-expander" href="" onclick="return relearn_expand_all()">Expand all</a>' + '<div id="relearn-swagger-ui"></div>' + '<script>' + 'function relearn_expand_all(){' + 'document.querySelectorAll( ".opblock-summary-control[aria-expanded=false]" ).forEach( btn => btn.click() );' + 'document.querySelectorAll( ".model-container > .model-box > button[aria-expanded=false]" ).forEach( btn => btn.click() );' + 'return false;' + '}' + 'function relearn_collapse_all(){' + 'document.querySelectorAll( ".opblock-summary-control[aria-expanded=true]" ).forEach( btn => btn.click() );' + 'document.querySelectorAll( ".model-container > .model-box > .model-box > .model > span > button[aria-expanded=true]" ).forEach( btn => btn.click() );' + 'return false;' + '}' + '</script>' + '</body>' + '</html>';
|
|
oi.height = '100%';
|
|
oi.width = '100%';
|
|
oi.onload = function () {
|
|
const openapiWrapper = getFirstAncestorByClass(oc, 'sc-openapi-wrapper');
|
|
const openapiPromise = new Promise(function (resolve) {
|
|
resolve();
|
|
});
|
|
openapiPromise
|
|
.then(function () {
|
|
var options = {
|
|
defaultModelsExpandDepth: 2,
|
|
defaultModelExpandDepth: 2,
|
|
docExpansion: isPrint || isPrintPreview ? 'full' : 'list',
|
|
domNode: oi.contentWindow.document.getElementById(openapiId),
|
|
filter: !(isPrint || isPrintPreview),
|
|
layout: 'BaseLayout',
|
|
onComplete: function () {
|
|
if (isPrint || isPrintPreview) {
|
|
oi.contentWindow.document.querySelectorAll('.model-container > .model-box > button[aria-expanded=false]').forEach(function (btn) {
|
|
btn.click();
|
|
});
|
|
setOpenAPIHeight(oi);
|
|
}
|
|
},
|
|
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
|
|
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
|
syntaxHighlight: {
|
|
activated: true,
|
|
theme: swagger_code_theme,
|
|
},
|
|
validatorUrl: 'none',
|
|
};
|
|
if (oc.dataset.openapiSpec) {
|
|
try {
|
|
Object.assign(options, { spec: JSON.parse(oc.dataset.openapiSpec) });
|
|
} catch (err) {
|
|
try {
|
|
Object.assign(options, { spec: jsyaml.load(oc.dataset.openapiSpec) });
|
|
} catch (err) {
|
|
console.error('OpenAPI: file "' + oc.dataset.openapiUrl + '" could not be parsed as JSON or YAML');
|
|
}
|
|
}
|
|
} else {
|
|
Object.assign(options, { url: oc.dataset.openapiUrl });
|
|
}
|
|
SwaggerUIBundle(options);
|
|
})
|
|
.then(function () {
|
|
let observerCallback = function () {
|
|
setOpenAPIHeight(oi);
|
|
};
|
|
let observer = new MutationObserver(observerCallback);
|
|
observer.observe(oi.contentWindow.document.documentElement, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
})
|
|
.then(function () {
|
|
if (openapiWrapper) {
|
|
openapiWrapper.classList.toggle('is-loading', false);
|
|
}
|
|
setOpenAPIHeight(oi);
|
|
})
|
|
.catch(function (error) {
|
|
const ed = document.createElement('div');
|
|
ed.classList.add('sc-alert', 'sc-alert-error');
|
|
ed.innerHTML = error;
|
|
ed.id = openapiErrorId;
|
|
while (oc.lastChild) {
|
|
oc.removeChild(oc.lastChild);
|
|
}
|
|
if (openapiWrapper) {
|
|
openapiWrapper.classList.toggle('is-loading', false);
|
|
openapiWrapper.insertAdjacentElement('afterbegin', ed);
|
|
}
|
|
});
|
|
};
|
|
oc.appendChild(oi);
|
|
}
|
|
function setOpenAPIHeight(oi) {
|
|
// add empirical offset if in print preview (GC 103)
|
|
oi.style.height = oi.contentWindow.document.documentElement.getBoundingClientRect().height + (isPrintPreview ? 200 : 0) + 'px';
|
|
}
|
|
function resizeOpenAPI() {
|
|
let divi = document.getElementsByClassName('sc-openapi-iframe');
|
|
for (let i = 0; i < divi.length; i++) {
|
|
setOpenAPIHeight(divi[i]);
|
|
}
|
|
}
|
|
let divo = document.getElementsByClassName('sc-openapi-container');
|
|
for (let i = 0; i < divo.length; i++) {
|
|
renderOpenAPI(divo[i]);
|
|
}
|
|
if (divo.length) {
|
|
addFunctionToResizeEvent(resizeOpenAPI);
|
|
}
|
|
}
|
|
|
|
function initAnchorClipboard() {
|
|
if (window.relearn.disableAnchorCopy && window.relearn.disableAnchorScrolling) {
|
|
return;
|
|
}
|
|
|
|
document.querySelectorAll('h1~h2,h1~h3,h1~h4,h1~h5,h1~h6').forEach(function (element) {
|
|
var url = encodeURI((document.location.origin == 'null' ? document.location.protocol + '//' + document.location.host : document.location.origin) + document.location.pathname);
|
|
var link = url + '#' + element.id;
|
|
var new_element = document.createElement('span');
|
|
new_element.classList.add('anchor');
|
|
if (!window.relearn.disableAnchorCopy) {
|
|
new_element.setAttribute('title', window.T_Copy_link_to_clipboard);
|
|
}
|
|
new_element.setAttribute('data-clipboard-text', link);
|
|
new_element.innerHTML = '<i class="fas fa-link fa-lg"></i>';
|
|
element.appendChild(new_element);
|
|
});
|
|
|
|
var anchors = document.querySelectorAll('.anchor');
|
|
if (!window.relearn.disableAnchorCopy) {
|
|
for (var i = 0; i < anchors.length; i++) {
|
|
anchors[i].addEventListener('mouseleave', function (e) {
|
|
this.removeAttribute('aria-label');
|
|
this.classList.remove('tooltipped', 'tooltipped-se', 'tooltipped-sw');
|
|
});
|
|
}
|
|
|
|
var clip = new ClipboardJS('.anchor');
|
|
clip.on('success', function (e) {
|
|
e.clearSelection();
|
|
e.trigger.setAttribute('aria-label', window.T_Link_copied_to_clipboard);
|
|
e.trigger.classList.add('tooltipped', 'tooltipped-s' + (isRtl ? 'e' : 'w'));
|
|
if (!window.relearn.disableAnchorScrolling) {
|
|
e.trigger.parentElement.scrollIntoView({ behavior: 'smooth' });
|
|
var state = window.history.state || {};
|
|
state = Object.assign({}, typeof state === 'object' ? state : {});
|
|
history.replaceState({}, '', e.text);
|
|
}
|
|
});
|
|
} else if (!window.relearn.disableAnchorScrolling) {
|
|
for (var i = 0; i < anchors.length; i++) {
|
|
anchors[i].addEventListener('click', function (e) {
|
|
e.target.parentElement.parentElement.scrollIntoView({ behavior: 'smooth' });
|
|
var state = window.history.state || {};
|
|
state = Object.assign({}, typeof state === 'object' ? state : {});
|
|
history.replaceState({}, '', e.text);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function initCodeClipboard() {
|
|
function getCodeText(node) {
|
|
// if highlight shortcode is used in inline lineno mode, remove lineno nodes before generating text, otherwise it doesn't hurt
|
|
var code = node.cloneNode(true);
|
|
Array.from(code.querySelectorAll('*:scope > span > span:first-child:not(:last-child)')).forEach(function (lineno) {
|
|
lineno.remove();
|
|
});
|
|
var text = code.textContent;
|
|
// remove a trailing line break, this may most likely
|
|
// come from the browser / Hugo transformation
|
|
text = text.replace(/\n$/, '');
|
|
return text;
|
|
}
|
|
|
|
function fallbackMessage(action) {
|
|
var actionMsg = '';
|
|
var actionKey = action === 'cut' ? 'X' : 'C';
|
|
if (/iPhone|iPad/i.test(navigator.userAgent)) {
|
|
actionMsg = 'No support :(';
|
|
} else if (/Mac/i.test(navigator.userAgent)) {
|
|
actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
|
|
} else {
|
|
actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
|
|
}
|
|
return actionMsg;
|
|
}
|
|
|
|
document.addEventListener('copy', function (ev) {
|
|
// shabby FF generates empty lines on cursor selection that we need to filter out; see #925
|
|
var selection = document.getSelection();
|
|
var node = selection.anchorNode;
|
|
|
|
// in case of GC, it works without this handler;
|
|
// instead GC fails if this handler is active, because it still contains
|
|
// the line number nodes with class 'ln' in the selection, although
|
|
// they are flagged with 'user-select: none;' see https://issues.chromium.org/issues/41393366;
|
|
// so in case of GC we don't want to do anything and bail out early in below code
|
|
function selectionContainsLnClass(selection) {
|
|
for (var i = 0; i < selection.rangeCount; i++) {
|
|
var range = selection.getRangeAt(i);
|
|
var fragment = range.cloneContents();
|
|
if (fragment.querySelector('.ln')) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (!selectionContainsLnClass(selection)) {
|
|
while (node) {
|
|
// selection could start in a text node, so account for this as it
|
|
// obviously does not support `classList`
|
|
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('highlight')) {
|
|
// only do this if we are inside of a code highlight node;
|
|
// now fix FFs selection by calculating the text ourself
|
|
var text = selection.toString();
|
|
ev.clipboardData.setData('text/plain', text);
|
|
ev.preventDefault();
|
|
break;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
});
|
|
|
|
var codeElements = document.querySelectorAll('code');
|
|
for (var i = 0; i < codeElements.length; i++) {
|
|
var code = codeElements[i];
|
|
var text = getCodeText(code);
|
|
var inPre = code.parentNode.tagName.toLowerCase() == 'pre';
|
|
var inTable = inPre && code.parentNode.parentNode.tagName.toLowerCase() == 'td' && code.parentNode.parentNode.classList.contains('lntd');
|
|
// avoid copy-to-clipboard for highlight shortcode in table lineno mode
|
|
var isFirstLineCell = inTable && code.parentNode.parentNode.parentNode.querySelector('td:first-child > pre > code') == code;
|
|
|
|
if (!isFirstLineCell && (inPre || text.length > 5)) {
|
|
code.classList.add('copy-to-clipboard-code');
|
|
if (inPre) {
|
|
code.classList.add('copy-to-clipboard');
|
|
code.parentNode.classList.add('pre-code');
|
|
} else {
|
|
var clone = code.cloneNode(true);
|
|
var span = document.createElement('span');
|
|
span.classList.add('copy-to-clipboard');
|
|
span.appendChild(clone);
|
|
code.parentNode.replaceChild(span, code);
|
|
code = clone;
|
|
}
|
|
var button = document.createElement('span');
|
|
button.classList.add('copy-to-clipboard-button');
|
|
button.setAttribute('title', window.T_Copy_to_clipboard);
|
|
button.innerHTML = '<i class="far fa-copy"></i>';
|
|
button.addEventListener('mouseleave', function () {
|
|
this.removeAttribute('aria-label');
|
|
this.classList.remove('tooltipped', 'tooltipped-w', 'tooltipped-se', 'tooltipped-sw');
|
|
});
|
|
if (inTable) {
|
|
var table = code.parentNode.parentNode.parentNode.parentNode.parentNode;
|
|
table.dataset.code = text;
|
|
table.parentNode.insertBefore(button, table.nextSibling);
|
|
} else if (inPre) {
|
|
var pre = code.parentNode;
|
|
pre.dataset.code = text;
|
|
var p = pre.parentNode;
|
|
// indented code blocks are missing the div
|
|
while (p != document && (p.tagName.toLowerCase() != 'div' || !p.classList.contains('highlight'))) {
|
|
p = p.parentNode;
|
|
}
|
|
if (p == document) {
|
|
var clone = pre.cloneNode(true);
|
|
var div = document.createElement('div');
|
|
div.classList.add('highlight');
|
|
div.appendChild(clone);
|
|
pre.parentNode.replaceChild(div, pre);
|
|
pre = clone;
|
|
}
|
|
pre.parentNode.insertBefore(button, pre.nextSibling);
|
|
} else {
|
|
code.dataset.code = text;
|
|
code.parentNode.insertBefore(button, code.nextSibling);
|
|
}
|
|
}
|
|
}
|
|
|
|
var clip = new ClipboardJS('.copy-to-clipboard-button', {
|
|
text: function (trigger) {
|
|
if (!trigger.previousElementSibling) {
|
|
return '';
|
|
}
|
|
return trigger.previousElementSibling.dataset.code || '';
|
|
},
|
|
});
|
|
|
|
clip.on('success', function (e) {
|
|
e.clearSelection();
|
|
var inPre = e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'pre';
|
|
var isCodeRtl = !inPre ? isRtl : false;
|
|
var doBeside = inPre || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table');
|
|
e.trigger.setAttribute('aria-label', window.T_Copied_to_clipboard);
|
|
e.trigger.classList.add('tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's' + (isCodeRtl ? 'e' : 'w')));
|
|
});
|
|
|
|
clip.on('error', function (e) {
|
|
var inPre = e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'pre';
|
|
var isCodeRtl = !inPre ? isRtl : false;
|
|
var doBeside = inPre || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table');
|
|
e.trigger.setAttribute('aria-label', fallbackMessage(e.action));
|
|
e.trigger.classList.add('tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's' + (isCodeRtl ? 'e' : 'w')));
|
|
var f = function () {
|
|
e.trigger.setAttribute('aria-label', window.T_Copied_to_clipboard);
|
|
e.trigger.classList.add('tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's' + (isCodeRtl ? 'e' : 'w')));
|
|
document.removeEventListener('copy', f);
|
|
};
|
|
document.addEventListener('copy', f);
|
|
});
|
|
}
|
|
|
|
function initArrowVerticalNav() {
|
|
var topMain = 0;
|
|
if (!isPrint) {
|
|
topMain = document.querySelector('main').getClientRects()[0].top;
|
|
}
|
|
|
|
document.addEventListener('keydown', function (event) {
|
|
var elems = Array.from(
|
|
document.querySelectorAll(`main :not(.include.hide-first-heading) > :where(
|
|
.article-subheading,
|
|
:not(.article-subheading) + h1:not(.a11y-only),
|
|
h1:not(.a11y-only):first-child,
|
|
h2, h3, h4, h5, h6
|
|
),
|
|
main .include.hide-first-heading > :where( h1, h2, h3, h4, h5, h6 ) ~ :where( h1, h2, h3, h4, h5, h6 )
|
|
`)
|
|
);
|
|
if (!event.shiftKey && !event.ctrlKey && event.altKey && !event.metaKey) {
|
|
if (event.which == 38) {
|
|
// up
|
|
var target = isPrint ? document.querySelector('#R-body') : document.querySelector('.flex-block-wrapper');
|
|
elems.some(function (elem, i) {
|
|
var top = elem.getBoundingClientRect().top;
|
|
var topBoundary = top - topMain;
|
|
if (topBoundary > -1) {
|
|
target.scrollIntoView();
|
|
return true;
|
|
}
|
|
target = elem;
|
|
});
|
|
} else if (event.which == 40) {
|
|
// down
|
|
elems.some(function (elem, i) {
|
|
var top = elem.getBoundingClientRect().top;
|
|
var topBoundary = top - topMain;
|
|
if (topBoundary > -1 && topBoundary < 1) {
|
|
if (i + 1 < elems.length) {
|
|
var target = elems[i + 1];
|
|
target.scrollIntoView();
|
|
}
|
|
return true;
|
|
}
|
|
if (topBoundary >= 1) {
|
|
var target = elem;
|
|
target.scrollIntoView();
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function initArrowHorizontalNav() {
|
|
if (isPrint) {
|
|
return;
|
|
}
|
|
|
|
// button navigation
|
|
var prev = document.querySelector('.topbar-button-prev a');
|
|
prev && prev.addEventListener('click', navPrev);
|
|
var next = document.querySelector('.topbar-button-next a');
|
|
next && next.addEventListener('click', navNext);
|
|
|
|
// keyboard navigation
|
|
// avoid prev/next navigation if we are not at the start/end of the
|
|
// horizontal area
|
|
var el = document.querySelector('#R-body-inner');
|
|
var scrollStart = 0;
|
|
var scrollEnd = 0;
|
|
document.addEventListener('keydown', function (event) {
|
|
if (!event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
var f = event.target.matches(formelements);
|
|
if (f) {
|
|
return;
|
|
}
|
|
if (event.which == dir_key_start) {
|
|
if (!scrollStart && +el.scrollLeft.toFixed() * dir_scroll <= 0) {
|
|
prev && prev.click();
|
|
} else if (scrollStart != -1) {
|
|
clearTimeout(scrollStart);
|
|
}
|
|
scrollStart = -1;
|
|
}
|
|
if (event.which == dir_key_end) {
|
|
if (!scrollEnd && +el.scrollLeft.toFixed() * dir_scroll + +el.clientWidth.toFixed() >= +el.scrollWidth.toFixed()) {
|
|
next && next.click();
|
|
} else if (scrollEnd != -1) {
|
|
clearTimeout(scrollEnd);
|
|
}
|
|
scrollEnd = -1;
|
|
}
|
|
}
|
|
});
|
|
document.addEventListener('keyup', function (event) {
|
|
if (!event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
var f = event.target.matches(formelements);
|
|
if (f) {
|
|
return;
|
|
}
|
|
if (event.which == dir_key_start) {
|
|
// check for false indication if keyup is delayed after navigation
|
|
if (scrollStart == -1) {
|
|
scrollStart = setTimeout(function () {
|
|
scrollStart = 0;
|
|
}, 300);
|
|
}
|
|
}
|
|
if (event.which == dir_key_end) {
|
|
if (scrollEnd == -1) {
|
|
scrollEnd = setTimeout(function () {
|
|
scrollEnd = 0;
|
|
}, 300);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function initMenuScrollbar() {
|
|
if (isPrint) {
|
|
return;
|
|
}
|
|
|
|
var elm = document.querySelector('#R-content-wrapper');
|
|
var elt = document.querySelector('.topbar-button.topbar-flyout .topbar-content-wrapper');
|
|
|
|
var autofocus = true;
|
|
document.addEventListener('keydown', function (event) {
|
|
// for initial keyboard scrolling support, no element
|
|
// may be hovered, but we still want to react on
|
|
// cursor/page up/down. because we can't hack
|
|
// the scrollbars implementation, we try to trick
|
|
// it and give focus to the scrollbar - only
|
|
// to just remove the focus right after scrolling
|
|
// happend
|
|
autofocus = false;
|
|
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || event.which < 32 || event.which > 40) {
|
|
// if tab key was pressed, we are ended with our initial
|
|
// focus job
|
|
return;
|
|
}
|
|
|
|
var c = elc && elc.matches(':hover');
|
|
var m = elm && elm.matches(':hover');
|
|
var t = elt && elt.matches(':hover');
|
|
var f = event.target.matches(formelements);
|
|
if (!c && !m && !t && !f) {
|
|
// only do this hack if none of our scrollbars
|
|
// is hovered
|
|
// if we are showing the sidebar as a flyout we
|
|
// want to scroll the content-wrapper, otherwise we want
|
|
// to scroll the body
|
|
var nt = document.querySelector('body').matches('.topbar-flyout');
|
|
var nm = document.querySelector('body').matches('.sidebar-flyout');
|
|
if (nt) {
|
|
var psb = pst.get(document.querySelector('.topbar-button.topbar-flyout'));
|
|
psb && psb.scrollbarY.focus();
|
|
} else if (nm) {
|
|
psm && psm.scrollbarY.focus();
|
|
} else {
|
|
document.querySelector('#R-body-inner').focus();
|
|
psc && psc.scrollbarY.focus();
|
|
}
|
|
}
|
|
});
|
|
// scrollbars will install their own keyboard handlers
|
|
// that need to be executed inbetween our own handlers
|
|
// PSC removed for #242 #243 #244
|
|
// psc = elc && new PerfectScrollbar('#R-body-inner');
|
|
psm = elm && new PerfectScrollbar('#R-content-wrapper', { scrollingThreshold: 2000, swipeEasing: false, wheelPropagation: false });
|
|
document.querySelectorAll('.topbar-button .topbar-content-wrapper').forEach(function (e) {
|
|
var button = getTopbarButtonParent(e);
|
|
if (!button) {
|
|
return;
|
|
}
|
|
pst.set(button, new PerfectScrollbar(e, { scrollingThreshold: 2000, swipeEasing: false, wheelPropagation: false }));
|
|
e.addEventListener('click', toggleTopbarFlyoutEvent);
|
|
});
|
|
|
|
document.addEventListener('keydown', function () {
|
|
// if we facked initial scrolling, we want to
|
|
// remove the focus to not leave visual markers on
|
|
// the scrollbar
|
|
if (autofocus) {
|
|
psc && psc.scrollbarY.blur();
|
|
psm && psm.scrollbarY.blur();
|
|
pst.forEach(function (psb) {
|
|
psb.scrollbarY.blur();
|
|
});
|
|
autofocus = false;
|
|
}
|
|
});
|
|
// on resize, we have to redraw the scrollbars to let new height
|
|
// affect their size
|
|
window.addEventListener('resize', function () {
|
|
pst.forEach(function (psb) {
|
|
setTimeout(function () {
|
|
psb.update();
|
|
}, 10);
|
|
});
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
psc &&
|
|
setTimeout(function () {
|
|
psc.update();
|
|
}, 10);
|
|
});
|
|
// now that we may have collapsible menus, we need to call a resize
|
|
// for the menu scrollbar if sections are expanded/collapsed
|
|
document.querySelectorAll('#R-sidebar .collapsible-menu input').forEach(function (e) {
|
|
e.addEventListener('change', function () {
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
});
|
|
});
|
|
// bugfix for PS in RTL mode: the initial scrollbar position is off;
|
|
// calling update() once, fixes this
|
|
pst.forEach(function (psb) {
|
|
setTimeout(function () {
|
|
psb.update();
|
|
}, 10);
|
|
});
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
psc &&
|
|
setTimeout(function () {
|
|
psc.update();
|
|
}, 10);
|
|
|
|
// finally, we want to adjust the contents end padding if there is a scrollbar visible
|
|
window.addEventListener('resize', adjustContentWidth);
|
|
adjustContentWidth();
|
|
}
|
|
|
|
function imageEscapeHandler(event) {
|
|
if (event.key == 'Escape') {
|
|
var image = event.target;
|
|
image.click();
|
|
}
|
|
}
|
|
|
|
function navShortcutHandler(event) {
|
|
if (!event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 78 /* n */) {
|
|
toggleNav();
|
|
}
|
|
}
|
|
|
|
function searchShortcutHandler(event) {
|
|
if (!event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 70 /* f */) {
|
|
showSearch();
|
|
}
|
|
}
|
|
|
|
function tocShortcutHandler(event) {
|
|
if (!event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 84 /* t */) {
|
|
toggleToc();
|
|
}
|
|
}
|
|
|
|
function editShortcutHandler(event) {
|
|
if (!event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 87 /* w */) {
|
|
showEdit();
|
|
}
|
|
}
|
|
|
|
function printShortcutHandler(event) {
|
|
if (!event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 80 /* p */) {
|
|
showPrint();
|
|
}
|
|
}
|
|
|
|
function showSearch() {
|
|
var s = document.querySelector('#R-search-by');
|
|
if (!s) {
|
|
return;
|
|
}
|
|
var b = document.querySelector('body');
|
|
if (s == document.activeElement) {
|
|
if (b.classList.contains('sidebar-flyout')) {
|
|
closeNav();
|
|
}
|
|
documentFocus();
|
|
} else {
|
|
if (!b.classList.contains('sidebar-flyout')) {
|
|
openNav();
|
|
}
|
|
s.focus();
|
|
}
|
|
}
|
|
|
|
function openNav() {
|
|
closeSomeTopbarButtonFlyout();
|
|
var b = document.querySelector('body');
|
|
b.classList.add('sidebar-flyout');
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
psm && psm.scrollbarY.focus();
|
|
var a = document.querySelector('#R-sidebar a');
|
|
if (a) {
|
|
a.focus();
|
|
}
|
|
}
|
|
|
|
function closeNav() {
|
|
var b = document.querySelector('body');
|
|
b.classList.remove('sidebar-flyout');
|
|
documentFocus();
|
|
}
|
|
|
|
function toggleNav() {
|
|
var b = document.querySelector('body');
|
|
if (b.classList.contains('sidebar-flyout')) {
|
|
closeNav();
|
|
} else {
|
|
openNav();
|
|
}
|
|
}
|
|
|
|
function navEscapeHandler(event) {
|
|
if (event.key == 'Escape') {
|
|
closeNav();
|
|
}
|
|
}
|
|
|
|
function getTopbarButtonParent(e) {
|
|
var button = e;
|
|
while (button && !button.classList.contains('topbar-button')) {
|
|
button = button.parentElement;
|
|
}
|
|
return button;
|
|
}
|
|
|
|
function openTopbarButtonFlyout(button) {
|
|
closeNav();
|
|
var body = document.querySelector('body');
|
|
button.classList.add('topbar-flyout');
|
|
body.classList.add('topbar-flyout');
|
|
var psb = pst.get(button);
|
|
psb &&
|
|
setTimeout(function () {
|
|
psb.update();
|
|
}, 10);
|
|
psb && psb.scrollbarY.focus();
|
|
var a = button.querySelector('.topbar-content-wrapper a');
|
|
if (a) {
|
|
a.focus();
|
|
}
|
|
}
|
|
|
|
function closeTopbarButtonFlyout(button) {
|
|
var body = document.querySelector('body');
|
|
button.classList.remove('topbar-flyout');
|
|
body.classList.remove('topbar-flyout');
|
|
documentFocus();
|
|
}
|
|
|
|
function closeSomeTopbarButtonFlyout() {
|
|
var someButton = document.querySelector('.topbar-button.topbar-flyout');
|
|
if (someButton) {
|
|
closeTopbarButtonFlyout(someButton);
|
|
}
|
|
return someButton;
|
|
}
|
|
|
|
function toggleTopbarButtonFlyout(button) {
|
|
var someButton = closeSomeTopbarButtonFlyout();
|
|
if (button && button != someButton) {
|
|
openTopbarButtonFlyout(button);
|
|
}
|
|
}
|
|
|
|
function toggleTopbarFlyout(e) {
|
|
var button = getTopbarButtonParent(e);
|
|
if (!button) {
|
|
return;
|
|
}
|
|
toggleTopbarButtonFlyout(button);
|
|
}
|
|
|
|
function toggleTopbarFlyoutEvent(event) {
|
|
if (event.target.classList.contains('topbar-content') || event.target.classList.contains('topbar-content-wrapper') || event.target.classList.contains('ps__rail-x') || event.target.classList.contains('ps__rail-y') || event.target.classList.contains('ps__thumb-x') || event.target.classList.contains('ps__thumb-y')) {
|
|
// the scrollbar was used, don't close flyout
|
|
return;
|
|
}
|
|
toggleTopbarFlyout(event.target);
|
|
}
|
|
|
|
function topbarFlyoutEscapeHandler(event) {
|
|
if (event.key == 'Escape') {
|
|
closeSomeTopbarButtonFlyout();
|
|
}
|
|
}
|
|
|
|
function toggleToc() {
|
|
toggleTopbarButtonFlyout(document.querySelector('.topbar-button-toc'));
|
|
}
|
|
|
|
function showEdit() {
|
|
var l = document.querySelector('.topbar-button-edit a');
|
|
if (l) {
|
|
l.click();
|
|
}
|
|
}
|
|
|
|
function showPrint() {
|
|
var l = document.querySelector('.topbar-button-print a');
|
|
if (l) {
|
|
l.click();
|
|
}
|
|
}
|
|
|
|
function navPrev() {
|
|
var e = document.querySelector('.topbar-button-prev a');
|
|
location.href = e && e.getAttribute('href');
|
|
}
|
|
|
|
function navNext() {
|
|
var e = document.querySelector('.topbar-button-next a');
|
|
location.href = e && e.getAttribute('href');
|
|
}
|
|
|
|
function initToc() {
|
|
if (isPrint) {
|
|
return;
|
|
}
|
|
|
|
document.addEventListener('keydown', editShortcutHandler);
|
|
document.addEventListener('keydown', navShortcutHandler);
|
|
document.addEventListener('keydown', printShortcutHandler);
|
|
document.addEventListener('keydown', searchShortcutHandler);
|
|
document.addEventListener('keydown', tocShortcutHandler);
|
|
document.addEventListener('keydown', navEscapeHandler);
|
|
document.addEventListener('keydown', topbarFlyoutEscapeHandler);
|
|
|
|
var b = document.querySelector('#R-body-overlay');
|
|
if (b) {
|
|
b.addEventListener('click', closeNav);
|
|
}
|
|
var m = document.querySelector('#R-main-overlay');
|
|
if (m) {
|
|
m.addEventListener('click', closeSomeTopbarButtonFlyout);
|
|
}
|
|
|
|
// finally give initial focus to allow keyboard scrolling in FF
|
|
documentFocus();
|
|
}
|
|
|
|
function initSwipeHandler() {
|
|
if (!touchsupport) {
|
|
return;
|
|
}
|
|
|
|
var startx = null;
|
|
var starty = null;
|
|
var handleStartX = function (evt) {
|
|
startx = evt.touches[0].clientX;
|
|
starty = evt.touches[0].clientY;
|
|
};
|
|
var handleMoveX = function (evt) {
|
|
if (startx !== null) {
|
|
var diffx = startx - evt.touches[0].clientX;
|
|
var diffy = starty - evt.touches[0].clientY || 0.1;
|
|
if (diffx / Math.abs(diffy) < 2) {
|
|
// detect mostly vertical swipes and reset our starting pos
|
|
// to not detect a horizontal move if vertical swipe is unprecise
|
|
startx = evt.touches[0].clientX;
|
|
} else if (diffx > 30) {
|
|
startx = null;
|
|
starty = null;
|
|
closeNav();
|
|
}
|
|
}
|
|
};
|
|
var handleEndX = function (evt) {
|
|
startx = null;
|
|
starty = null;
|
|
};
|
|
|
|
var s = document.querySelector('#R-body-overlay');
|
|
s && s.addEventListener('touchstart', handleStartX, { capture: false, passive: true });
|
|
document.querySelector('#R-sidebar').addEventListener('touchstart', handleStartX, { capture: false, passive: true });
|
|
document.querySelectorAll('#R-sidebar *').forEach(function (e) {
|
|
e.addEventListener('touchstart', handleStartX, { capture: false, passive: true });
|
|
});
|
|
s && s.addEventListener('touchmove', handleMoveX, { capture: false, passive: true });
|
|
document.querySelector('#R-sidebar').addEventListener('touchmove', handleMoveX, { capture: false, passive: true });
|
|
document.querySelectorAll('#R-sidebar *').forEach(function (e) {
|
|
e.addEventListener('touchmove', handleMoveX, { capture: false, passive: true });
|
|
});
|
|
s && s.addEventListener('touchend', handleEndX, { capture: false, passive: true });
|
|
document.querySelector('#R-sidebar').addEventListener('touchend', handleEndX, { capture: false, passive: true });
|
|
document.querySelectorAll('#R-sidebar *').forEach(function (e) {
|
|
e.addEventListener('touchend', handleEndX, { capture: false, passive: true });
|
|
});
|
|
}
|
|
|
|
function initImage() {
|
|
document.querySelectorAll('.lightbox-back').forEach(function (e) {
|
|
e.addEventListener('keydown', imageEscapeHandler);
|
|
});
|
|
}
|
|
|
|
function initExpand() {
|
|
document.querySelectorAll('.expand > input').forEach(function (e) {
|
|
e.addEventListener('change', initMermaid.bind(null, true, null));
|
|
});
|
|
}
|
|
|
|
function clearHistory() {
|
|
var visitedItem = window.relearn.absBaseUri + '/visited-url/';
|
|
for (var item in sessionStorage) {
|
|
if (item.substring(0, visitedItem.length) === visitedItem) {
|
|
sessionStorage.removeItem(item);
|
|
var url = item.substring(visitedItem.length);
|
|
// in case we have `relativeURLs=true` we have to strip the
|
|
// relative path to root
|
|
url = url.replace(/\.\.\//g, '/').replace(/^\/+\//, '/');
|
|
document.querySelectorAll('[data-nav-id="' + url + '"]').forEach(function (e) {
|
|
e.classList.remove('visited');
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function initHistory() {
|
|
var visitedItem = window.relearn.absBaseUri + '/visited-url/';
|
|
sessionStorage.setItem(visitedItem + document.querySelector('body').dataset.url, 1);
|
|
|
|
// loop through the sessionStorage and see if something should be marked as visited
|
|
for (var item in sessionStorage) {
|
|
if (item.substring(0, visitedItem.length) === visitedItem && sessionStorage.getItem(item) == 1) {
|
|
var url = item.substring(visitedItem.length);
|
|
// in case we have `relativeURLs=true` we have to strip the
|
|
// relative path to root
|
|
url = url.replace(/\.\.\//g, '/').replace(/^\/+\//, '/');
|
|
document.querySelectorAll('[data-nav-id="' + url + '"]').forEach(function (e) {
|
|
e.classList.add('visited');
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function initScrollPositionSaver() {
|
|
function savePosition(event) {
|
|
// #959 if we fiddle around with the history during print preview
|
|
// GC will close the preview immediatley
|
|
if (isPrintPreview) {
|
|
return;
|
|
}
|
|
var state = window.history.state || {};
|
|
state = Object.assign({}, typeof state === 'object' ? state : {});
|
|
state.contentScrollTop = +elc.scrollTop;
|
|
window.history.replaceState(state, '', window.location);
|
|
}
|
|
|
|
var ticking = false;
|
|
elc.addEventListener('scroll', function (event) {
|
|
if (!ticking) {
|
|
window.requestAnimationFrame(function () {
|
|
savePosition();
|
|
ticking = false;
|
|
});
|
|
ticking = true;
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', savePosition);
|
|
}
|
|
|
|
function scrollToPositions() {
|
|
// show active menu entry
|
|
window.setTimeout(function () {
|
|
var e = document.querySelector('#R-sidebar li.active a');
|
|
if (e && e.scrollIntoView) {
|
|
e.scrollIntoView({
|
|
block: 'center',
|
|
});
|
|
}
|
|
}, 10);
|
|
|
|
// scroll the content to point of interest;
|
|
// if we have a scroll position saved, the user was here
|
|
// before in his history stack and we want to reposition
|
|
// to the position he was when he left the page;
|
|
// otherwise if he used page search before, we want to position
|
|
// to its last outcome;
|
|
// otherwise he may want to see a specific fragment
|
|
|
|
var state = window.history.state || {};
|
|
state = typeof state === 'object' ? state : {};
|
|
if (state.hasOwnProperty('contentScrollTop')) {
|
|
window.setTimeout(function () {
|
|
elc.scrollTop = +state.contentScrollTop;
|
|
}, 10);
|
|
return;
|
|
}
|
|
|
|
var search = sessionStorage.getItem(window.relearn.absBaseUri + '/search-value');
|
|
if (search && search.length) {
|
|
search = regexEscape(search);
|
|
var found = elementContains(search, elc);
|
|
var searchedElem = found.length && found[0];
|
|
if (searchedElem) {
|
|
searchedElem.scrollIntoView();
|
|
var scrolledY = window.scrollY;
|
|
if (scrolledY) {
|
|
window.scroll(0, scrolledY - 125);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (window.location.hash && window.location.hash.length > 1) {
|
|
window.setTimeout(function () {
|
|
try {
|
|
var e = document.querySelector(window.location.hash);
|
|
if (e && e.scrollIntoView) {
|
|
e.scrollIntoView();
|
|
}
|
|
} catch (e) {}
|
|
}, 10);
|
|
return;
|
|
}
|
|
}
|
|
|
|
window.addEventListener('popstate', function (event) {
|
|
scrollToPositions();
|
|
});
|
|
|
|
const observer = new PerformanceObserver(function () {
|
|
scrollToPositions();
|
|
});
|
|
observer.observe({ type: 'navigation' });
|
|
|
|
function mark() {
|
|
// mark some additional stuff as searchable
|
|
var bodyInnerLinks = document.querySelectorAll('#R-body-inner a:not(.lightbox-link):not(.btn):not(.lightbox-back)');
|
|
for (var i = 0; i < bodyInnerLinks.length; i++) {
|
|
bodyInnerLinks[i].classList.add('highlight');
|
|
}
|
|
|
|
var value = sessionStorage.getItem(window.relearn.absBaseUri + '/search-value');
|
|
var highlightableElements = document.querySelectorAll('.highlightable');
|
|
highlight(highlightableElements, value, { element: 'mark', className: 'search' });
|
|
|
|
var markedElements = document.querySelectorAll('mark.search');
|
|
for (var i = 0; i < markedElements.length; i++) {
|
|
var parent = markedElements[i].parentNode;
|
|
while (parent && parent.classList) {
|
|
if (parent.classList.contains('expand')) {
|
|
var expandInputs = parent.querySelectorAll('input:not(.expand-marked)');
|
|
if (expandInputs.length) {
|
|
expandInputs[0].classList.add('expand-marked');
|
|
expandInputs[0].dataset.checked = expandInputs[0].checked ? 'true' : 'false';
|
|
expandInputs[0].checked = true;
|
|
}
|
|
}
|
|
if (parent.tagName.toLowerCase() === 'li' && parent.parentNode && parent.parentNode.tagName.toLowerCase() === 'ul' && parent.parentNode.classList.contains('collapsible-menu')) {
|
|
var toggleInputs = parent.querySelectorAll('input:not(.menu-marked)');
|
|
if (toggleInputs.length) {
|
|
toggleInputs[0].classList.add('menu-marked');
|
|
toggleInputs[0].dataset.checked = toggleInputs[0].checked ? 'true' : 'false';
|
|
toggleInputs[0].checked = true;
|
|
}
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
}
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
}
|
|
window.relearn.markSearch = mark;
|
|
|
|
function highlight(es, words, options) {
|
|
var settings = {
|
|
className: 'highlight',
|
|
element: 'span',
|
|
caseSensitive: false,
|
|
wordsOnly: false,
|
|
};
|
|
Object.assign(settings, options);
|
|
|
|
if (!words) {
|
|
return;
|
|
}
|
|
if (words.constructor === String) {
|
|
words = [words];
|
|
}
|
|
words = words.filter(function (word, i) {
|
|
return word != '';
|
|
});
|
|
words = words.map(function (word, i) {
|
|
return regexEscape(word);
|
|
});
|
|
if (words.length == 0) {
|
|
return this;
|
|
}
|
|
|
|
var flag = settings.caseSensitive ? '' : 'i';
|
|
var pattern = '(' + words.join('|') + ')';
|
|
if (settings.wordsOnly) {
|
|
pattern = '\\b' + pattern + '\\b';
|
|
}
|
|
var re = new RegExp(pattern, flag);
|
|
|
|
for (var i = 0; i < es.length; i++) {
|
|
highlightNode(es[i], re, settings.element, settings.className);
|
|
}
|
|
}
|
|
|
|
function highlightNode(node, re, nodeName, className) {
|
|
if (node.nodeType === 3 && node.parentElement && node.parentElement.namespaceURI == 'http://www.w3.org/1999/xhtml') {
|
|
// text nodes
|
|
var match = node.data.match(re);
|
|
if (match) {
|
|
var highlight = document.createElement(nodeName || 'span');
|
|
highlight.className = className || 'highlight';
|
|
var wordNode = node.splitText(match.index);
|
|
wordNode.splitText(match[0].length);
|
|
var wordClone = wordNode.cloneNode(true);
|
|
highlight.appendChild(wordClone);
|
|
wordNode.parentNode.replaceChild(highlight, wordNode);
|
|
return 1; //skip added node in parent
|
|
}
|
|
} else if (
|
|
node.nodeType === 1 &&
|
|
node.childNodes && // only element nodes that have children
|
|
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
|
|
!(node.tagName === nodeName.toUpperCase() && node.className === className)
|
|
) {
|
|
// skip if already highlighted
|
|
for (var i = 0; i < node.childNodes.length; i++) {
|
|
i += highlightNode(node.childNodes[i], re, nodeName, className);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function unmark() {
|
|
sessionStorage.removeItem(window.relearn.absBaseUri + '/search-value');
|
|
var markedElements = document.querySelectorAll('mark.search');
|
|
for (var i = 0; i < markedElements.length; i++) {
|
|
var parent = markedElements[i].parentNode;
|
|
while (parent && parent.classList) {
|
|
if (parent.tagName.toLowerCase() === 'li' && parent.parentNode && parent.parentNode.tagName.toLowerCase() === 'ul' && parent.parentNode.classList.contains('collapsible-menu')) {
|
|
var toggleInputs = parent.querySelectorAll('input.menu-marked');
|
|
if (toggleInputs.length) {
|
|
toggleInputs[0].checked = toggleInputs[0].dataset.checked === 'true';
|
|
toggleInputs[0].dataset.checked = null;
|
|
toggleInputs[0].classList.remove('menu-marked');
|
|
}
|
|
}
|
|
if (parent.classList.contains('expand')) {
|
|
var expandInputs = parent.querySelectorAll('input.expand-marked');
|
|
if (expandInputs.length) {
|
|
expandInputs[0].checked = expandInputs[0].dataset.checked === 'true';
|
|
expandInputs[0].dataset.checked = null;
|
|
expandInputs[0].classList.remove('expand-marked');
|
|
}
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
}
|
|
|
|
var highlighted = document.querySelectorAll('.highlightable');
|
|
unhighlight(highlighted, { element: 'mark', className: 'search' });
|
|
psm &&
|
|
setTimeout(function () {
|
|
psm.update();
|
|
}, 10);
|
|
}
|
|
|
|
function unhighlight(es, options) {
|
|
var settings = {
|
|
className: 'highlight',
|
|
element: 'span',
|
|
};
|
|
Object.assign(settings, options);
|
|
|
|
for (var i = 0; i < es.length; i++) {
|
|
var highlightedElements = es[i].querySelectorAll(settings.element + '.' + settings.className);
|
|
for (var j = 0; j < highlightedElements.length; j++) {
|
|
var parent = highlightedElements[j].parentNode;
|
|
parent.replaceChild(highlightedElements[j].firstChild, highlightedElements[j]);
|
|
parent.normalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
// replace jQuery.createPseudo with https://stackoverflow.com/a/66318392
|
|
function elementContains(txt, e) {
|
|
var regex = RegExp(txt, 'i');
|
|
var nodes = [];
|
|
if (e) {
|
|
var tree = document.createTreeWalker(
|
|
e,
|
|
4 /* NodeFilter.SHOW_TEXT */,
|
|
function (node) {
|
|
return regex.test(node.data);
|
|
},
|
|
false
|
|
);
|
|
var node = null;
|
|
while ((node = tree.nextNode())) {
|
|
nodes.push(node.parentElement);
|
|
}
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
function searchInputHandler(value) {
|
|
unmark();
|
|
if (value.length) {
|
|
sessionStorage.setItem(window.relearn.absBaseUri + '/search-value', value);
|
|
mark();
|
|
}
|
|
}
|
|
|
|
function initSearch() {
|
|
// sync input/escape between searchbox and searchdetail
|
|
var inputs = document.querySelectorAll('input.search-by');
|
|
inputs.forEach(function (e) {
|
|
e.addEventListener('keydown', function (event) {
|
|
if (event.key == 'Escape') {
|
|
var input = event.target;
|
|
var search = sessionStorage.getItem(window.relearn.absBaseUri + '/search-value');
|
|
if (!search || !search.length) {
|
|
input.blur();
|
|
}
|
|
searchInputHandler('');
|
|
inputs.forEach(function (e) {
|
|
e.value = '';
|
|
});
|
|
if (!search || !search.length) {
|
|
documentFocus();
|
|
}
|
|
}
|
|
});
|
|
e.addEventListener('input', function (event) {
|
|
var input = event.target;
|
|
var value = input.value;
|
|
searchInputHandler(value);
|
|
inputs.forEach(function (e) {
|
|
if (e != input) {
|
|
e.value = value;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('[data-search-clear]').forEach(function (e) {
|
|
e.addEventListener('click', function () {
|
|
inputs.forEach(function (e) {
|
|
e.value = '';
|
|
var event = document.createEvent('Event');
|
|
event.initEvent('input', false, false);
|
|
e.dispatchEvent(event);
|
|
});
|
|
unmark();
|
|
});
|
|
});
|
|
|
|
var urlParams = new URLSearchParams(window.location.search);
|
|
var value = urlParams.get('search-by');
|
|
if (value) {
|
|
sessionStorage.setItem(window.relearn.absBaseUri + '/search-value', value);
|
|
}
|
|
mark();
|
|
|
|
// set initial search value for inputs on page load
|
|
if (sessionStorage.getItem(window.relearn.absBaseUri + '/search-value')) {
|
|
var search = sessionStorage.getItem(window.relearn.absBaseUri + '/search-value');
|
|
inputs.forEach(function (e) {
|
|
e.value = search;
|
|
var event = document.createEvent('Event');
|
|
event.initEvent('input', false, false);
|
|
e.dispatchEvent(event);
|
|
});
|
|
}
|
|
|
|
window.relearn.isSearchInit = true;
|
|
window.relearn.runInitialSearch && window.relearn.runInitialSearch();
|
|
}
|
|
|
|
document.addEventListener('themeVariantLoaded', function (ev) {
|
|
updateTheme(ev);
|
|
});
|
|
|
|
function updateTheme(ev) {
|
|
if (window.relearn.lastVariant == ev.detail.variant) {
|
|
return;
|
|
}
|
|
window.relearn.lastVariant = ev.detail.variant;
|
|
|
|
initMermaid(true);
|
|
initOpenapi(true);
|
|
}
|
|
|
|
(function () {
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
|
|
initMermaid(true);
|
|
initOpenapi(true);
|
|
});
|
|
})();
|
|
|
|
function useMermaid(config) {
|
|
delete config.theme;
|
|
window.relearn.mermaidConfig = config;
|
|
if (typeof mermaid != 'undefined' && typeof mermaid.mermaidAPI != 'undefined') {
|
|
mermaid.initialize(Object.assign({ securityLevel: 'antiscript', startOnLoad: false }, config));
|
|
}
|
|
}
|
|
if (window.themeUseMermaid) {
|
|
useMermaid(window.themeUseMermaid);
|
|
}
|
|
|
|
function useOpenapi(config) {
|
|
if (config.css && config.cssInProject) {
|
|
config.css = window.relearn.relBasePath + config.css;
|
|
}
|
|
}
|
|
if (window.themeUseOpenapi) {
|
|
useOpenapi(window.themeUseOpenapi);
|
|
}
|
|
|
|
function ready(fn) {
|
|
if (document.readyState == 'complete') {
|
|
fn();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', fn);
|
|
}
|
|
}
|
|
|
|
ready(function () {
|
|
initArrowVerticalNav();
|
|
initArrowHorizontalNav();
|
|
initMermaid();
|
|
initOpenapi();
|
|
initMenuScrollbar();
|
|
initToc();
|
|
initAnchorClipboard();
|
|
initCodeClipboard();
|
|
fixCodeTabs();
|
|
restoreTabSelections();
|
|
initSwipeHandler();
|
|
initHistory();
|
|
initSearch();
|
|
initImage();
|
|
initExpand();
|
|
initScrollPositionSaver();
|
|
});
|
|
|
|
(function () {
|
|
var body = document.querySelector('body');
|
|
var topbar = document.querySelector('#R-topbar');
|
|
function addTopbarButtonInfos() {
|
|
// initially add some management infos to buttons and areas
|
|
var areas = body.querySelectorAll('.topbar-area');
|
|
areas.forEach(function (area) {
|
|
area.dataset.area = 'area-' + area.dataset.area;
|
|
var buttons = area.querySelectorAll(':scope > .topbar-button');
|
|
buttons.forEach(function (button) {
|
|
button.dataset.origin = area.dataset.area;
|
|
button.dataset.action = 'show';
|
|
var placeholder = document.createElement('div');
|
|
placeholder.classList.add('topbar-placeholder');
|
|
placeholder.dataset.action = 'show';
|
|
button.insertAdjacentElement('afterend', placeholder);
|
|
});
|
|
var placeholder = document.createElement('div');
|
|
area.insertAdjacentElement('beforeend', placeholder);
|
|
var hidden = document.createElement('div');
|
|
hidden.classList.add('topbar-hidden');
|
|
hidden.dataset.area = area.dataset.area;
|
|
var hplaceholder = document.createElement('div');
|
|
hidden.insertAdjacentElement('beforeend', hplaceholder);
|
|
area.insertAdjacentElement('afterend', hidden);
|
|
});
|
|
}
|
|
function moveAreaTopbarButtons(width) {
|
|
topbar.querySelectorAll('.topbar-hidden .topbar-button').forEach(function (button) {
|
|
// move hidden to origins area
|
|
var placeholder = button.parentNode.parentNode.querySelector(':scope > .topbar-area .topbar-placeholder[data-action="hide"]');
|
|
placeholder.dataset.action = 'show';
|
|
button.dataset.action = 'show';
|
|
placeholder.insertAdjacentElement('beforebegin', button);
|
|
});
|
|
topbar.querySelectorAll('.topbar-area .topbar-button').forEach(function (button) {
|
|
var current_area = button.dataset.action;
|
|
var origin_area = button.dataset.origin;
|
|
if (current_area != 'show' && origin_area != current_area) {
|
|
// move moved to origins area
|
|
var placeholder = topbar.querySelector('.topbar-area[data-area="' + origin_area + '"] > .topbar-placeholder[data-action="' + current_area + '"]');
|
|
placeholder.dataset.action = 'show';
|
|
button.dataset.action = 'show';
|
|
placeholder.insertAdjacentElement('beforebegin', button);
|
|
}
|
|
});
|
|
Array.from(topbar.querySelectorAll('.topbar-area .topbar-button'))
|
|
.reverse()
|
|
.forEach(function (button) {
|
|
var parent = button.parentElement;
|
|
var current_area = parent.dataset.area;
|
|
var action = button.dataset['width' + width.toUpperCase()];
|
|
if (action == 'show') {
|
|
} else if (action == 'hide') {
|
|
// move to origins hidden
|
|
var hidden = button.parentNode.parentNode.querySelector(':scope > .topbar-hidden > *');
|
|
var placeholder = button.nextSibling;
|
|
placeholder.dataset.action = action;
|
|
button.dataset.action = action;
|
|
hidden.insertAdjacentElement('beforebegin', button);
|
|
} else if (action != current_area) {
|
|
// move to action area
|
|
var dest = button.parentNode.parentNode.querySelector('.topbar-area[data-area="' + action + '"] > *');
|
|
if (dest) {
|
|
var placeholder = button.nextSibling;
|
|
placeholder.dataset.action = action;
|
|
button.dataset.action = action;
|
|
dest.insertAdjacentElement('beforebegin', button);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function moveTopbarButtons() {
|
|
var isS = body.classList.contains('menu-width-s');
|
|
var isM = body.classList.contains('menu-width-m');
|
|
var isL = body.classList.contains('menu-width-l');
|
|
// move buttons once, width has a distinct value
|
|
if (isS && !isM && !isL) {
|
|
moveAreaTopbarButtons('s');
|
|
} else if (!isS && isM && !isL) {
|
|
moveAreaTopbarButtons('m');
|
|
} else if (!isS && !isM && isL) {
|
|
moveAreaTopbarButtons('l');
|
|
}
|
|
}
|
|
function adjustEmptyTopbarContents() {
|
|
var buttons = Array.from(document.querySelectorAll('.topbar-button > .topbar-content > .topbar-content-wrapper'));
|
|
// we have to reverse order to make sure to handle innermost areas first
|
|
buttons.reverse().forEach(function (wrapper) {
|
|
var button = getTopbarButtonParent(wrapper);
|
|
if (button) {
|
|
var isEmpty = true;
|
|
var area = wrapper.querySelector(':scope > .topbar-area');
|
|
if (area) {
|
|
// if it's an area, we have to check each contained button
|
|
// manually for its display property
|
|
var areabuttons = area.querySelectorAll(':scope > .topbar-button');
|
|
isEmpty = true;
|
|
areabuttons.forEach(function (ab) {
|
|
if (ab.style.display != 'none') {
|
|
isEmpty = false;
|
|
}
|
|
});
|
|
} else {
|
|
var clone = wrapper.cloneNode(true);
|
|
var irrelevant = clone.querySelectorAll('div.ps__rail-x, div.ps__rail-y');
|
|
irrelevant.forEach(function (e) {
|
|
e.parentNode.removeChild(e);
|
|
});
|
|
isEmpty = !clone.innerHTML.trim();
|
|
}
|
|
button.querySelector('button').disabled = isEmpty;
|
|
button.style.display = isEmpty && button.dataset.contentEmpty == 'hide' ? 'none' : 'inline-block';
|
|
}
|
|
});
|
|
}
|
|
function setWidthS(e) {
|
|
body.classList[e.matches ? 'add' : 'remove']('menu-width-s');
|
|
}
|
|
function setWidthM(e) {
|
|
body.classList[e.matches ? 'add' : 'remove']('menu-width-m');
|
|
}
|
|
function setWidthL(e) {
|
|
body.classList[e.matches ? 'add' : 'remove']('menu-width-l');
|
|
}
|
|
function onWidthChange(setWidth, e) {
|
|
setWidth(e);
|
|
moveTopbarButtons();
|
|
adjustEmptyTopbarContents();
|
|
}
|
|
var mqs = window.matchMedia('only screen and (max-width: 47.999rem)');
|
|
mqs.addEventListener('change', onWidthChange.bind(null, setWidthS));
|
|
var mqm = window.matchMedia('only screen and (min-width: 48rem) and (max-width: 59.999rem)');
|
|
mqm.addEventListener('change', onWidthChange.bind(null, setWidthM));
|
|
var mql = window.matchMedia('only screen and (min-width: 60rem)');
|
|
mql.addEventListener('change', onWidthChange.bind(null, setWidthL));
|
|
|
|
addTopbarButtonInfos();
|
|
setWidthS(mqs);
|
|
setWidthM(mqm);
|
|
setWidthL(mql);
|
|
moveTopbarButtons();
|
|
adjustEmptyTopbarContents();
|
|
})();
|
|
|
|
(function () {
|
|
var body = document.querySelector('body');
|
|
function setWidth(e) {
|
|
body.classList[e.matches ? 'add' : 'remove']('main-width-max');
|
|
}
|
|
function onWidthChange(setWidth, e) {
|
|
setWidth(e);
|
|
}
|
|
var width = getColorValue('MAIN-WIDTH-MAX');
|
|
var mqm = window.matchMedia('screen and ( min-width: ' + width + ')');
|
|
mqm.addEventListener('change', onWidthChange.bind(null, setWidth));
|
|
setWidth(mqm);
|
|
})();
|
|
|
|
function getColorValue(c) {
|
|
return this.normalizeColor(getComputedStyle(document.documentElement).getPropertyValue('--INTERNAL-' + c));
|
|
}
|
|
|
|
function normalizeColor(c) {
|
|
if (!c || !c.trim) {
|
|
return c;
|
|
}
|
|
c = c.trim();
|
|
c = c.replace(/\s*\(\s*/g, '( ');
|
|
c = c.replace(/\s*\)\s*/g, ' )');
|
|
c = c.replace(/\s*,\s*/g, ', ');
|
|
c = c.replace(/0*\./g, '.');
|
|
c = c.replace(/ +/g, ' ');
|
|
return c;
|
|
}
|