Added documentation; Added multiple joins
This commit is contained in:
parent
aa0b62be09
commit
85499e837c
3
docs/AlterTable.html
Normal file
3
docs/AlterTable.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/CreateTable.html
Normal file
3
docs/CreateTable.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Delete.html
Normal file
3
docs/Delete.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Insert.html
Normal file
3
docs/Insert.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Instance.html
Normal file
3
docs/Instance.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Select.html
Normal file
3
docs/Select.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Structure.html
Normal file
3
docs/Structure.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/Update.html
Normal file
3
docs/Update.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/awSQL.html
Normal file
3
docs/awSQL.html
Normal file
File diff suppressed because one or more lines are too long
1
docs/data/search.json
Normal file
1
docs/data/search.json
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/fonts/Inconsolata-Regular.ttf
Normal file
BIN
docs/fonts/Inconsolata-Regular.ttf
Normal file
Binary file not shown.
BIN
docs/fonts/OpenSans-Regular.ttf
Normal file
BIN
docs/fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
BIN
docs/fonts/WorkSans-Bold.ttf
Normal file
BIN
docs/fonts/WorkSans-Bold.ttf
Normal file
Binary file not shown.
3
docs/global.html
Normal file
3
docs/global.html
Normal file
File diff suppressed because one or more lines are too long
3
docs/index.html
Normal file
3
docs/index.html
Normal file
File diff suppressed because one or more lines are too long
726
docs/scripts/core.js
Normal file
726
docs/scripts/core.js
Normal file
@ -0,0 +1,726 @@
|
||||
/* global document */
|
||||
var accordionLocalStorageKey = 'accordion-id';
|
||||
var themeLocalStorageKey = 'theme';
|
||||
var fontSizeLocalStorageKey = 'font-size';
|
||||
var html = document.querySelector('html');
|
||||
|
||||
var MAX_FONT_SIZE = 30;
|
||||
var MIN_FONT_SIZE = 10;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var localStorage = window.localStorage;
|
||||
|
||||
function getTheme() {
|
||||
var theme = localStorage.getItem(themeLocalStorageKey);
|
||||
|
||||
if (theme) return theme;
|
||||
|
||||
theme = document.body.getAttribute('data-theme');
|
||||
|
||||
switch (theme) {
|
||||
case 'dark':
|
||||
case 'light':
|
||||
return theme;
|
||||
case 'fallback-dark':
|
||||
if (
|
||||
// eslint-disable-next-line no-undef
|
||||
window.matchMedia('(prefers-color-scheme)').matches &&
|
||||
// eslint-disable-next-line no-undef
|
||||
window.matchMedia('(prefers-color-scheme: light)').matches
|
||||
) {
|
||||
return 'light';
|
||||
}
|
||||
|
||||
return 'dark';
|
||||
|
||||
case 'fallback-light':
|
||||
if (
|
||||
// eslint-disable-next-line no-undef
|
||||
window.matchMedia('(prefers-color-scheme)').matches &&
|
||||
// eslint-disable-next-line no-undef
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
) {
|
||||
return 'dark';
|
||||
}
|
||||
|
||||
return 'light';
|
||||
|
||||
default:
|
||||
return 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
function localUpdateTheme(theme) {
|
||||
var body = document.body;
|
||||
var svgUse = document.querySelectorAll('.theme-svg-use');
|
||||
var iconID = theme === 'dark' ? '#light-theme-icon' : '#dark-theme-icon';
|
||||
|
||||
body.setAttribute('data-theme', theme);
|
||||
body.classList.remove('dark', 'light');
|
||||
body.classList.add(theme);
|
||||
|
||||
svgUse.forEach(function (svg) {
|
||||
svg.setAttribute('xlink:href', iconID);
|
||||
});
|
||||
}
|
||||
|
||||
function updateTheme(theme) {
|
||||
localUpdateTheme(theme);
|
||||
localStorage.setItem(themeLocalStorageKey, theme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
var body = document.body;
|
||||
var theme = body.getAttribute('data-theme');
|
||||
|
||||
var newTheme = theme === 'dark' ? 'light' : 'dark';
|
||||
|
||||
updateTheme(newTheme);
|
||||
}
|
||||
|
||||
(function () {
|
||||
var theme = getTheme();
|
||||
|
||||
updateTheme(theme);
|
||||
})();
|
||||
|
||||
/**
|
||||
* Function to set accordion id to localStorage.
|
||||
* @param {string} id Accordion id
|
||||
*/
|
||||
function setAccordionIdToLocalStorage(id) {
|
||||
/**
|
||||
* @type {object}
|
||||
*/
|
||||
var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey));
|
||||
|
||||
ids[id] = id;
|
||||
localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to remove accordion id from localStorage.
|
||||
* @param {string} id Accordion id
|
||||
*/
|
||||
function removeAccordionIdFromLocalStorage(id) {
|
||||
/**
|
||||
* @type {object}
|
||||
*/
|
||||
var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey));
|
||||
|
||||
delete ids[id];
|
||||
localStorage.setItem(accordionLocalStorageKey, JSON.stringify(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get all accordion ids from localStorage.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
function getAccordionIdsFromLocalStorage() {
|
||||
/**
|
||||
* @type {object}
|
||||
*/
|
||||
var ids = JSON.parse(localStorage.getItem(accordionLocalStorageKey));
|
||||
|
||||
return ids || {};
|
||||
}
|
||||
|
||||
function toggleAccordion(element) {
|
||||
var currentNode = element;
|
||||
var isCollapsed = currentNode.getAttribute('data-isopen') === 'false';
|
||||
|
||||
if (isCollapsed) {
|
||||
currentNode.setAttribute('data-isopen', 'true');
|
||||
setAccordionIdToLocalStorage(currentNode.id);
|
||||
} else {
|
||||
currentNode.setAttribute('data-isopen', 'false');
|
||||
removeAccordionIdFromLocalStorage(currentNode.id);
|
||||
}
|
||||
}
|
||||
|
||||
function initAccordion() {
|
||||
if (
|
||||
localStorage.getItem(accordionLocalStorageKey) === undefined ||
|
||||
localStorage.getItem(accordionLocalStorageKey) === null
|
||||
) {
|
||||
localStorage.setItem(accordionLocalStorageKey, '{}');
|
||||
}
|
||||
var allAccordion = document.querySelectorAll('.sidebar-section-title');
|
||||
var ids = getAccordionIdsFromLocalStorage();
|
||||
|
||||
allAccordion.forEach(function (item) {
|
||||
item.addEventListener('click', function () {
|
||||
toggleAccordion(item);
|
||||
});
|
||||
if (item.id in ids) {
|
||||
toggleAccordion(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isSourcePage() {
|
||||
return Boolean(document.querySelector('#source-page'));
|
||||
}
|
||||
|
||||
function bringElementIntoView(element, updateHistory = true) {
|
||||
// If element is null then we are not going further
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* tocbotInstance is defined in layout.tmpl
|
||||
* It is defined when we are initializing tocbot.
|
||||
*
|
||||
*/
|
||||
// eslint-disable-next-line no-undef
|
||||
if (tocbotInstance) {
|
||||
setTimeout(
|
||||
// eslint-disable-next-line no-undef
|
||||
() => tocbotInstance.updateTocListActiveElement(element),
|
||||
60
|
||||
);
|
||||
}
|
||||
var navbar = document.querySelector('.navbar-container');
|
||||
var body = document.querySelector('.main-content');
|
||||
var elementTop = element.getBoundingClientRect().top;
|
||||
|
||||
var offset = 16;
|
||||
|
||||
if (navbar) {
|
||||
offset += navbar.scrollHeight;
|
||||
}
|
||||
|
||||
if (body) {
|
||||
body.scrollBy(0, elementTop - offset);
|
||||
}
|
||||
|
||||
if (updateHistory) {
|
||||
// eslint-disable-next-line no-undef
|
||||
history.pushState(null, null, '#' + element.id);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function bringLinkToView(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var id = event.currentTarget.getAttribute('href');
|
||||
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = document.getElementById(id.slice(1));
|
||||
|
||||
if (element) {
|
||||
bringElementIntoView(element);
|
||||
}
|
||||
}
|
||||
|
||||
function bringIdToViewOnMount() {
|
||||
if (isSourcePage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var id = window.location.hash;
|
||||
|
||||
if (id === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = document.getElementById(id.slice(1));
|
||||
|
||||
if (!element) {
|
||||
id = decodeURI(id);
|
||||
element = document.getElementById(id.slice(1));
|
||||
}
|
||||
|
||||
if (element) {
|
||||
bringElementIntoView(element, false);
|
||||
}
|
||||
}
|
||||
|
||||
function createAnchorElement(id) {
|
||||
var anchor = document.createElement('a');
|
||||
|
||||
anchor.textContent = '#';
|
||||
anchor.href = '#' + id;
|
||||
anchor.classList.add('link-anchor');
|
||||
anchor.onclick = bringLinkToView;
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
function addAnchor() {
|
||||
var main = document.querySelector('.main-content').querySelector('section');
|
||||
|
||||
var h1 = main.querySelectorAll('h1');
|
||||
var h2 = main.querySelectorAll('h2');
|
||||
var h3 = main.querySelectorAll('h3');
|
||||
var h4 = main.querySelectorAll('h4');
|
||||
|
||||
var targets = [h1, h2, h3, h4];
|
||||
|
||||
targets.forEach(function (target) {
|
||||
target.forEach(function (heading) {
|
||||
var anchor = createAnchorElement(heading.id);
|
||||
|
||||
heading.classList.add('has-anchor');
|
||||
heading.append(anchor);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
function copy(value) {
|
||||
const el = document.createElement('textarea');
|
||||
|
||||
el.value = value;
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
}
|
||||
|
||||
function showTooltip(id) {
|
||||
var tooltip = document.getElementById(id);
|
||||
|
||||
tooltip.classList.add('show-tooltip');
|
||||
setTimeout(function () {
|
||||
tooltip.classList.remove('show-tooltip');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
function copyFunction(id) {
|
||||
// selecting the pre element
|
||||
var code = document.getElementById(id);
|
||||
|
||||
// selecting the ol.linenums
|
||||
var element = code.querySelector('.linenums');
|
||||
|
||||
if (!element) {
|
||||
// selecting the code block
|
||||
element = code.querySelector('code');
|
||||
}
|
||||
|
||||
// copy
|
||||
copy(element.innerText.trim().replace(/(^\t)/gm, ''));
|
||||
|
||||
// show tooltip
|
||||
showTooltip('tooltip-' + id);
|
||||
}
|
||||
|
||||
function hideTocOnSourcePage() {
|
||||
if (isSourcePage()) {
|
||||
document.querySelector('.toc-container').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function getPreTopBar(id, lang = '') {
|
||||
// tooltip
|
||||
var tooltip = '<div class="tooltip" id="tooltip-' + id + '">Copied!</div>';
|
||||
|
||||
// template of copy to clipboard icon container
|
||||
var copyToClipboard =
|
||||
'<button aria-label="copy code" class="icon-button copy-code" onclick="copyFunction(\'' +
|
||||
id +
|
||||
'\')"><svg class="sm-icon" alt="click to copy"><use xlink:href="#copy-icon"></use></svg>' +
|
||||
tooltip +
|
||||
'</button>';
|
||||
|
||||
var langNameDiv =
|
||||
'<div class="code-lang-name-container"><div class="code-lang-name">' +
|
||||
lang.toLocaleUpperCase() +
|
||||
'</div></div>';
|
||||
|
||||
var topBar =
|
||||
'<div class="pre-top-bar-container">' +
|
||||
langNameDiv +
|
||||
copyToClipboard +
|
||||
'</div>';
|
||||
|
||||
return topBar;
|
||||
}
|
||||
|
||||
function getPreDiv() {
|
||||
var divElement = document.createElement('div');
|
||||
|
||||
divElement.classList.add('pre-div');
|
||||
|
||||
return divElement;
|
||||
}
|
||||
|
||||
function processAllPre() {
|
||||
var targets = document.querySelectorAll('pre');
|
||||
var footer = document.querySelector('#PeOAagUepe');
|
||||
var navbar = document.querySelector('#VuAckcnZhf');
|
||||
|
||||
var navbarHeight = 0;
|
||||
var footerHeight = 0;
|
||||
|
||||
if (footer) {
|
||||
footerHeight = footer.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
if (navbar) {
|
||||
navbarHeight = navbar.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
var preMaxHeight = window.innerHeight - navbarHeight - footerHeight - 250;
|
||||
|
||||
targets.forEach(function (pre, idx) {
|
||||
var parent = pre.parentNode;
|
||||
|
||||
if (parent && parent.getAttribute('data-skip-pre-process') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
var div = getPreDiv();
|
||||
var id = 'ScDloZOMdL' + idx;
|
||||
|
||||
var lang = pre.getAttribute('data-lang') || 'code';
|
||||
var topBar = getPreTopBar(id, lang);
|
||||
|
||||
div.innerHTML = topBar;
|
||||
|
||||
pre.style.maxHeight = preMaxHeight + 'px';
|
||||
pre.id = id;
|
||||
pre.classList.add('prettyprint');
|
||||
pre.parentNode.insertBefore(div, pre);
|
||||
div.appendChild(pre);
|
||||
});
|
||||
}
|
||||
|
||||
function highlightAndBringLineIntoView() {
|
||||
// eslint-disable-next-line no-undef
|
||||
var lineNumber = window.location.hash.replace('#line', '');
|
||||
|
||||
try {
|
||||
var selector = '[data-line-number="' + lineNumber + '"';
|
||||
|
||||
var element = document.querySelector(selector);
|
||||
|
||||
element.scrollIntoView();
|
||||
element.parentNode.classList.add('selected');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function getFontSize() {
|
||||
var currentFontSize = 16;
|
||||
|
||||
try {
|
||||
currentFontSize = Number.parseInt(
|
||||
html.style.fontSize.split('px')[0],
|
||||
10
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return currentFontSize;
|
||||
}
|
||||
|
||||
function localUpdateFontSize(fontSize) {
|
||||
html.style.fontSize = fontSize + 'px';
|
||||
|
||||
var fontSizeText = document.querySelector(
|
||||
'#b77a68a492f343baabea06fad81f651e'
|
||||
);
|
||||
|
||||
if (fontSizeText) {
|
||||
fontSizeText.innerHTML = fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFontSize(fontSize) {
|
||||
localUpdateFontSize(fontSize);
|
||||
localStorage.setItem(fontSizeLocalStorageKey, fontSize);
|
||||
}
|
||||
|
||||
(function () {
|
||||
var fontSize = getFontSize();
|
||||
var fontSizeInLocalStorage = localStorage.getItem(fontSizeLocalStorageKey);
|
||||
|
||||
if (fontSizeInLocalStorage) {
|
||||
var n = Number.parseInt(fontSizeInLocalStorage, 10);
|
||||
|
||||
if (n === fontSize) {
|
||||
return;
|
||||
}
|
||||
updateFontSize(n);
|
||||
} else {
|
||||
updateFontSize(fontSize);
|
||||
}
|
||||
})();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function incrementFont(event) {
|
||||
var n = getFontSize();
|
||||
|
||||
if (n < MAX_FONT_SIZE) {
|
||||
updateFontSize(n + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function decrementFont(event) {
|
||||
var n = getFontSize();
|
||||
|
||||
if (n > MIN_FONT_SIZE) {
|
||||
updateFontSize(n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function fontSizeTooltip() {
|
||||
var fontSize = getFontSize();
|
||||
|
||||
return `
|
||||
<div class="font-size-tooltip">
|
||||
<button aria-label="decrease-font-size" class="icon-button ${
|
||||
fontSize >= MAX_FONT_SIZE ? 'disabled' : ''
|
||||
}" onclick="decrementFont(event)">
|
||||
<svg>
|
||||
<use xlink:href="#minus-icon"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="font-size-text" id="b77a68a492f343baabea06fad81f651e">
|
||||
${fontSize}
|
||||
</div>
|
||||
<button aria-label="increase-font-size" class="icon-button ${
|
||||
fontSize <= MIN_FONT_SIZE ? 'disabled' : ''
|
||||
}" onclick="incrementFont(event)">
|
||||
<svg>
|
||||
<use xlink:href="#add-icon"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="icon-button" onclick="updateFontSize(16)">
|
||||
<svg>
|
||||
<use xlink:href="#reset-icon"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
function initTooltip() {
|
||||
// add tooltip to navbar item
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.theme-toggle', {
|
||||
content: 'Toggle Theme',
|
||||
delay: 500,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.search-button', {
|
||||
content: 'Search',
|
||||
delay: 500,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.font-size', {
|
||||
content: 'Change font size',
|
||||
delay: 500,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.codepen-button', {
|
||||
content: 'Open code in CodePen',
|
||||
placement: 'left',
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.copy-code', {
|
||||
content: 'Copy this code',
|
||||
placement: 'left',
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
tippy('.font-size', {
|
||||
content: fontSizeTooltip(),
|
||||
trigger: 'click',
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
placement: 'left',
|
||||
});
|
||||
}
|
||||
|
||||
function fixTable() {
|
||||
const tables = document.querySelectorAll('table');
|
||||
|
||||
for (const table of tables) {
|
||||
if (table.classList.contains('hljs-ln')) {
|
||||
// don't want to wrap code blocks.
|
||||
return;
|
||||
}
|
||||
|
||||
var div = document.createElement('div');
|
||||
|
||||
div.classList.add('table-div');
|
||||
table.parentNode.insertBefore(div, table);
|
||||
div.appendChild(table);
|
||||
}
|
||||
}
|
||||
|
||||
function hideMobileMenu() {
|
||||
var mobileMenuContainer = document.querySelector('#mobile-sidebar');
|
||||
var target = document.querySelector('#mobile-menu');
|
||||
var svgUse = target.querySelector('use');
|
||||
|
||||
if (mobileMenuContainer) {
|
||||
mobileMenuContainer.classList.remove('show');
|
||||
}
|
||||
if (target) {
|
||||
target.setAttribute('data-isopen', 'false');
|
||||
}
|
||||
if (svgUse) {
|
||||
svgUse.setAttribute('xlink:href', '#menu-icon');
|
||||
}
|
||||
}
|
||||
|
||||
function showMobileMenu() {
|
||||
var mobileMenuContainer = document.querySelector('#mobile-sidebar');
|
||||
var target = document.querySelector('#mobile-menu');
|
||||
var svgUse = target.querySelector('use');
|
||||
|
||||
if (mobileMenuContainer) {
|
||||
mobileMenuContainer.classList.add('show');
|
||||
}
|
||||
if (target) {
|
||||
target.setAttribute('data-isopen', 'true');
|
||||
}
|
||||
if (svgUse) {
|
||||
svgUse.setAttribute('xlink:href', '#close-icon');
|
||||
}
|
||||
}
|
||||
|
||||
function onMobileMenuClick() {
|
||||
var target = document.querySelector('#mobile-menu');
|
||||
var isOpen = target.getAttribute('data-isopen') === 'true';
|
||||
|
||||
if (isOpen) {
|
||||
hideMobileMenu();
|
||||
} else {
|
||||
showMobileMenu();
|
||||
}
|
||||
}
|
||||
|
||||
function initMobileMenu() {
|
||||
var menu = document.querySelector('#mobile-menu');
|
||||
|
||||
if (menu) {
|
||||
menu.addEventListener('click', onMobileMenuClick);
|
||||
}
|
||||
}
|
||||
|
||||
function addHrefToSidebarTitle() {
|
||||
var titles = document.querySelectorAll('.sidebar-title-anchor');
|
||||
|
||||
titles.forEach(function (title) {
|
||||
// eslint-disable-next-line no-undef
|
||||
title.setAttribute('href', baseURL);
|
||||
});
|
||||
}
|
||||
|
||||
function highlightActiveLinkInSidebar() {
|
||||
const list = document.location.href.split('/');
|
||||
const targetURL = decodeURI(list[list.length - 1]);
|
||||
let element = document.querySelector(`.sidebar a[href*='${targetURL}']`);
|
||||
|
||||
if (!element) {
|
||||
try {
|
||||
element = document.querySelector(
|
||||
`.sidebar a[href*='${targetURL.split('#')[0]}']`
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!element) return;
|
||||
|
||||
element.parentElement.classList.add('active');
|
||||
element.scrollIntoView();
|
||||
}
|
||||
|
||||
function onDomContentLoaded() {
|
||||
var themeButton = document.querySelectorAll('.theme-toggle');
|
||||
|
||||
initMobileMenu();
|
||||
|
||||
if (themeButton) {
|
||||
themeButton.forEach(function (button) {
|
||||
button.addEventListener('click', toggleTheme);
|
||||
});
|
||||
}
|
||||
|
||||
// Highlighting code
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
hljs.addPlugin({
|
||||
'after:highlightElement': function (obj) {
|
||||
// Replace 'code' with result.language when
|
||||
// we are able to cross-check the correctness of
|
||||
// result.
|
||||
obj.el.parentNode.setAttribute('data-lang', 'code');
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-undef
|
||||
hljs.highlightAll();
|
||||
// eslint-disable-next-line no-undef
|
||||
hljs.initLineNumbersOnLoad({
|
||||
singleLine: true,
|
||||
});
|
||||
|
||||
// Highlight complete
|
||||
|
||||
initAccordion();
|
||||
addAnchor();
|
||||
processAllPre();
|
||||
hideTocOnSourcePage();
|
||||
setTimeout(function () {
|
||||
bringIdToViewOnMount();
|
||||
if (isSourcePage()) {
|
||||
highlightAndBringLineIntoView();
|
||||
}
|
||||
}, 1000);
|
||||
initTooltip();
|
||||
fixTable();
|
||||
addHrefToSidebarTitle();
|
||||
highlightActiveLinkInSidebar();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('DOMContentLoaded', onDomContentLoaded);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('hashchange', (event) => {
|
||||
const url = new URL(event.newURL);
|
||||
|
||||
if (url.hash !== '') {
|
||||
bringIdToViewOnMount(url.hash);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('storage', (event) => {
|
||||
if (event.newValue === 'undefined') return;
|
||||
|
||||
initTooltip();
|
||||
|
||||
if (event.key === themeLocalStorageKey) localUpdateTheme(event.newValue);
|
||||
if (event.key === fontSizeLocalStorageKey)
|
||||
localUpdateFontSize(event.newValue);
|
||||
});
|
||||
23
docs/scripts/core.min.js
vendored
Normal file
23
docs/scripts/core.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
90
docs/scripts/resize.js
Normal file
90
docs/scripts/resize.js
Normal file
@ -0,0 +1,90 @@
|
||||
/* global document */
|
||||
// This file is @deprecated
|
||||
|
||||
var NAVBAR_OPTIONS = {};
|
||||
|
||||
(function() {
|
||||
var NAVBAR_RESIZE_LOCAL_STORAGE_KEY = 'NAVBAR_RESIZE_LOCAL_STORAGE_KEY';
|
||||
|
||||
var navbar = document.querySelector('#navbar');
|
||||
var footer = document.querySelector('#footer');
|
||||
var mainSection = document.querySelector('#main');
|
||||
var localStorageResizeObject = JSON.parse(
|
||||
// eslint-disable-next-line no-undef
|
||||
localStorage.getItem(NAVBAR_RESIZE_LOCAL_STORAGE_KEY)
|
||||
);
|
||||
|
||||
/**
|
||||
* Check whether we have any resize value in local storage or not.
|
||||
* If we have resize value then resize the navbar.
|
||||
**/
|
||||
if (localStorageResizeObject) {
|
||||
navbar.style.width = localStorageResizeObject.width;
|
||||
mainSection.style.marginLeft = localStorageResizeObject.width;
|
||||
footer.style.marginLeft = localStorageResizeObject.width;
|
||||
}
|
||||
|
||||
var navbarSlider = document.querySelector('#navbar-resize');
|
||||
|
||||
function resizeNavbar(event) {
|
||||
var pageX = event.pageX,
|
||||
pageXPlusPx = event.pageX + 'px',
|
||||
min = Number.parseInt(NAVBAR_OPTIONS.min, 10) || 300,
|
||||
max = Number.parseInt(NAVBAR_OPTIONS.max, 10) || 600;
|
||||
|
||||
/**
|
||||
* Just to add some checks. If min is smaller than 10 then
|
||||
* user may accidentally end up reducing the size of navbar
|
||||
* less than 10. In that case user will not able to resize navbar
|
||||
* because navbar slider will be hidden.
|
||||
*/
|
||||
if (min < 10) {
|
||||
min = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only resize if pageX in range between min and max
|
||||
* allowed value.
|
||||
*/
|
||||
if (min < pageX && pageX < max) {
|
||||
navbar.style.width = pageXPlusPx;
|
||||
mainSection.style.marginLeft = pageXPlusPx;
|
||||
footer.style.marginLeft = pageXPlusPx;
|
||||
}
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('mousemove', resizeNavbar);
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('touchmove', resizeNavbar);
|
||||
}
|
||||
|
||||
function afterRemovingEventListeners() {
|
||||
// eslint-disable-next-line no-undef
|
||||
localStorage.setItem(
|
||||
NAVBAR_RESIZE_LOCAL_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
width: navbar.style.width
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function removeEventListeners() {
|
||||
// eslint-disable-next-line no-undef
|
||||
window.removeEventListener('mousemove', resizeNavbar);
|
||||
// eslint-disable-next-line no-undef
|
||||
window.removeEventListener('touchend', resizeNavbar);
|
||||
afterRemovingEventListeners();
|
||||
}
|
||||
|
||||
navbarSlider.addEventListener('mousedown', setupEventListeners);
|
||||
navbarSlider.addEventListener('touchstart', setupEventListeners);
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('mouseup', removeEventListeners);
|
||||
})();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function setupResizeOptions(options) {
|
||||
NAVBAR_OPTIONS = options;
|
||||
}
|
||||
265
docs/scripts/search.js
Normal file
265
docs/scripts/search.js
Normal file
@ -0,0 +1,265 @@
|
||||
/* global document */
|
||||
|
||||
const searchId = 'LiBfqbJVcV';
|
||||
const searchHash = '#' + searchId;
|
||||
const searchContainer = document.querySelector('#PkfLWpAbet');
|
||||
const searchWrapper = document.querySelector('#iCxFxjkHbP');
|
||||
const searchCloseButton = document.querySelector('#VjLlGakifb');
|
||||
const searchInput = document.querySelector('#vpcKVYIppa');
|
||||
const resultBox = document.querySelector('#fWwVHRuDuN');
|
||||
|
||||
function showResultText(text) {
|
||||
resultBox.innerHTML = `<span class="search-result-c-text">${text}</span>`;
|
||||
}
|
||||
|
||||
function hideSearch() {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (window.location.hash === searchHash) {
|
||||
// eslint-disable-next-line no-undef
|
||||
history.go(-1);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.onhashchange = null;
|
||||
|
||||
if (searchContainer) {
|
||||
searchContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function listenCloseKey(event) {
|
||||
if (event.key === 'Escape') {
|
||||
hideSearch();
|
||||
// eslint-disable-next-line no-undef
|
||||
window.removeEventListener('keyup', listenCloseKey);
|
||||
}
|
||||
}
|
||||
|
||||
function showSearch() {
|
||||
try {
|
||||
// Closing mobile menu before opening
|
||||
// search box.
|
||||
// It is defined in core.js
|
||||
// eslint-disable-next-line no-undef
|
||||
hideMobileMenu();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.onhashchange = hideSearch;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
if (window.location.hash !== searchHash) {
|
||||
// eslint-disable-next-line no-undef
|
||||
history.pushState(null, null, searchHash);
|
||||
}
|
||||
|
||||
if (searchContainer) {
|
||||
searchContainer.style.display = 'flex';
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('keyup', listenCloseKey);
|
||||
}
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAllData() {
|
||||
// eslint-disable-next-line no-undef
|
||||
const { hostname, protocol, port } = location;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const base = protocol + '//' + hostname + (port !== '' ? ':' + port : '') + baseURL;
|
||||
// eslint-disable-next-line no-undef
|
||||
const url = new URL('data/search.json', base);
|
||||
const result = await fetch(url);
|
||||
const { list } = await result.json();
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function onClickSearchItem(event) {
|
||||
const target = event.currentTarget;
|
||||
|
||||
if (target) {
|
||||
const href = target.getAttribute('href') || '';
|
||||
let elementId = href.split('#')[1] || '';
|
||||
let element = document.getElementById(elementId);
|
||||
|
||||
if (!element) {
|
||||
elementId = decodeURI(elementId);
|
||||
element = document.getElementById(elementId);
|
||||
}
|
||||
|
||||
if (element) {
|
||||
setTimeout(function() {
|
||||
// eslint-disable-next-line no-undef
|
||||
bringElementIntoView(element); // defined in core.js
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildSearchResult(result) {
|
||||
let output = '';
|
||||
const removeHTMLTagsRegExp = /(<([^>]+)>)/ig;
|
||||
|
||||
for (const res of result) {
|
||||
const { title = '', description = '' } = res.item;
|
||||
|
||||
const _link = res.item.link.replace('<a href="', '').replace(/">.*/, '');
|
||||
const _title = title.replace(removeHTMLTagsRegExp, "");
|
||||
const _description = description.replace(removeHTMLTagsRegExp, "");
|
||||
|
||||
output += `
|
||||
<a onclick="onClickSearchItem(event)" href="${_link}" class="search-result-item">
|
||||
<div class="search-result-item-title">${_title}</div>
|
||||
<div class="search-result-item-p">${_description || 'No description available.'}</div>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function getSearchResult(list, keys, searchKey) {
|
||||
const defaultOptions = {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: keys
|
||||
};
|
||||
|
||||
const options = { ...defaultOptions };
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const searchIndex = Fuse.createIndex(options.keys, list);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const fuse = new Fuse(list, options, searchIndex);
|
||||
|
||||
const result = fuse.search(searchKey);
|
||||
|
||||
if (result.length > 20) {
|
||||
return result.slice(0, 20);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
|
||||
return function() {
|
||||
const args = arguments;
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
// eslint-disable-next-line consistent-this, no-invalid-this
|
||||
func.apply(this, args);
|
||||
}
|
||||
}, wait);
|
||||
|
||||
if (immediate && !timeout) {
|
||||
// eslint-disable-next-line consistent-this, no-invalid-this
|
||||
func.apply(this, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let searchData;
|
||||
|
||||
async function search(event) {
|
||||
const value = event.target.value;
|
||||
const keys = ['title', 'description'];
|
||||
|
||||
if (!resultBox) {
|
||||
console.error('Search result container not found');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
showResultText('Type anything to view search result');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!searchData) {
|
||||
showResultText('Loading...');
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
searchData = await fetchAllData();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
showResultText('Failed to load result.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const result = getSearchResult(searchData, keys, value);
|
||||
|
||||
if (!result.length) {
|
||||
showResultText('No result found! Try some different combination.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
resultBox.innerHTML = buildSearchResult(result);
|
||||
}
|
||||
|
||||
function onDomContentLoaded() {
|
||||
const searchButton = document.querySelectorAll('.search-button');
|
||||
const debouncedSearch = debounce(search, 300);
|
||||
|
||||
if (searchCloseButton) {
|
||||
searchCloseButton.addEventListener('click', hideSearch);
|
||||
}
|
||||
|
||||
if (searchButton) {
|
||||
searchButton.forEach(function(item) {
|
||||
item.addEventListener('click', showSearch);
|
||||
});
|
||||
}
|
||||
|
||||
if (searchContainer) {
|
||||
searchContainer.addEventListener('click', hideSearch);
|
||||
}
|
||||
|
||||
if (searchWrapper) {
|
||||
searchWrapper.addEventListener('click', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('keyup', debouncedSearch);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
if (window.location.hash === searchHash) {
|
||||
showSearch();
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('DOMContentLoaded', onDomContentLoaded);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
window.addEventListener('hashchange', function() {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (window.location.hash === searchHash) {
|
||||
showSearch();
|
||||
}
|
||||
});
|
||||
6
docs/scripts/search.min.js
vendored
Normal file
6
docs/scripts/search.min.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
const searchId="LiBfqbJVcV",searchHash="#"+searchId,searchContainer=document.querySelector("#PkfLWpAbet"),searchWrapper=document.querySelector("#iCxFxjkHbP"),searchCloseButton=document.querySelector("#VjLlGakifb"),searchInput=document.querySelector("#vpcKVYIppa"),resultBox=document.querySelector("#fWwVHRuDuN");function showResultText(e){resultBox.innerHTML=`<span class="search-result-c-text">${e}</span>`}function hideSearch(){window.location.hash===searchHash&&history.go(-1),window.onhashchange=null,searchContainer&&(searchContainer.style.display="none")}function listenCloseKey(e){"Escape"===e.key&&(hideSearch(),window.removeEventListener("keyup",listenCloseKey))}function showSearch(){try{hideMobileMenu()}catch(e){console.error(e)}window.onhashchange=hideSearch,window.location.hash!==searchHash&&history.pushState(null,null,searchHash),searchContainer&&(searchContainer.style.display="flex",window.addEventListener("keyup",listenCloseKey)),searchInput&&searchInput.focus()}async function fetchAllData(){var{hostname:e,protocol:t,port:n}=location,t=t+"//"+e+(""!==n?":"+n:"")+baseURL,e=new URL("data/search.json",t);const a=await fetch(e);n=(await a.json()).list;return n}function onClickSearchItem(t){const n=t.currentTarget;if(n){const a=n.getAttribute("href")||"";t=a.split("#")[1]||"";let e=document.getElementById(t);e||(t=decodeURI(t),e=document.getElementById(t)),e&&setTimeout(function(){bringElementIntoView(e)},100)}}function buildSearchResult(e){let t="";var n=/(<([^>]+)>)/gi;for(const s of e){const{title:c="",description:i=""}=s.item;var a=s.item.link.replace('<a href="',"").replace(/">.*/,""),o=c.replace(n,""),r=i.replace(n,"");t+=`
|
||||
<a onclick="onClickSearchItem(event)" href="${a}" class="search-result-item">
|
||||
<div class="search-result-item-title">${o}</div>
|
||||
<div class="search-result-item-p">${r||"No description available."}</div>
|
||||
</a>
|
||||
`}return t}function getSearchResult(e,t,n){var t={...{shouldSort:!0,threshold:.4,location:0,distance:100,maxPatternLength:32,minMatchCharLength:1,keys:t}},a=Fuse.createIndex(t.keys,e);const o=new Fuse(e,t,a),r=o.search(n);return 20<r.length?r.slice(0,20):r}function debounce(t,n,a){let o;return function(){const e=arguments;clearTimeout(o),o=setTimeout(()=>{o=null,a||t.apply(this,e)},n),a&&!o&&t.apply(this,e)}}let searchData;async function search(e){e=e.target.value;if(resultBox)if(e){if(!searchData){showResultText("Loading...");try{searchData=await fetchAllData()}catch(e){return console.log(e),void showResultText("Failed to load result.")}}e=getSearchResult(searchData,["title","description"],e);e.length?resultBox.innerHTML=buildSearchResult(e):showResultText("No result found! Try some different combination.")}else showResultText("Type anything to view search result");else console.error("Search result container not found")}function onDomContentLoaded(){const e=document.querySelectorAll(".search-button");var t=debounce(search,300);searchCloseButton&&searchCloseButton.addEventListener("click",hideSearch),e&&e.forEach(function(e){e.addEventListener("click",showSearch)}),searchContainer&&searchContainer.addEventListener("click",hideSearch),searchWrapper&&searchWrapper.addEventListener("click",function(e){e.stopPropagation()}),searchInput&&searchInput.addEventListener("keyup",t),window.location.hash===searchHash&&showSearch()}window.addEventListener("DOMContentLoaded",onDomContentLoaded),window.addEventListener("hashchange",function(){window.location.hash===searchHash&&showSearch()});
|
||||
202
docs/scripts/third-party/Apache-License-2.0.txt
vendored
Normal file
202
docs/scripts/third-party/Apache-License-2.0.txt
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
9
docs/scripts/third-party/fuse.js
vendored
Normal file
9
docs/scripts/third-party/fuse.js
vendored
Normal file
File diff suppressed because one or more lines are too long
369
docs/scripts/third-party/hljs-line-num-original.js
vendored
Normal file
369
docs/scripts/third-party/hljs-line-num-original.js
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
// jshint multistr:true
|
||||
|
||||
(function (w, d) {
|
||||
'use strict';
|
||||
|
||||
var TABLE_NAME = 'hljs-ln',
|
||||
LINE_NAME = 'hljs-ln-line',
|
||||
CODE_BLOCK_NAME = 'hljs-ln-code',
|
||||
NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
|
||||
NUMBER_LINE_NAME = 'hljs-ln-n',
|
||||
DATA_ATTR_NAME = 'data-line-number',
|
||||
BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
|
||||
|
||||
if (w.hljs) {
|
||||
w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
|
||||
w.hljs.lineNumbersBlock = lineNumbersBlock;
|
||||
w.hljs.lineNumbersValue = lineNumbersValue;
|
||||
|
||||
addStyles();
|
||||
} else {
|
||||
w.console.error('highlight.js not detected!');
|
||||
}
|
||||
|
||||
function isHljsLnCodeDescendant(domElt) {
|
||||
var curElt = domElt;
|
||||
while (curElt) {
|
||||
if (curElt.className && curElt.className.indexOf('hljs-ln-code') !== -1) {
|
||||
return true;
|
||||
}
|
||||
curElt = curElt.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getHljsLnTable(hljsLnDomElt) {
|
||||
var curElt = hljsLnDomElt;
|
||||
while (curElt.nodeName !== 'TABLE') {
|
||||
curElt = curElt.parentNode;
|
||||
}
|
||||
return curElt;
|
||||
}
|
||||
|
||||
// Function to workaround a copy issue with Microsoft Edge.
|
||||
// Due to hljs-ln wrapping the lines of code inside a <table> element,
|
||||
// itself wrapped inside a <pre> element, window.getSelection().toString()
|
||||
// does not contain any line breaks. So we need to get them back using the
|
||||
// rendered code in the DOM as reference.
|
||||
function edgeGetSelectedCodeLines(selection) {
|
||||
// current selected text without line breaks
|
||||
var selectionText = selection.toString();
|
||||
|
||||
// get the <td> element wrapping the first line of selected code
|
||||
var tdAnchor = selection.anchorNode;
|
||||
while (tdAnchor.nodeName !== 'TD') {
|
||||
tdAnchor = tdAnchor.parentNode;
|
||||
}
|
||||
|
||||
// get the <td> element wrapping the last line of selected code
|
||||
var tdFocus = selection.focusNode;
|
||||
while (tdFocus.nodeName !== 'TD') {
|
||||
tdFocus = tdFocus.parentNode;
|
||||
}
|
||||
|
||||
// extract line numbers
|
||||
var firstLineNumber = parseInt(tdAnchor.dataset.lineNumber);
|
||||
var lastLineNumber = parseInt(tdFocus.dataset.lineNumber);
|
||||
|
||||
// multi-lines copied case
|
||||
if (firstLineNumber != lastLineNumber) {
|
||||
|
||||
var firstLineText = tdAnchor.textContent;
|
||||
var lastLineText = tdFocus.textContent;
|
||||
|
||||
// if the selection was made backward, swap values
|
||||
if (firstLineNumber > lastLineNumber) {
|
||||
var tmp = firstLineNumber;
|
||||
firstLineNumber = lastLineNumber;
|
||||
lastLineNumber = tmp;
|
||||
tmp = firstLineText;
|
||||
firstLineText = lastLineText;
|
||||
lastLineText = tmp;
|
||||
}
|
||||
|
||||
// discard not copied characters in first line
|
||||
while (selectionText.indexOf(firstLineText) !== 0) {
|
||||
firstLineText = firstLineText.slice(1);
|
||||
}
|
||||
|
||||
// discard not copied characters in last line
|
||||
while (selectionText.lastIndexOf(lastLineText) === -1) {
|
||||
lastLineText = lastLineText.slice(0, -1);
|
||||
}
|
||||
|
||||
// reconstruct and return the real copied text
|
||||
var selectedText = firstLineText;
|
||||
var hljsLnTable = getHljsLnTable(tdAnchor);
|
||||
for (var i = firstLineNumber + 1 ; i < lastLineNumber ; ++i) {
|
||||
var codeLineSel = format('.{0}[{1}="{2}"]', [CODE_BLOCK_NAME, DATA_ATTR_NAME, i]);
|
||||
var codeLineElt = hljsLnTable.querySelector(codeLineSel);
|
||||
selectedText += '\n' + codeLineElt.textContent;
|
||||
}
|
||||
selectedText += '\n' + lastLineText;
|
||||
return selectedText;
|
||||
// single copied line case
|
||||
} else {
|
||||
return selectionText;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure consistent code copy/paste behavior across all browsers
|
||||
// (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51)
|
||||
document.addEventListener('copy', function(e) {
|
||||
// get current selection
|
||||
var selection = window.getSelection();
|
||||
// override behavior when one wants to copy line of codes
|
||||
if (isHljsLnCodeDescendant(selection.anchorNode)) {
|
||||
var selectionText;
|
||||
// workaround an issue with Microsoft Edge as copied line breaks
|
||||
// are removed otherwise from the selection string
|
||||
if (window.navigator.userAgent.indexOf('Edge') !== -1) {
|
||||
selectionText = edgeGetSelectedCodeLines(selection);
|
||||
} else {
|
||||
// other browsers can directly use the selection string
|
||||
selectionText = selection.toString();
|
||||
}
|
||||
e.clipboardData.setData(
|
||||
'text/plain',
|
||||
selectionText
|
||||
.replace(/(^\t)/gm, '')
|
||||
);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
function addStyles () {
|
||||
var css = d.createElement('style');
|
||||
css.type = 'text/css';
|
||||
css.innerHTML = format(
|
||||
'.{0}{border-collapse:collapse}' +
|
||||
'.{0} td{padding:0}' +
|
||||
'.{1}:before{content:attr({2})}',
|
||||
[
|
||||
TABLE_NAME,
|
||||
NUMBER_LINE_NAME,
|
||||
DATA_ATTR_NAME
|
||||
]);
|
||||
d.getElementsByTagName('head')[0].appendChild(css);
|
||||
}
|
||||
|
||||
function initLineNumbersOnLoad (options) {
|
||||
if (d.readyState === 'interactive' || d.readyState === 'complete') {
|
||||
documentReady(options);
|
||||
} else {
|
||||
w.addEventListener('DOMContentLoaded', function () {
|
||||
documentReady(options);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function documentReady (options) {
|
||||
try {
|
||||
var blocks = d.querySelectorAll('code.hljs,code.nohighlight');
|
||||
|
||||
for (var i in blocks) {
|
||||
if (blocks.hasOwnProperty(i)) {
|
||||
if (!isPluginDisabledForBlock(blocks[i])) {
|
||||
lineNumbersBlock(blocks[i], options);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
w.console.error('LineNumbers error: ', e);
|
||||
}
|
||||
}
|
||||
|
||||
function isPluginDisabledForBlock(element) {
|
||||
return element.classList.contains('nohljsln');
|
||||
}
|
||||
|
||||
function lineNumbersBlock (element, options) {
|
||||
if (typeof element !== 'object') return;
|
||||
|
||||
async(function () {
|
||||
element.innerHTML = lineNumbersInternal(element, options);
|
||||
});
|
||||
}
|
||||
|
||||
function lineNumbersValue (value, options) {
|
||||
if (typeof value !== 'string') return;
|
||||
|
||||
var element = document.createElement('code')
|
||||
element.innerHTML = value
|
||||
|
||||
return lineNumbersInternal(element, options);
|
||||
}
|
||||
|
||||
function lineNumbersInternal (element, options) {
|
||||
|
||||
var internalOptions = mapOptions(element, options);
|
||||
|
||||
duplicateMultilineNodes(element);
|
||||
|
||||
return addLineNumbersBlockFor(element.innerHTML, internalOptions);
|
||||
}
|
||||
|
||||
function addLineNumbersBlockFor (inputHtml, options) {
|
||||
var lines = getLines(inputHtml);
|
||||
|
||||
// if last line contains only carriage return remove it
|
||||
if (lines[lines.length-1].trim() === '') {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
if (lines.length > 1 || options.singleLine) {
|
||||
var html = '';
|
||||
|
||||
for (var i = 0, l = lines.length; i < l; i++) {
|
||||
html += format(
|
||||
'<tr>' +
|
||||
'<td class="{0} {1}" {3}="{5}">' +
|
||||
'</td>' +
|
||||
'<td class="{0} {4}" {3}="{5}">' +
|
||||
'{6}' +
|
||||
'</td>' +
|
||||
'</tr>',
|
||||
[
|
||||
LINE_NAME,
|
||||
NUMBERS_BLOCK_NAME,
|
||||
NUMBER_LINE_NAME,
|
||||
DATA_ATTR_NAME,
|
||||
CODE_BLOCK_NAME,
|
||||
i + options.startFrom,
|
||||
lines[i].length > 0 ? lines[i] : ' '
|
||||
]);
|
||||
}
|
||||
|
||||
return format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]);
|
||||
}
|
||||
|
||||
return inputHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element Code block.
|
||||
* @param {Object} options External API options.
|
||||
* @returns {Object} Internal API options.
|
||||
*/
|
||||
function mapOptions (element, options) {
|
||||
options = options || {};
|
||||
return {
|
||||
singleLine: getSingleLineOption(options),
|
||||
startFrom: getStartFromOption(element, options)
|
||||
};
|
||||
}
|
||||
|
||||
function getSingleLineOption (options) {
|
||||
var defaultValue = false;
|
||||
if (!!options.singleLine) {
|
||||
return options.singleLine;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function getStartFromOption (element, options) {
|
||||
var defaultValue = 1;
|
||||
var startFrom = defaultValue;
|
||||
|
||||
if (isFinite(options.startFrom)) {
|
||||
startFrom = options.startFrom;
|
||||
}
|
||||
|
||||
// can be overridden because local option is priority
|
||||
var value = getAttribute(element, 'data-ln-start-from');
|
||||
if (value !== null) {
|
||||
startFrom = toNumber(value, defaultValue);
|
||||
}
|
||||
|
||||
return startFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method for fix multi-line elements implementation in highlight.js
|
||||
* Doing deep passage on child nodes.
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function duplicateMultilineNodes (element) {
|
||||
var nodes = element.childNodes;
|
||||
for (var node in nodes) {
|
||||
if (nodes.hasOwnProperty(node)) {
|
||||
var child = nodes[node];
|
||||
if (getLinesCount(child.textContent) > 0) {
|
||||
if (child.childNodes.length > 0) {
|
||||
duplicateMultilineNodes(child);
|
||||
} else {
|
||||
duplicateMultilineNode(child.parentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for fix multi-line elements implementation in highlight.js
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function duplicateMultilineNode (element) {
|
||||
var className = element.className;
|
||||
|
||||
if ( ! /hljs-/.test(className)) return;
|
||||
|
||||
var lines = getLines(element.innerHTML);
|
||||
|
||||
for (var i = 0, result = ''; i < lines.length; i++) {
|
||||
var lineText = lines[i].length > 0 ? lines[i] : ' ';
|
||||
result += format('<span class="{0}">{1}</span>\n', [ className, lineText ]);
|
||||
}
|
||||
|
||||
element.innerHTML = result.trim();
|
||||
}
|
||||
|
||||
function getLines (text) {
|
||||
if (text.length === 0) return [];
|
||||
return text.split(BREAK_LINE_REGEXP);
|
||||
}
|
||||
|
||||
function getLinesCount (text) {
|
||||
return (text.trim().match(BREAK_LINE_REGEXP) || []).length;
|
||||
}
|
||||
|
||||
///
|
||||
/// HELPERS
|
||||
///
|
||||
|
||||
function async (func) {
|
||||
w.setTimeout(func, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript}
|
||||
* @param {string} format
|
||||
* @param {array} args
|
||||
*/
|
||||
function format (format, args) {
|
||||
return format.replace(/\{(\d+)\}/g, function(m, n){
|
||||
return args[n] !== undefined ? args[n] : m;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element Code block.
|
||||
* @param {String} attrName Attribute name.
|
||||
* @returns {String} Attribute value or empty.
|
||||
*/
|
||||
function getAttribute (element, attrName) {
|
||||
return element.hasAttribute(attrName) ? element.getAttribute(attrName) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} str Source string.
|
||||
* @param {Number} fallback Fallback value.
|
||||
* @returns Parsed number or fallback value.
|
||||
*/
|
||||
function toNumber (str, fallback) {
|
||||
if (!str) return fallback;
|
||||
var number = Number(str);
|
||||
return isFinite(number) ? number : fallback;
|
||||
}
|
||||
|
||||
}(window, document));
|
||||
1
docs/scripts/third-party/hljs-line-num.js
vendored
Normal file
1
docs/scripts/third-party/hljs-line-num.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(r,o){"use strict";var e,l="hljs-ln",s="hljs-ln-line",f="hljs-ln-code",c="hljs-ln-numbers",u="hljs-ln-n",h="data-line-number",n=/\r\n|\r|\n/g;function t(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var e=parseInt(t.dataset.lineNumber),o=parseInt(r.dataset.lineNumber);if(e==o)return n;var a,i=t.textContent,l=r.textContent;for(o<e&&(a=e,e=o,o=a,a=i,i=l,l=a);0!==n.indexOf(i);)i=i.slice(1);for(;-1===n.lastIndexOf(l);)l=l.slice(0,-1);for(var s=i,c=function(e){for(var n=e;"TABLE"!==n.nodeName;)n=n.parentNode;return n}(t),u=e+1;u<o;++u){var d=v('.{0}[{1}="{2}"]',[f,h,u]);s+="\n"+c.querySelector(d).textContent}return s+="\n"+l}function a(e){try{var n,t=o.querySelectorAll("code.hljs,code.nohighlight");for(n in t)!t.hasOwnProperty(n)||t[n].classList.contains("nohljsln")||i(t[n],e)}catch(e){r.console.error("LineNumbers error: ",e)}}function i(e,n){"object"==typeof e&&r.setTimeout(function(){e.innerHTML=d(e,n)},0)}function d(e,n){var n={singleLine:function(e){return e.singleLine||!1}(n=(n=n)||{}),startFrom:function(e,n){var t=1;isFinite(n.startFrom)&&(t=n.startFrom);n=function(e,n){return e.hasAttribute(n)?e.getAttribute(n):null}(e,"data-ln-start-from");null!==n&&(t=function(e,n){if(!e)return n;e=Number(e);return isFinite(e)?e:n}(n,1));return t}(e,n)},e=(!function e(n){var t=n.childNodes;for(var r in t)!t.hasOwnProperty(r)||0<p((r=t[r]).textContent)&&(0<r.childNodes.length?e(r):m(r.parentNode))}(e),e.innerHTML),t=n,r=g(e);if(""===r[r.length-1].trim()&&r.pop(),1<r.length||t.singleLine){for(var o="",a=0,i=r.length;a<i;a++)o+=v('<tr><td class="{0} {1}" {3}="{5}"></td><td class="{0} {4}" {3}="{5}">{6}</td></tr>',[s,c,u,h,f,a+t.startFrom,0<r[a].length?r[a]:" "]);return v('<table class="{0}">{1}</table>',[l,o])}return e}function m(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r<t.length;r++)o+=v('<span class="{0}">{1}</span>\n',[n,0<t[r].length?t[r]:" "]);e.innerHTML=o.trim()}}function g(e){return 0===e.length?[]:e.split(n)}function p(e){return(e.trim().match(n)||[]).length}function v(e,t){return e.replace(/\{(\d+)\}/g,function(e,n){return void 0!==t[n]?t[n]:e})}r.hljs?(r.hljs.initLineNumbersOnLoad=function(e){"interactive"===o.readyState||"complete"===o.readyState?a(e):r.addEventListener("DOMContentLoaded",function(){a(e)})},r.hljs.lineNumbersBlock=i,r.hljs.lineNumbersValue=function(e,n){var t;if("string"==typeof e)return(t=document.createElement("code")).innerHTML=e,d(t,n)},(e=o.createElement("style")).type="text/css",e.innerHTML=v(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[l,u,h]),o.getElementsByTagName("head")[0].appendChild(e)):r.console.error("highlight.js not detected!"),document.addEventListener("copy",function(e){var n=window.getSelection();!function(e){for(var n=e;n;){if(n.className&&-1!==n.className.indexOf("hljs-ln-code"))return 1;n=n.parentNode}}(n.anchorNode)||(n=-1!==window.navigator.userAgent.indexOf("Edge")?t(n):n.toString(),e.clipboardData.setData("text/plain",n.replace(/(^\t)/gm,"")),e.preventDefault())})}(window,document);
|
||||
5171
docs/scripts/third-party/hljs-original.js
vendored
Normal file
5171
docs/scripts/third-party/hljs-original.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
docs/scripts/third-party/hljs.js
vendored
Normal file
1
docs/scripts/third-party/hljs.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
docs/scripts/third-party/popper.js
vendored
Normal file
5
docs/scripts/third-party/popper.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/scripts/third-party/tippy.js
vendored
Normal file
1
docs/scripts/third-party/tippy.js
vendored
Normal file
File diff suppressed because one or more lines are too long
672
docs/scripts/third-party/tocbot.js
vendored
Normal file
672
docs/scripts/third-party/tocbot.js
vendored
Normal file
@ -0,0 +1,672 @@
|
||||
/* eslint no-var: off */
|
||||
var defaultOptions = {
|
||||
ignoreSelector: '.js-toc-ignore',
|
||||
linkClass: 'toc-link',
|
||||
extraLinkClasses: '',
|
||||
activeLinkClass: 'is-active-link',
|
||||
listClass: 'toc-list',
|
||||
extraListClasses: '',
|
||||
isCollapsedClass: 'is-collapsed',
|
||||
collapsibleClass: 'is-collapsible',
|
||||
listItemClass: 'toc-list-item',
|
||||
activeListItemClass: 'is-active-li',
|
||||
collapseDepth: 0,
|
||||
scrollSmooth: true,
|
||||
scrollSmoothDuration: 420,
|
||||
scrollSmoothOffset: 0,
|
||||
scrollEndCallback: function (e) { },
|
||||
throttleTimeout: 50,
|
||||
positionFixedSelector: null,
|
||||
positionFixedClass: 'is-position-fixed',
|
||||
fixedSidebarOffset: 'auto',
|
||||
includeHtml: false,
|
||||
includeTitleTags: false,
|
||||
orderedList: true,
|
||||
scrollContainer: null,
|
||||
skipRendering: false,
|
||||
headingLabelCallback: false,
|
||||
ignoreHiddenElements: false,
|
||||
headingObjectCallback: null,
|
||||
basePath: '',
|
||||
disableTocScrollSync: false
|
||||
}
|
||||
|
||||
function ParseContent(options) {
|
||||
var reduce = [].reduce
|
||||
|
||||
/**
|
||||
* Get the last item in an array and return a reference to it.
|
||||
* @param {Array} array
|
||||
* @return {Object}
|
||||
*/
|
||||
function getLastItem(array) {
|
||||
return array[array.length - 1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get heading level for a heading dom node.
|
||||
* @param {HTMLElement} heading
|
||||
* @return {Number}
|
||||
*/
|
||||
function getHeadingLevel(heading) {
|
||||
return +heading.nodeName.toUpperCase().replace('H', '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get important properties from a heading element and store in a plain object.
|
||||
* @param {HTMLElement} heading
|
||||
* @return {Object}
|
||||
*/
|
||||
function getHeadingObject(heading) {
|
||||
// each node is processed twice by this method because nestHeadingsArray() and addNode() calls it
|
||||
// first time heading is real DOM node element, second time it is obj
|
||||
// that is causing problem so I am processing only original DOM node
|
||||
if (!(heading instanceof window.HTMLElement)) return heading
|
||||
|
||||
if (options.ignoreHiddenElements && (!heading.offsetHeight || !heading.offsetParent)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const headingLabel = heading.getAttribute('data-heading-label') ||
|
||||
(options.headingLabelCallback ? String(options.headingLabelCallback(heading.textContent)) : heading.textContent.trim())
|
||||
var obj = {
|
||||
id: heading.id,
|
||||
children: [],
|
||||
nodeName: heading.nodeName,
|
||||
headingLevel: getHeadingLevel(heading),
|
||||
textContent: headingLabel
|
||||
}
|
||||
|
||||
if (options.includeHtml) {
|
||||
obj.childNodes = heading.childNodes
|
||||
}
|
||||
|
||||
if (options.headingObjectCallback) {
|
||||
return options.headingObjectCallback(obj, heading)
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a node to the nested array.
|
||||
* @param {Object} node
|
||||
* @param {Array} nest
|
||||
* @return {Array}
|
||||
*/
|
||||
function addNode(node, nest) {
|
||||
var obj = getHeadingObject(node)
|
||||
var level = obj.headingLevel
|
||||
var array = nest
|
||||
var lastItem = getLastItem(array)
|
||||
var lastItemLevel = lastItem
|
||||
? lastItem.headingLevel
|
||||
: 0
|
||||
var counter = level - lastItemLevel
|
||||
|
||||
while (counter > 0) {
|
||||
lastItem = getLastItem(array)
|
||||
// Handle case where there are multiple h5+ in a row.
|
||||
if (lastItem && level === lastItem.headingLevel) {
|
||||
break
|
||||
} else if (lastItem && lastItem.children !== undefined) {
|
||||
array = lastItem.children
|
||||
}
|
||||
counter--
|
||||
}
|
||||
|
||||
if (level >= options.collapseDepth) {
|
||||
obj.isCollapsed = true
|
||||
}
|
||||
|
||||
array.push(obj)
|
||||
return array
|
||||
}
|
||||
|
||||
/**
|
||||
* Select headings in content area, exclude any selector in options.ignoreSelector
|
||||
* @param {HTMLElement} contentElement
|
||||
* @param {Array} headingSelector
|
||||
* @return {Array}
|
||||
*/
|
||||
function selectHeadings(contentElement, headingSelector) {
|
||||
var selectors = headingSelector
|
||||
if (options.ignoreSelector) {
|
||||
selectors = headingSelector.split(',')
|
||||
.map(function mapSelectors(selector) {
|
||||
return selector.trim() + ':not(' + options.ignoreSelector + ')'
|
||||
})
|
||||
}
|
||||
try {
|
||||
return contentElement.querySelectorAll(selectors)
|
||||
} catch (e) {
|
||||
console.warn('Headers not found with selector: ' + selectors); // eslint-disable-line
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest headings array into nested arrays with 'children' property.
|
||||
* @param {Array} headingsArray
|
||||
* @return {Object}
|
||||
*/
|
||||
function nestHeadingsArray(headingsArray) {
|
||||
return reduce.call(headingsArray, function reducer(prev, curr) {
|
||||
var currentHeading = getHeadingObject(curr)
|
||||
if (currentHeading) {
|
||||
addNode(currentHeading, prev.nest)
|
||||
}
|
||||
return prev
|
||||
}, {
|
||||
nest: []
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
nestHeadingsArray: nestHeadingsArray,
|
||||
selectHeadings: selectHeadings
|
||||
}
|
||||
}
|
||||
|
||||
function BuildHtml(options) {
|
||||
var forEach = [].forEach
|
||||
var some = [].some
|
||||
var body = document.body
|
||||
var tocElement
|
||||
var mainContainer = document.querySelector(options.contentSelector)
|
||||
var currentlyHighlighting = true
|
||||
var SPACE_CHAR = ' '
|
||||
|
||||
/**
|
||||
* Create link and list elements.
|
||||
* @param {Object} d
|
||||
* @param {HTMLElement} container
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function createEl(d, container) {
|
||||
var link = container.appendChild(createLink(d))
|
||||
if (d.children.length) {
|
||||
var list = createList(d.isCollapsed)
|
||||
d.children.forEach(function (child) {
|
||||
createEl(child, list)
|
||||
})
|
||||
link.appendChild(list)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render nested heading array data into a given element.
|
||||
* @param {HTMLElement} parent Optional. If provided updates the {@see tocElement} to match.
|
||||
* @param {Array} data
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function render(parent, data) {
|
||||
var collapsed = false
|
||||
var container = createList(collapsed)
|
||||
|
||||
data.forEach(function (d) {
|
||||
createEl(d, container)
|
||||
})
|
||||
|
||||
// Return if no TOC element is provided or known.
|
||||
tocElement = parent || tocElement
|
||||
if (tocElement === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove existing child if it exists.
|
||||
if (tocElement.firstChild) {
|
||||
tocElement.removeChild(tocElement.firstChild)
|
||||
}
|
||||
|
||||
// Just return the parent and don't append the list if no links are found.
|
||||
if (data.length === 0) {
|
||||
return tocElement
|
||||
}
|
||||
|
||||
// Append the Elements that have been created
|
||||
return tocElement.appendChild(container)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create link element.
|
||||
* @param {Object} data
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function createLink(data) {
|
||||
var item = document.createElement('li')
|
||||
var a = document.createElement('a')
|
||||
if (options.listItemClass) {
|
||||
item.setAttribute('class', options.listItemClass)
|
||||
}
|
||||
|
||||
if (options.onClick) {
|
||||
a.onclick = options.onClick
|
||||
}
|
||||
|
||||
if (options.includeTitleTags) {
|
||||
a.setAttribute('title', data.textContent)
|
||||
}
|
||||
|
||||
if (options.includeHtml && data.childNodes.length) {
|
||||
forEach.call(data.childNodes, function (node) {
|
||||
a.appendChild(node.cloneNode(true))
|
||||
})
|
||||
} else {
|
||||
// Default behavior.
|
||||
a.textContent = data.textContent
|
||||
}
|
||||
a.setAttribute('href', options.basePath + '#' + data.id)
|
||||
a.setAttribute('class', options.linkClass +
|
||||
SPACE_CHAR + 'node-name--' + data.nodeName +
|
||||
SPACE_CHAR + options.extraLinkClasses)
|
||||
item.appendChild(a)
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* Create list element.
|
||||
* @param {Boolean} isCollapsed
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function createList(isCollapsed) {
|
||||
var listElement = (options.orderedList) ? 'ol' : 'ul'
|
||||
var list = document.createElement(listElement)
|
||||
var classes = options.listClass +
|
||||
SPACE_CHAR + options.extraListClasses
|
||||
if (isCollapsed) {
|
||||
classes += SPACE_CHAR + options.collapsibleClass
|
||||
classes += SPACE_CHAR + options.isCollapsedClass
|
||||
}
|
||||
list.setAttribute('class', classes)
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* Update fixed sidebar class.
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function updateFixedSidebarClass() {
|
||||
if (options.scrollContainer && document.querySelector(options.scrollContainer)) {
|
||||
var top
|
||||
top = document.querySelector(options.scrollContainer).scrollTop
|
||||
} else {
|
||||
top = document.documentElement.scrollTop || body.scrollTop
|
||||
}
|
||||
var posFixedEl = document.querySelector(options.positionFixedSelector)
|
||||
|
||||
if (options.fixedSidebarOffset === 'auto') {
|
||||
options.fixedSidebarOffset = tocElement.offsetTop
|
||||
}
|
||||
|
||||
if (top > options.fixedSidebarOffset) {
|
||||
if (posFixedEl.className.indexOf(options.positionFixedClass) === -1) {
|
||||
posFixedEl.className += SPACE_CHAR + options.positionFixedClass
|
||||
}
|
||||
} else {
|
||||
posFixedEl.className = posFixedEl.className.split(SPACE_CHAR + options.positionFixedClass).join('')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get top position of heading
|
||||
* @param {HTMLElement} obj
|
||||
* @return {int} position
|
||||
*/
|
||||
function getHeadingTopPos(obj) {
|
||||
var position = 0
|
||||
if (obj !== null) {
|
||||
position = obj.offsetTop
|
||||
if (options.hasInnerContainers) { position += getHeadingTopPos(obj.offsetParent) }
|
||||
}
|
||||
return position
|
||||
}
|
||||
|
||||
|
||||
function updateListActiveElement(topHeader) {
|
||||
var forEach = [].forEach
|
||||
|
||||
var tocLinks = tocElement
|
||||
.querySelectorAll('.' + options.linkClass)
|
||||
forEach.call(tocLinks, function (tocLink) {
|
||||
tocLink.className = tocLink.className.split(SPACE_CHAR + options.activeLinkClass).join('')
|
||||
})
|
||||
var tocLis = tocElement
|
||||
.querySelectorAll('.' + options.listItemClass)
|
||||
forEach.call(tocLis, function (tocLi) {
|
||||
tocLi.className = tocLi.className.split(SPACE_CHAR + options.activeListItemClass).join('')
|
||||
})
|
||||
|
||||
// Add the active class to the active tocLink.
|
||||
var activeTocLink = tocElement
|
||||
.querySelector('.' + options.linkClass +
|
||||
'.node-name--' + topHeader.nodeName +
|
||||
'[href="' + options.basePath + '#' + topHeader.id.replace(/([ #;&,.+*~':"!^$[\]()=>|/@])/g, '\\$1') + '"]')
|
||||
if (activeTocLink && activeTocLink.className.indexOf(options.activeLinkClass) === -1) {
|
||||
activeTocLink.className += SPACE_CHAR + options.activeLinkClass
|
||||
}
|
||||
var li = activeTocLink && activeTocLink.parentNode
|
||||
if (li && li.className.indexOf(options.activeListItemClass) === -1) {
|
||||
li.className += SPACE_CHAR + options.activeListItemClass
|
||||
}
|
||||
|
||||
var tocLists = tocElement
|
||||
.querySelectorAll('.' + options.listClass + '.' + options.collapsibleClass)
|
||||
|
||||
// Collapse the other collapsible lists.
|
||||
forEach.call(tocLists, function (list) {
|
||||
if (list.className.indexOf(options.isCollapsedClass) === -1) {
|
||||
list.className += SPACE_CHAR + options.isCollapsedClass
|
||||
}
|
||||
})
|
||||
|
||||
// Expand the active link's collapsible list and its sibling if applicable.
|
||||
if (activeTocLink && activeTocLink.nextSibling && activeTocLink.nextSibling.className.indexOf(options.isCollapsedClass) !== -1) {
|
||||
activeTocLink.nextSibling.className = activeTocLink.nextSibling.className.split(SPACE_CHAR + options.isCollapsedClass).join('')
|
||||
}
|
||||
removeCollapsedFromParents(activeTocLink && activeTocLink.parentNode.parentNode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update TOC highlighting and collpased groupings.
|
||||
*/
|
||||
function updateToc(headingsArray) {
|
||||
// If a fixed content container was set
|
||||
if (options.scrollContainer && document.querySelector(options.scrollContainer)) {
|
||||
var top
|
||||
top = document.querySelector(options.scrollContainer).scrollTop
|
||||
} else {
|
||||
top = document.documentElement.scrollTop || body.scrollTop
|
||||
}
|
||||
|
||||
// Add fixed class at offset
|
||||
if (options.positionFixedSelector) {
|
||||
updateFixedSidebarClass()
|
||||
}
|
||||
|
||||
// Get the top most heading currently visible on the page so we know what to highlight.
|
||||
var headings = headingsArray
|
||||
var topHeader
|
||||
// Using some instead of each so that we can escape early.
|
||||
if (currentlyHighlighting &&
|
||||
tocElement !== null &&
|
||||
headings.length > 0) {
|
||||
some.call(headings, function (heading, i) {
|
||||
var modifiedTopOffset = top + 10
|
||||
if (mainContainer) {
|
||||
modifiedTopOffset += mainContainer.clientHeight * (mainContainer.scrollTop) / (mainContainer.scrollHeight - mainContainer.clientHeight)
|
||||
}
|
||||
if (getHeadingTopPos(heading) > modifiedTopOffset) {
|
||||
// Don't allow negative index value.
|
||||
var index = (i === 0) ? i : i - 1
|
||||
topHeader = headings[index]
|
||||
return true
|
||||
} else if (i === headings.length - 1) {
|
||||
// This allows scrolling for the last heading on the page.
|
||||
topHeader = headings[headings.length - 1]
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// Remove the active class from the other tocLinks.
|
||||
updateListActiveElement(topHeader)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove collpased class from parent elements.
|
||||
* @param {HTMLElement} element
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function removeCollapsedFromParents(element) {
|
||||
if (element && element.className.indexOf(options.collapsibleClass) !== -1 && element.className.indexOf(options.isCollapsedClass) !== -1) {
|
||||
element.className = element.className.split(SPACE_CHAR + options.isCollapsedClass).join('')
|
||||
return removeCollapsedFromParents(element.parentNode.parentNode)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable TOC Animation when a link is clicked.
|
||||
* @param {Event} event
|
||||
*/
|
||||
function disableTocAnimation(event) {
|
||||
var target = event.target || event.srcElement
|
||||
if (typeof target.className !== 'string' || target.className.indexOf(options.linkClass) === -1) {
|
||||
return
|
||||
}
|
||||
// Bind to tocLink clicks to temporarily disable highlighting
|
||||
// while smoothScroll is animating.
|
||||
currentlyHighlighting = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable TOC Animation.
|
||||
*/
|
||||
function enableTocAnimation() {
|
||||
currentlyHighlighting = true
|
||||
}
|
||||
|
||||
return {
|
||||
enableTocAnimation: enableTocAnimation,
|
||||
disableTocAnimation: disableTocAnimation,
|
||||
render: render,
|
||||
updateToc: updateToc,
|
||||
updateListActiveElement: updateListActiveElement
|
||||
}
|
||||
}
|
||||
|
||||
function updateTocScroll(options) {
|
||||
var toc = options.tocElement || document.querySelector(options.tocSelector)
|
||||
if (toc && toc.scrollHeight > toc.clientHeight) {
|
||||
var activeItem = toc.querySelector('.' + options.activeListItemClass)
|
||||
if (activeItem) {
|
||||
var topOffset = toc.getBoundingClientRect().top
|
||||
toc.scrollTop = activeItem.offsetTop - topOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([], factory(root))
|
||||
} else if (typeof exports === 'object') {
|
||||
module.exports = factory(root)
|
||||
} else {
|
||||
root.tocbot = factory(root)
|
||||
}
|
||||
})(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {
|
||||
'use strict'
|
||||
|
||||
var options = {}
|
||||
var tocbot = {}
|
||||
var buildHtml
|
||||
var parseContent
|
||||
|
||||
// Just return if its not a browser.
|
||||
var supports = !!root && !!root.document && !!root.document.querySelector && !!root.addEventListener // Feature test
|
||||
if (typeof window === 'undefined' && !supports) {
|
||||
return
|
||||
}
|
||||
var headingsArray
|
||||
|
||||
// From: https://github.com/Raynos/xtend
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
function extend() {
|
||||
var target = {}
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var source = arguments[i]
|
||||
for (var key in source) {
|
||||
if (hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
// From: https://remysharp.com/2010/07/21/throttling-function-calls
|
||||
function throttle(fn, threshhold, scope) {
|
||||
threshhold || (threshhold = 250)
|
||||
var last
|
||||
var deferTimer
|
||||
return function () {
|
||||
var context = scope || this
|
||||
var now = +new Date()
|
||||
var args = arguments
|
||||
if (last && now < last + threshhold) {
|
||||
// hold on to it
|
||||
clearTimeout(deferTimer)
|
||||
deferTimer = setTimeout(function () {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}, threshhold)
|
||||
} else {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getContentElement(options) {
|
||||
try {
|
||||
return options.contentElement || document.querySelector(options.contentSelector)
|
||||
} catch (e) {
|
||||
console.warn('Contents element not found: ' + options.contentSelector) // eslint-disable-line
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function getTocElement(options) {
|
||||
try {
|
||||
return options.tocElement || document.querySelector(options.tocSelector)
|
||||
} catch (e) {
|
||||
console.warn('TOC element not found: ' + options.tocSelector) // eslint-disable-line
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy tocbot.
|
||||
*/
|
||||
tocbot.destroy = function () {
|
||||
var tocElement = getTocElement(options)
|
||||
if (tocElement === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!options.skipRendering) {
|
||||
// Clear HTML.
|
||||
if (tocElement) {
|
||||
tocElement.innerHTML = ''
|
||||
}
|
||||
}
|
||||
|
||||
// Remove event listeners.
|
||||
if (options.scrollContainer && document.querySelector(options.scrollContainer)) {
|
||||
document.querySelector(options.scrollContainer).removeEventListener('scroll', this._scrollListener, false)
|
||||
document.querySelector(options.scrollContainer).removeEventListener('resize', this._scrollListener, false)
|
||||
} else {
|
||||
document.removeEventListener('scroll', this._scrollListener, false)
|
||||
document.removeEventListener('resize', this._scrollListener, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize tocbot.
|
||||
* @param {object} customOptions
|
||||
*/
|
||||
tocbot.init = function (customOptions) {
|
||||
// feature test
|
||||
if (!supports) {
|
||||
return
|
||||
}
|
||||
|
||||
// Merge defaults with user options.
|
||||
// Set to options variable at the top.
|
||||
options = extend(defaultOptions, customOptions || {})
|
||||
this.options = options
|
||||
this.state = {}
|
||||
|
||||
// Init smooth scroll if enabled (default).
|
||||
if (options.scrollSmooth) {
|
||||
options.duration = options.scrollSmoothDuration
|
||||
options.offset = options.scrollSmoothOffset
|
||||
}
|
||||
|
||||
// Pass options to these modules.
|
||||
buildHtml = BuildHtml(options)
|
||||
parseContent = ParseContent(options)
|
||||
|
||||
// For testing purposes.
|
||||
this._buildHtml = buildHtml
|
||||
this._parseContent = parseContent
|
||||
this._headingsArray = headingsArray
|
||||
this.updateTocListActiveElement = buildHtml.updateListActiveElement
|
||||
|
||||
// Destroy it if it exists first.
|
||||
tocbot.destroy()
|
||||
|
||||
var contentElement = getContentElement(options)
|
||||
if (contentElement === null) {
|
||||
return
|
||||
}
|
||||
|
||||
var tocElement = getTocElement(options)
|
||||
if (tocElement === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get headings array.
|
||||
headingsArray = parseContent.selectHeadings(contentElement, options.headingSelector)
|
||||
// Return if no headings are found.
|
||||
if (headingsArray === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Build nested headings array.
|
||||
var nestedHeadingsObj = parseContent.nestHeadingsArray(headingsArray)
|
||||
var nestedHeadings = nestedHeadingsObj.nest
|
||||
|
||||
// Render.
|
||||
if (!options.skipRendering) {
|
||||
buildHtml.render(tocElement, nestedHeadings)
|
||||
}
|
||||
|
||||
// Update Sidebar and bind listeners.
|
||||
this._scrollListener = throttle(function (e) {
|
||||
buildHtml.updateToc(headingsArray)
|
||||
!options.disableTocScrollSync && updateTocScroll(options)
|
||||
var isTop = e && e.target && e.target.scrollingElement && e.target.scrollingElement.scrollTop === 0
|
||||
if ((e && (e.eventPhase === 0 || e.currentTarget === null)) || isTop) {
|
||||
buildHtml.updateToc(headingsArray)
|
||||
if (options.scrollEndCallback) {
|
||||
options.scrollEndCallback(e)
|
||||
}
|
||||
}
|
||||
}, options.throttleTimeout)
|
||||
this._scrollListener()
|
||||
if (options.scrollContainer && document.querySelector(options.scrollContainer)) {
|
||||
document.querySelector(options.scrollContainer).addEventListener('scroll', this._scrollListener, false)
|
||||
document.querySelector(options.scrollContainer).addEventListener('resize', this._scrollListener, false)
|
||||
} else {
|
||||
document.addEventListener('scroll', this._scrollListener, false)
|
||||
document.addEventListener('resize', this._scrollListener, false)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh tocbot.
|
||||
*/
|
||||
tocbot.refresh = function (customOptions) {
|
||||
tocbot.destroy()
|
||||
tocbot.init(customOptions || this.options)
|
||||
}
|
||||
|
||||
// Make tocbot available globally.
|
||||
root.tocbot = tocbot
|
||||
|
||||
return tocbot
|
||||
})
|
||||
1
docs/scripts/third-party/tocbot.min.js
vendored
Normal file
1
docs/scripts/third-party/tocbot.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1159
docs/styles/clean-jsdoc-theme-base.css
Normal file
1159
docs/styles/clean-jsdoc-theme-base.css
Normal file
File diff suppressed because it is too large
Load Diff
412
docs/styles/clean-jsdoc-theme-dark.css
Normal file
412
docs/styles/clean-jsdoc-theme-dark.css
Normal file
@ -0,0 +1,412 @@
|
||||
::selection {
|
||||
background: #ffce76;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #1a1a1a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a,
|
||||
a:active {
|
||||
color: #0bf;
|
||||
}
|
||||
|
||||
hr {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-color: #222;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sidebar-section-title {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sidebar-section-title:hover {
|
||||
background: #252525;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.with-arrow {
|
||||
fill: #999;
|
||||
}
|
||||
|
||||
.sidebar-section-children-container {
|
||||
background: #292929;
|
||||
}
|
||||
|
||||
.sidebar-section-children.active {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.sidebar-section-children a:hover {
|
||||
background: #2c2c2c;
|
||||
}
|
||||
|
||||
.sidebar-section-children a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-container {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.icon-button svg,
|
||||
.navbar-item a {
|
||||
color: #999;
|
||||
fill: #999;
|
||||
}
|
||||
|
||||
.font-size-tooltip .icon-button svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.font-size-tooltip .icon-button.disabled {
|
||||
background: #999;
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.icon-button:active {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.navbar-item a:active {
|
||||
background-color: #222;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.navbar-item:hover {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: #222;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.toc-link {
|
||||
color: #777;
|
||||
font-size: 0.875rem;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.toc-link.is-active-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.has-anchor .link-anchor {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.has-anchor .link-anchor:hover {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
tt,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.signature-attributes {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.ancestors {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.ancestors a {
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
.important {
|
||||
color: #c51313;
|
||||
}
|
||||
|
||||
.type-signature {
|
||||
color: #00918e;
|
||||
}
|
||||
|
||||
.name,
|
||||
.name a {
|
||||
color: #f7f7f7;
|
||||
}
|
||||
|
||||
.details {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.prettyprint {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.member-item-container strong,
|
||||
.method-member-container strong {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pre-top-bar-container {
|
||||
background: #292929;
|
||||
}
|
||||
|
||||
.prettyprint.source,
|
||||
.prettyprint code {
|
||||
background-color: #222;
|
||||
color: #c9d1d9;
|
||||
}
|
||||
|
||||
.pre-div {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.hljs .hljs-ln-numbers {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.hljs .selected {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.hljs .selected .hljs-ln-numbers {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
|
||||
table .name,
|
||||
.params .name,
|
||||
.props .name,
|
||||
.name code {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
table td,
|
||||
.params td {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
table thead th,
|
||||
.params thead th,
|
||||
.props thead th {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* stylelint-disable */
|
||||
table .params thead tr,
|
||||
.params .params thead tr,
|
||||
.props .props thead tr {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
.code-lang-name {
|
||||
color: #ff8a00;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #ffce76;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
/* code */
|
||||
.hljs-comment {
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-template-tag,
|
||||
.hljs-variable.language_ {
|
||||
color: #ff7b72;
|
||||
}
|
||||
|
||||
.hljs-template-variable,
|
||||
.hljs-type {
|
||||
color: #30ac7c;
|
||||
}
|
||||
|
||||
.hljs-meta,
|
||||
.hljs-string,
|
||||
.hljs-regexp {
|
||||
color: #a5d6ff;
|
||||
}
|
||||
|
||||
.hljs-title.class_,
|
||||
.hljs-title {
|
||||
color: #ffa657;
|
||||
}
|
||||
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-title.function_ {
|
||||
color: #d2a8ff;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-attribute,
|
||||
.hljs-literal,
|
||||
.hljs-meta,
|
||||
.hljs-number,
|
||||
.hljs-operator,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-id,
|
||||
.hljs-variable {
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.hljs-meta .hljs-string,
|
||||
.hljs-regexp,
|
||||
.hljs-string {
|
||||
color: #a5d6ff;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-symbol {
|
||||
color: #ffa657;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-comment,
|
||||
.hljs-formula {
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.hljs-name,
|
||||
.hljs-quote,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-selector-tag {
|
||||
color: #7ee787;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
color: #c9d1d9;
|
||||
}
|
||||
|
||||
.hljs-section {
|
||||
color: #1f6feb;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hljs-bullet {
|
||||
color: #f2cc60;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
color: #c9d1d9;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
color: #c9d1d9;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* code end*/
|
||||
|
||||
blockquote {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.icon-button.search-close-button svg {
|
||||
fill: #a00;
|
||||
}
|
||||
|
||||
.search-container .wrapper {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.search-result-c {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.search-box-c {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-box-c svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.search-result-item:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.search-result-item:active {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.search-result-item-title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-result-item-p {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.mobile-menu-icon-container .icon-button {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.mobile-sidebar-container {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.mobile-sidebar-wrapper {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
|
||||
.child-tutorial {
|
||||
border-color: #555;
|
||||
color: #f3f3f3;
|
||||
}
|
||||
|
||||
.child-tutorial:hover {
|
||||
background: #222;
|
||||
}
|
||||
482
docs/styles/clean-jsdoc-theme-light.css
Normal file
482
docs/styles/clean-jsdoc-theme-light.css
Normal file
@ -0,0 +1,482 @@
|
||||
.light ::selection {
|
||||
background: #ffce76;
|
||||
color: #1d1919;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-no-qualifying-type,rule-empty-line-before */
|
||||
body.light {
|
||||
background-color: #fff;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light a,
|
||||
.light a:active {
|
||||
color: #007bff;
|
||||
|
||||
}
|
||||
|
||||
.light hr {
|
||||
color: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light h1,
|
||||
.light h2,
|
||||
.light h3,
|
||||
.light h4,
|
||||
.light h5,
|
||||
.light h6 {
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar {
|
||||
background-color: #f7f7f7;
|
||||
color: #222;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar-title {
|
||||
color: #222;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar-section-title {
|
||||
color: #222;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar-section-title:hover,
|
||||
.light .sidebar-section-title.active {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
|
||||
.light .with-arrow {
|
||||
fill: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar-section-children-container {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.light .sidebar-section-children.active {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.light .sidebar-section-children a:hover {
|
||||
background: #e0e0e0;
|
||||
|
||||
}
|
||||
|
||||
.light .sidebar-section-children a {
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .navbar-container {
|
||||
background: #fff;
|
||||
|
||||
}
|
||||
|
||||
.light .icon-button svg,
|
||||
.light .navbar-item a {
|
||||
color: #222;
|
||||
fill: #222;
|
||||
|
||||
}
|
||||
|
||||
.light .tippy-box {
|
||||
background: #eee;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .tippy-arrow {
|
||||
color: #f1f1f1;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-max-compound-selectors,rule-empty-line-before */
|
||||
.light .font-size-tooltip .icon-button svg {
|
||||
fill: #111;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-max-compound-selectors, rule-empty-line-before */
|
||||
.light .font-size-tooltip .icon-button.disabled svg {
|
||||
fill: #999;
|
||||
|
||||
}
|
||||
|
||||
.light .icon-button:hover {
|
||||
background: #ddd;
|
||||
|
||||
}
|
||||
|
||||
.light .icon-button:active {
|
||||
background: #ccc;
|
||||
|
||||
}
|
||||
|
||||
.light .navbar-item a:active {
|
||||
background-color: #eee;
|
||||
color: #333;
|
||||
|
||||
}
|
||||
|
||||
.light .navbar-item:hover {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .footer {
|
||||
background: #f7f7f7;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .footer a {
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .toc-link {
|
||||
color: #999;
|
||||
font-size: 0.875rem;
|
||||
transition: color 0.3s;
|
||||
|
||||
}
|
||||
|
||||
.light .toc-link.is-active-link {
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .has-anchor .link-anchor {
|
||||
color: #ddd;
|
||||
|
||||
}
|
||||
|
||||
.light .has-anchor .link-anchor:hover {
|
||||
color: #ccc;
|
||||
|
||||
}
|
||||
|
||||
.light .signature-attributes {
|
||||
color: #aaa;
|
||||
|
||||
}
|
||||
|
||||
.light .ancestors {
|
||||
color: #999;
|
||||
|
||||
}
|
||||
|
||||
.light .ancestors a {
|
||||
color: #999 !important;
|
||||
|
||||
}
|
||||
|
||||
.light .important {
|
||||
color: #ee1313;
|
||||
|
||||
}
|
||||
|
||||
.light .type-signature {
|
||||
color: #00918e;
|
||||
|
||||
}
|
||||
|
||||
.light .name,
|
||||
.light .name a {
|
||||
color: #293a80;
|
||||
|
||||
}
|
||||
|
||||
.light .details {
|
||||
background: #f9f9f9;
|
||||
color: #101010;
|
||||
|
||||
}
|
||||
|
||||
.light .member-item-container strong,
|
||||
.light .method-member-container strong {
|
||||
color: #000;
|
||||
|
||||
}
|
||||
|
||||
.light .prettyprint {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .pre-div {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs .hljs-ln-numbers {
|
||||
color: #aaa;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs .selected {
|
||||
background: #ccc;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-no-qualifying-type,rule-empty-line-before */
|
||||
.light table.hljs-ln td {
|
||||
background: none;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-max-compound-selectors,rule-empty-line-before */
|
||||
.light .hljs .selected .hljs-ln-numbers {
|
||||
color: #444;
|
||||
|
||||
}
|
||||
|
||||
.light .pre-top-bar-container {
|
||||
background-color: #eee;
|
||||
|
||||
}
|
||||
|
||||
.light .prettyprint code {
|
||||
background-color: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light table .name,
|
||||
.light .params .name,
|
||||
.light .props .name,
|
||||
.light .name code {
|
||||
color: #4d4e53;
|
||||
|
||||
}
|
||||
|
||||
.light table td,
|
||||
.light .params td {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-max-compound-selectors,rule-empty-line-before */
|
||||
.light table thead th,
|
||||
.light .params thead th,
|
||||
.light .props thead th {
|
||||
background-color: #eee;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
/* stylelint-disable */
|
||||
.light table .params thead tr,
|
||||
.light .params .params thead tr,
|
||||
.light .props .props thead tr {
|
||||
background-color: #eee;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .disabled {
|
||||
color: #454545;
|
||||
|
||||
}
|
||||
|
||||
.light .code-lang-name {
|
||||
color: #ff0000;
|
||||
|
||||
}
|
||||
|
||||
.light .tooltip {
|
||||
background: #ffce76;
|
||||
color: #000;
|
||||
|
||||
}
|
||||
|
||||
/* Code */
|
||||
|
||||
.light .hljs-comment,
|
||||
.light .hljs-quote {
|
||||
color: #a0a1a7;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-doctag,
|
||||
.light .hljs-keyword,
|
||||
.light .hljs-formula {
|
||||
color: #a626a4;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-section,
|
||||
.light .hljs-name,
|
||||
.light .hljs-selector-tag,
|
||||
.light .hljs-deletion,
|
||||
.light .hljs-subst {
|
||||
color: #e45649;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-literal {
|
||||
color: #0184bb;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-string,
|
||||
.light .hljs-regexp,
|
||||
.light .hljs-addition,
|
||||
.light .hljs-attribute,
|
||||
.light .hljs-meta .hljs-string {
|
||||
color: #50a14f;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-attr,
|
||||
.light .hljs-variable,
|
||||
.light .hljs-template-variable,
|
||||
.light .hljs-type,
|
||||
.light .hljs-selector-class,
|
||||
.light .hljs-selector-attr,
|
||||
.light .hljs-selector-pseudo,
|
||||
.light .hljs-number {
|
||||
color: #986801;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-symbol,
|
||||
.light .hljs-bullet,
|
||||
.light .hljs-link,
|
||||
.light .hljs-meta,
|
||||
.light .hljs-selector-id,
|
||||
.light .hljs-title {
|
||||
color: #4078f2;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-built_in,
|
||||
.light .hljs-title.class_,
|
||||
.light .hljs-class .hljs-title {
|
||||
color: #c18401;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-emphasis {
|
||||
font-style: italic;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-strong {
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
.light .hljs-link {
|
||||
text-decoration: underline;
|
||||
|
||||
}
|
||||
|
||||
/* Code Ends */
|
||||
|
||||
.light blockquote {
|
||||
background: #eee;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light code {
|
||||
background: #ddd;
|
||||
color: #000;
|
||||
|
||||
}
|
||||
|
||||
.light .search-container {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
|
||||
}
|
||||
|
||||
.light .search-close-button svg {
|
||||
fill: #f00;
|
||||
|
||||
}
|
||||
|
||||
.light .search-container .wrapper {
|
||||
background: #eee;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-c {
|
||||
color: #aaa;
|
||||
|
||||
}
|
||||
|
||||
.light .search-box-c svg {
|
||||
fill: #333;
|
||||
|
||||
}
|
||||
|
||||
.light .search-input {
|
||||
background: #f7f7f7;
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-item {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-item:hover {
|
||||
background: #e9e9e9;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-item:active {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-item-title {
|
||||
color: #111;
|
||||
|
||||
}
|
||||
|
||||
.light .search-result-item-p {
|
||||
color: #aaa;
|
||||
|
||||
}
|
||||
|
||||
.light .mobile-menu-icon-container .icon-button {
|
||||
background: #e5e5e5;
|
||||
|
||||
}
|
||||
|
||||
.light .mobile-sidebar-container {
|
||||
background: #fff;
|
||||
|
||||
}
|
||||
|
||||
.light .mobile-sidebar-wrapper {
|
||||
background: #f7f7f7;
|
||||
|
||||
}
|
||||
|
||||
.light .child-tutorial {
|
||||
border-color: #aaa;
|
||||
color: #222;
|
||||
|
||||
}
|
||||
|
||||
.light .child-tutorial:hover {
|
||||
background: #ccc;
|
||||
|
||||
}
|
||||
|
||||
30
docs/styles/clean-jsdoc-theme-scrollbar.css
Normal file
30
docs/styles/clean-jsdoc-theme-scrollbar.css
Normal file
@ -0,0 +1,30 @@
|
||||
::-webkit-scrollbar {
|
||||
height: 0.3125rem;
|
||||
width: 0.3125rem;
|
||||
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb,
|
||||
::-webkit-scrollbar-track {
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #555;
|
||||
outline: 0.06125rem solid #555;
|
||||
}
|
||||
|
||||
|
||||
.light ::-webkit-scrollbar-track {
|
||||
background: #ddd;
|
||||
|
||||
}
|
||||
|
||||
.light ::-webkit-scrollbar-thumb {
|
||||
background: #aaa;
|
||||
outline: 0.06125rem solid #aaa;
|
||||
}
|
||||
1
docs/styles/clean-jsdoc-theme-without-scrollbar.min.css
vendored
Normal file
1
docs/styles/clean-jsdoc-theme-without-scrollbar.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/styles/clean-jsdoc-theme.min.css
vendored
Normal file
1
docs/styles/clean-jsdoc-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
185
index.js
185
index.js
@ -5,59 +5,92 @@ const Delete = require("./lib/Delete");
|
||||
const Update = require("./lib/Update");
|
||||
const { CreateTable, Structure, AlterTable } = require("./lib/Tables");
|
||||
|
||||
class awSQL {
|
||||
#instances = {};
|
||||
#default;
|
||||
/**
|
||||
* @typedef {Object} InstanceOptions
|
||||
* @property {String} [charset] - Charset to use
|
||||
* @property {String} [defaultDatabase] - The default database
|
||||
* @property {Boolean} [multipleStatements] - Whether multiple statements should be allowed in a single query
|
||||
* @property {Boolean} [insecureAuth] - Whether insecure authentication methods should be allowed
|
||||
* @property {String} [customIdentifier] - Sets a custom identifier for this instance
|
||||
* @property {Boolean} [isDefault] - Whether this instance is returned by default via 'getInstance'
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} hostname
|
||||
* @param {*} username
|
||||
* @param {*} password
|
||||
* @param {*} options
|
||||
* Exported as instance
|
||||
*/
|
||||
class awSQL {
|
||||
#instances = {}; // Holds instances. Key is identifier/name.
|
||||
#default; // Holds the identifier of the default instance (the one used if identifier is omitted)
|
||||
|
||||
/**
|
||||
* Creates a new instance (database connection)
|
||||
* @param {String} hostname - Hostname where the database is located
|
||||
* @param {String} username - Username to login with
|
||||
* @param {String} password
|
||||
* @param {InstanceOptions} [options]
|
||||
* @returns {Instance}
|
||||
*/
|
||||
createInstance = (hostname="localhost", username, password, options = {
|
||||
createInstance(hostname="localhost", username, password, options = {
|
||||
charset:"utf8mb4",
|
||||
defaultDatabase: false,
|
||||
multipleStatements: false,
|
||||
insecureAuth: false,
|
||||
customIdentifier: false,
|
||||
isDefault: false,
|
||||
})=>{
|
||||
const identifier = options.customIdentifier||`${username}@${hostname}`;
|
||||
}){
|
||||
const identifier = options.customIdentifier||`${username}@${hostname}`; // Set identifier to given identifier or [username]@[hostname]
|
||||
// If an instance with that identifier exists, throw error
|
||||
if (this.#instances[identifier]) throw new Error(`Can't create new instance with identifier "${identifier}": An instance with the same name already exists`);
|
||||
const instance = new Instance(hostname, username, password, options.charset, options.defaultDatabase, options.multipleStatements, options.insecureAuth);
|
||||
this.#instances[identifier] = instance;
|
||||
if (options.createAsDefault) this.#default = identifier;
|
||||
this.#instances[identifier] = instance; // Store instance
|
||||
if (options.createAsDefault) this.#default = identifier; // If this instance was created with default option set it as the default instance
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance matching the given identifier
|
||||
*
|
||||
* @param {String} identifier
|
||||
* Returns default (or first) instance if no identifier is given
|
||||
* @param {String} [identifier] - Identifier of the instance to get
|
||||
* @returns {Instance}
|
||||
*/
|
||||
getInstance = (identifier) => {
|
||||
if (Object.keys(this.#instances).length===0) return undefined;
|
||||
if (!identifier) return this.#default?this.#instances[this.#default]:this.#instances[Object.keys(this.#instances)[0]]; // If no identifier is set return default or first instance
|
||||
return this.#instances[identifier];
|
||||
getInstance(identifier) {
|
||||
if (Object.keys(this.#instances).length===0) return undefined; // If no instance is found at all return -> Safety return
|
||||
// If no identifier is set return default or first instance
|
||||
if (!identifier) return
|
||||
this.#default?
|
||||
this.#instances[this.#default] // If default exists get that
|
||||
:
|
||||
this.#instances[Object.keys(this.#instances)[0]]; // Otherwise get instance with that identifier
|
||||
return this.#instances[identifier]; // If identifier given return that instance
|
||||
}
|
||||
|
||||
listInstances = () => {
|
||||
/**
|
||||
* Returns a list of the identifiers of all instances
|
||||
* @returns {Array<String>}
|
||||
*/
|
||||
listInstances(){
|
||||
return Object.keys(this.#instances);
|
||||
}
|
||||
|
||||
deleteInstance = (identifier) => {
|
||||
/**
|
||||
* Deletes an instance (and closes any open connection)
|
||||
* @param {String} identifier - Identifier of the instance to delete
|
||||
* @returns {true}
|
||||
*/
|
||||
deleteInstance(identifier){
|
||||
if (!identifier) throw new Error("Can't delete Instance: No identifier set");
|
||||
if (!this.#instances[identifier]) throw new Error(`Can't delete Instance '${identifier}': No Instance`);
|
||||
this.#instances[identifier].destroy();
|
||||
if (this.#default === identifier) this.#default = undefined;
|
||||
this.#instances[identifier].destroy(); // Memory: Close connection
|
||||
if (this.#default === identifier) this.#default = undefined; // If this instance was default, clear it from default
|
||||
delete this.#instances[identifier];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An awSQL-Instance
|
||||
*/
|
||||
class Instance {
|
||||
#user;
|
||||
#password;
|
||||
@ -68,7 +101,6 @@ class Instance {
|
||||
#connection;
|
||||
|
||||
#selectedDatabase;
|
||||
|
||||
constructor(hostname="localhost", username, password, charset="utf8mb4", defaultDatabase=false, multipleStatements=false, insecureAuth=false){
|
||||
this.#host = hostname;
|
||||
this.#user = username;
|
||||
@ -81,11 +113,11 @@ class Instance {
|
||||
|
||||
/**
|
||||
* Connects the instance
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
connect = () => {
|
||||
connect(){
|
||||
return new Promise((resolve, reject) => {
|
||||
const connection = mysql.createConnection({
|
||||
const connection = mysql.createConnection({ // Create a new mysql-connection
|
||||
user: this.#user,
|
||||
password: this.#password,
|
||||
host: this.#host,
|
||||
@ -93,18 +125,26 @@ class Instance {
|
||||
multipleStatements: this.#multipleStatements,
|
||||
charset: this.#charset
|
||||
});
|
||||
this.#connection = connection;
|
||||
this.#connection = connection; // Store the connection
|
||||
connection.connect((err) =>{
|
||||
if (err) throw err;
|
||||
resolve(`Connected to ${this.#host} with user ${this.#user}`);
|
||||
});
|
||||
connection.on("error", (err) => {
|
||||
if (!err.fatal){
|
||||
return;
|
||||
}
|
||||
this.destroy(); // Memory: Destroy if connection errored
|
||||
this.connect(); // And try to reconnect
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the instance
|
||||
* @returns {true}
|
||||
*/
|
||||
destroy = () => {
|
||||
destroy(){
|
||||
if (this.#connection) this.#connection.end();
|
||||
this.#connection = undefined;
|
||||
return true;
|
||||
@ -113,10 +153,10 @@ class Instance {
|
||||
/**
|
||||
* Performs a raw query
|
||||
* @param {String} queryString The sql query string to perform.
|
||||
* @param {Array} values - An array holding all replacable ?-values from left to right.
|
||||
* @param {Array<Any>} values - An array holding all replacable ?-values from left to right.
|
||||
* @returns {Any} - The individual result of your query
|
||||
*/
|
||||
queryRaw = (queryString, values) => {
|
||||
queryRaw(queryString, values){
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.#connection) throw new Error("Querying failed: No connection");
|
||||
this.#connection.query(queryString, values, (err, result) => {
|
||||
@ -129,9 +169,9 @@ class Instance {
|
||||
/**
|
||||
* Returns a list of database names the user has access to
|
||||
* @param {Boolean} excludeSchema - Whether to exclude the default database 'information_schema'
|
||||
* @returns {Array}
|
||||
* @returns {Array<String>}
|
||||
*/
|
||||
getDatabases = async (excludeSchema=false) => {
|
||||
async getDatabases (excludeSchema=false){
|
||||
let dbs = await this.queryRaw("SHOW DATABASES;");
|
||||
if (excludeSchema) dbs = dbs.filter((db)=>db.Database!=="information_schema")
|
||||
return dbs.map(db => db.Database);
|
||||
@ -142,7 +182,7 @@ class Instance {
|
||||
* @param {String} name - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (name) => {
|
||||
selectDatabase(name){
|
||||
this.#selectedDatabase = name;
|
||||
return this;
|
||||
}
|
||||
@ -153,7 +193,7 @@ class Instance {
|
||||
* @param {String} [database] - Database to select. Can be empty as long as a default database was set with 'selectDatabase'
|
||||
* @returns {Array}
|
||||
*/
|
||||
getTables = async (database) => {
|
||||
async getTables(database){
|
||||
if (!this.#multipleStatements) throw new Error("getTables: multipleStatements must be set to 'true' in instance options");
|
||||
if (!this.#selectedDatabase && !database) throw new Error("getTables: No database selected");
|
||||
const tables = (await this.queryRaw(`USE ${database||this.#selectedDatabase}; SHOW TABLES;`))[1];
|
||||
@ -166,7 +206,7 @@ class Instance {
|
||||
* @param {...String} [columns] - Name of columns to select. Leave empty to select all
|
||||
* @returns {Select}
|
||||
*/
|
||||
select = (from, ...columns) => {
|
||||
select(from, ...columns){
|
||||
return new Select(this, this.#selectedDatabase, from, columns);
|
||||
}
|
||||
|
||||
@ -176,7 +216,7 @@ class Instance {
|
||||
* @param {String} into - Name of the table to insert into
|
||||
* @returns {Insert}
|
||||
*/
|
||||
insert = (into) => {
|
||||
insert(into){
|
||||
return new Insert(this, this.#selectedDatabase, into);
|
||||
}
|
||||
|
||||
@ -185,7 +225,7 @@ class Instance {
|
||||
* @param {String} from - Name of the table to delete from
|
||||
* @returns {Delete}
|
||||
*/
|
||||
delete = (from) => {
|
||||
delete(from){
|
||||
return new Delete(this, this.#selectedDatabase, from);
|
||||
}
|
||||
|
||||
@ -194,7 +234,7 @@ class Instance {
|
||||
* @param {String} table - Name of the table to update data in
|
||||
* @returns {Update}
|
||||
*/
|
||||
update = (table) => {
|
||||
update(table){
|
||||
return new Update(this, this.#selectedDatabase, table);
|
||||
}
|
||||
|
||||
@ -202,17 +242,18 @@ class Instance {
|
||||
* Drops a whole database
|
||||
* - Requires admin privileges
|
||||
* @param {String} database - Name of the database to drop
|
||||
* @returns
|
||||
* @returns {Any}
|
||||
*/
|
||||
dropDatabase = async (database) => {
|
||||
async dropDatabase (database){
|
||||
return await this.queryRaw(`DROP DATABASE ${database};`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a whole table
|
||||
* @param {String} table - Name of the table to drop
|
||||
* @returns {Any}
|
||||
*/
|
||||
dropTable = async (table) => {
|
||||
async dropTable(table){
|
||||
if (!this.#selectedDatabase) throw new Error(`Can't drop table '${table}': Database not set`);
|
||||
return await this.queryRaw(`DROP TABLE ${this.#selectedDatabase}.${table}`);
|
||||
}
|
||||
@ -221,9 +262,9 @@ class Instance {
|
||||
* Creates a new database
|
||||
* - Requires admin privileges
|
||||
* @param {String} name - Name of the database to create
|
||||
* @returns
|
||||
* @returns {Any}
|
||||
*/
|
||||
createDatabase = async (name) => {
|
||||
async createDatabase(name){
|
||||
return await this.queryRaw(`CREATE DATABASE ${name};`);
|
||||
}
|
||||
|
||||
@ -232,16 +273,17 @@ class Instance {
|
||||
* @param {String} name - Name of the table to create
|
||||
* @returns {CreateTable}
|
||||
*/
|
||||
createTable = (name) => {
|
||||
createTable(name){
|
||||
return new CreateTable(this, this.#selectedDatabase, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters a table and updates to the new given structure.
|
||||
*
|
||||
* @param {*} name
|
||||
* @param {String} name
|
||||
* @returns {AlterTable}
|
||||
*/
|
||||
alterTable = (name) => {
|
||||
alterTable(name){
|
||||
return new AlterTable(this, this.#selectedDatabase, name);
|
||||
}
|
||||
|
||||
@ -249,7 +291,7 @@ class Instance {
|
||||
* Prepares to create a new table-structure
|
||||
* @returns {Structure}
|
||||
*/
|
||||
createStructure = () => {
|
||||
createStructure(){
|
||||
return new Structure();
|
||||
}
|
||||
|
||||
@ -259,52 +301,73 @@ class Instance {
|
||||
* @param {String} [database] - Name of the underlying database
|
||||
* @returns {Structure}
|
||||
*/
|
||||
getStructure = async (table, database) => {
|
||||
async getStructure(table, database){
|
||||
if (!this.#selectedDatabase && !database) throw new Error(`Can't get structure of table ${table}: Database not selected`);
|
||||
return new Structure(await this.queryRaw(`DESCRIBE ${database||this.#selectedDatabase}.${table};`));
|
||||
}
|
||||
|
||||
checkStructure = async (table, desiredStructure, database) => {
|
||||
/**
|
||||
* Checks the structure of a table
|
||||
* @param {String} table - Name of the table
|
||||
* @param {Structure} desiredStructure - Structure to check against
|
||||
* @param {String} [database] - Name of the database. If omitted, uses default database
|
||||
* @returns {CheckResult}
|
||||
*/
|
||||
async checkStructure(table, desiredStructure, database){
|
||||
if (!this.#selectedDatabase && !database) throw new Error(`Can't get structure of table ${table}: Database not selected`);
|
||||
const dbStruc = (await this.getStructure(table, database||this.#selectedDatabase)).get();
|
||||
const dbStruc = (await this.getStructure(table, database||this.#selectedDatabase)).get(); // Get current structure -> Array<Objects>
|
||||
const result = {
|
||||
errors: [],
|
||||
passed: []
|
||||
}
|
||||
for (let col of dbStruc){
|
||||
const desiredCol = desiredStructure.find((desCol) => desCol.Field === col.Field);
|
||||
if (!desiredCol) {
|
||||
for (let col of desiredStructure){
|
||||
const dbCol = dbStruc.find((dbCol) => col.Field === dbCol.Field); // Check if the current table has the field
|
||||
if (!dbCol) {
|
||||
result.errors.push(`${col.Field}: Missing completely`);
|
||||
continue;
|
||||
}
|
||||
let breakOut = false;
|
||||
for (let key in col){
|
||||
if (col[key] !== desiredCol[key]){
|
||||
result.errors.push(`${col.Field}.${key}: Required ${desiredCol[key]}, got ${col[key]}`);
|
||||
if (col[key] !== dbCol[key]){ // If the current key has a different value
|
||||
result.errors.push(`${dbCol.Field}.${key}: Required ${col[key]}, got ${dbCol[key]}`);
|
||||
breakOut=true;
|
||||
}
|
||||
}
|
||||
if (!breakOut){
|
||||
if (!breakOut){ // If no errors happened
|
||||
result.passed.push(`${col.Field}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns total amount of rows of a table
|
||||
* @param {String} table - Table name
|
||||
* @returns
|
||||
* @returns {Any}
|
||||
*/
|
||||
total = async (table) => {
|
||||
async total(table){
|
||||
return await new Select(this, this.#selectedDatabase, table).count(true).execute();
|
||||
}
|
||||
|
||||
isConnected = () => {
|
||||
/**
|
||||
* Returns if connection is established
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isConnected(){
|
||||
if (this.#connection) return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {awSQL: new awSQL(), Structure};
|
||||
/**
|
||||
* @typedef {Object} CheckResult
|
||||
* @property {Array<String>} errors - String representation of errors found
|
||||
* @property {Array<String>} passed - String representation of passed checks
|
||||
*/
|
||||
|
||||
const awSQLInstance = new awSQL();
|
||||
module.exports = {awSQLInstance, Structure};
|
||||
|
||||
/**
|
||||
* @exports awSQLInstance
|
||||
*/
|
||||
17
jsdoc.json
Normal file
17
jsdoc.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"source": {
|
||||
"include": ["./index.js", "./lib"],
|
||||
"includePattern": ".+\\.js$"
|
||||
},
|
||||
"opts": {
|
||||
"destination": "./docs",
|
||||
"recurse": true,
|
||||
"template": "node_modules/clean-jsdoc-theme",
|
||||
"readme": "./README.md"
|
||||
},
|
||||
"templates": {
|
||||
"default": {
|
||||
"outputSourceFiles": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Prepares a new Deletion
|
||||
*/
|
||||
class Delete {
|
||||
#instance;
|
||||
#database;
|
||||
@ -19,7 +22,7 @@ class Delete {
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
@ -28,10 +31,10 @@ class Delete {
|
||||
* Adds a where-clause to the query
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @param {Array<Any>} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
where(string, values=[]){
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
@ -40,12 +43,16 @@ class Delete {
|
||||
/**
|
||||
* Enables deletion of all rows
|
||||
*/
|
||||
force = () => {
|
||||
force(){
|
||||
this.#force = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
/**
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
async execute(){
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Prepares a new insertion
|
||||
*/
|
||||
class Insert {
|
||||
#instance;
|
||||
#database;
|
||||
@ -16,22 +19,26 @@ class Insert {
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The data (rows) to insert
|
||||
* @param {Array} objects - Array containing objects to insert, where the key represents the column-name. All objects must have the same structure!
|
||||
* @returns
|
||||
* @param {Array<Object>} objects - Array containing objects to insert, where the key represents the column-name. All objects must have the same structure!
|
||||
* @returns {this}
|
||||
*/
|
||||
data = (objects) => {
|
||||
data(objects){
|
||||
this.#data = objects;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
/**
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
async execute(){
|
||||
if (!this.#data) throw new Error("Insert: tried to insert without data");
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
|
||||
/**
|
||||
* Prepares a new Selection
|
||||
*/
|
||||
class Select {
|
||||
#instance;
|
||||
#database;
|
||||
@ -16,7 +19,7 @@ class Select {
|
||||
#group;
|
||||
|
||||
#aggregator;
|
||||
#join;
|
||||
#joins = [];
|
||||
#having;
|
||||
#havingValues;
|
||||
#limit;
|
||||
@ -33,7 +36,7 @@ class Select {
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
@ -44,7 +47,7 @@ class Select {
|
||||
* - With 'distinct' only unique values are returned
|
||||
* @returns {this}
|
||||
*/
|
||||
distinct = () => {
|
||||
distinct(){
|
||||
this.#distinct = true;
|
||||
return this;
|
||||
}
|
||||
@ -54,10 +57,10 @@ class Select {
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* - If you are using joins, specify the table and column together: table.column
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @param {Array<Any>} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
where(string, values=[]){
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
@ -68,10 +71,10 @@ class Select {
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* - If you are using joins, specify the table and column together: table.column
|
||||
* @param {String} string - The having-clause with possible aggregation and ? representing each values
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @param {Array<Any>} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
having = (string, values = []) => {
|
||||
having(string, values = []){
|
||||
this.#having = string;
|
||||
this.#havingValues = values;
|
||||
return this;
|
||||
@ -83,9 +86,9 @@ class Select {
|
||||
* @param {String} column - Column to order by
|
||||
* @param {Boolean} desc - Sorty descending
|
||||
* @param {"MIN"|"MAX"|"COUNT"|"SUM"|"AVG"} aggregation - The aggregation type to use
|
||||
* @returns
|
||||
* @returns {this}
|
||||
*/
|
||||
order = (column, desc=false, aggregation) => {
|
||||
order(column, desc=false, aggregation){
|
||||
if (["MIN", "MAX", "COUNT", "SUM", "AVG"].includes(aggregation)){
|
||||
switch(aggregation){
|
||||
case "MIN":
|
||||
@ -116,8 +119,9 @@ class Select {
|
||||
/**
|
||||
* Counts number of entries of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
* @returns {this}
|
||||
*/
|
||||
count = (doParse=false) => {
|
||||
count(doParse=false){
|
||||
this.#aggregator = "COUNT";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
@ -126,9 +130,9 @@ class Select {
|
||||
/**
|
||||
* Sums numerical rows of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
* @returns
|
||||
* @returns {this}
|
||||
*/
|
||||
sum = (doParse=false) => {
|
||||
sum(doParse=false){
|
||||
this.#aggregator ="SUM";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
@ -137,9 +141,9 @@ class Select {
|
||||
/**
|
||||
* Averages numerical rows of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
* @returns
|
||||
* @returns {this}
|
||||
*/
|
||||
avg = (doParse=false) => {
|
||||
avg(doParse=false){
|
||||
this.#aggregator = "AVG";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
@ -148,37 +152,39 @@ class Select {
|
||||
/**
|
||||
* Groups rows that have the same values into summary rows
|
||||
* @param {...String} columns - The columns to group by
|
||||
* @returns {this}
|
||||
*/
|
||||
group = (...columns) => {
|
||||
group(...columns){
|
||||
this.#group = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {"LEFT"|"INNER"|"RIGHT"|"FULL OUTER"} type
|
||||
* @param {*} table
|
||||
* @param {*} onOriginalColumn
|
||||
* @param {*} onJoinedColumn
|
||||
* @param {...any} columns
|
||||
* Adds a new join to the querry
|
||||
* @param {"LEFT"|"INNER"|"RIGHT"|"FULL OUTER"} type - Join-type
|
||||
* @param {String} table - Table to join on
|
||||
* @param {String} onOriginalColumn - Column name on the original table to check against
|
||||
* @param {String} onJoinedColumn - Column name of the join table to check against
|
||||
* @param {...any} columns - The columns to join. OG-Columns must be set!
|
||||
* @returns {this}
|
||||
*/
|
||||
join = (type, table, onOriginalColumn, onJoinedColumn, ...columns) => {
|
||||
this.#join = {
|
||||
join(type, table, onOriginalColumn, onJoinedColumn, ...columns){
|
||||
this.#joins.push({
|
||||
type,
|
||||
on: `%%FROM%%.${onOriginalColumn}=${table}.${onJoinedColumn}`,
|
||||
columns,
|
||||
table
|
||||
}
|
||||
})
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the query and specifies an offset
|
||||
* @param {Number} number - Limits the query by specified rows
|
||||
* @param {*} offset - Offset to start at
|
||||
* @returns
|
||||
* @param {Number} offset - Offset to start at
|
||||
* @returns {this}
|
||||
*/
|
||||
limit = (number, offset) => {
|
||||
limit(number, offset){
|
||||
this.#limit = {
|
||||
number,
|
||||
offset
|
||||
@ -190,9 +196,9 @@ class Select {
|
||||
* Paginates the query
|
||||
* @param {Number} page - The page to get (Minimum 1)
|
||||
* @param {Number} itemsPerPage - How many items a page should have
|
||||
* @returns
|
||||
* @returns {this}
|
||||
*/
|
||||
pagination = (page, itemsPerPage) => {
|
||||
pagination(page, itemsPerPage){
|
||||
if (page<1) page=1;
|
||||
this.#limit = {
|
||||
number: itemsPerPage,
|
||||
@ -202,17 +208,20 @@ class Select {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
* @returns
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
execute = async () => {
|
||||
async execute(){
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
const values = [];
|
||||
let columnString;
|
||||
if (this.#join && this.#columns.length>0){
|
||||
columnString = `${this.#columns.toString()},${this.#join.columns.toString()}`
|
||||
if (this.#joins.length>0 && this.#columns.length>0){
|
||||
columnString = `${this.#columns.toString()}`;
|
||||
for (let join of this.#joins){
|
||||
columnString+=`,${join.columns.toString()}`;
|
||||
}
|
||||
}else{
|
||||
columnString = this.#columns.length>0?this.#columns.toString():"*"
|
||||
}
|
||||
@ -228,7 +237,10 @@ class Select {
|
||||
|
||||
const groupString = this.#group?` GROUP BY ${this.#group.toString()}`:"";
|
||||
|
||||
const joinString = this.#join?` ${this.#join.type} JOIN ${this.#database}.${this.#join.table} ON ${this.#join.on.replace("%%FROM%%", this.#from)}`:"";
|
||||
let joinString = "";
|
||||
for (let join of this.#joins){
|
||||
joinString+=` ${join.type} JOIN ${this.#database}.${join.table} ON ${join.on.replace("%%FROM%%", this.#from)}`;
|
||||
}
|
||||
|
||||
const limitString = this.#limit?` LIMIT ${this.#limit.number}${this.#limit.offset?` OFFSET ${this.#limit.offset}`:""}`:"";
|
||||
|
||||
|
||||
307
lib/Tables.js
307
lib/Tables.js
@ -1,4 +1,8 @@
|
||||
|
||||
|
||||
/**
|
||||
* Prepares a new alteration of a table
|
||||
*/
|
||||
class AlterTable {
|
||||
#instance;
|
||||
#database;
|
||||
@ -15,7 +19,7 @@ class AlterTable {
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
@ -26,14 +30,18 @@ class AlterTable {
|
||||
* - Adds columns that are missing in the current table
|
||||
* - Modifies all other columns where at least one datatype is not matching
|
||||
* @param {Structure} struc - New structure for the table
|
||||
* @returns
|
||||
* @returns {this}
|
||||
*/
|
||||
structure = (struc) => {
|
||||
structure(struc){
|
||||
this.#structure = struc;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
/**
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
async execute(){
|
||||
if (!this.#database) throw new Error(`Can't alter table ${this.#name}: Database not selected`);
|
||||
if (!this.#name) throw new Error(`Can't alter table on ${this.#database}: No name given`);
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
@ -70,12 +78,13 @@ class AlterTable {
|
||||
}
|
||||
for (let key in col){
|
||||
if (oldStrucCol[key] !== col[key]){
|
||||
console.log(oldStrucCol[key], col[key]);
|
||||
// UPDATE COLUMN
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} MODIFY COLUMN \`${col.Field}\` ${col.Type}${col.Extra?` ${col.Extra}`:""}${col.Null==="YES"?" NULL":" NOT NULL"}${col.Default?` DEFAULT '${col.Default}'`:""}`);
|
||||
if (oldStrucCol.Key){
|
||||
switch(oldStrucCol.Key){
|
||||
case "PRI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP PRIMARY KEY (${col.Field});`);
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP PRIMARY KEY;`);
|
||||
break;
|
||||
case "MUL":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP INDEX ${col.Field};`);
|
||||
@ -106,6 +115,9 @@ class AlterTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares creation of a new table
|
||||
*/
|
||||
class CreateTable {
|
||||
#instance;
|
||||
#database;
|
||||
@ -122,7 +134,7 @@ class CreateTable {
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
@ -130,8 +142,9 @@ class CreateTable {
|
||||
/**
|
||||
* Sets a new name
|
||||
* @param {String} name
|
||||
* @returns {this}
|
||||
*/
|
||||
name = (name) => {
|
||||
name(name){
|
||||
this.#name = name;
|
||||
return this;
|
||||
}
|
||||
@ -139,17 +152,18 @@ class CreateTable {
|
||||
/**
|
||||
* Defines the structure for the table
|
||||
* @param {Structure} struc - Instance of Structure
|
||||
* @returns {this}
|
||||
*/
|
||||
structure = (struc) => {
|
||||
structure(struc){
|
||||
this.#structure = struc;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
* @returns
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
execute = async () => {
|
||||
async execute(){
|
||||
if (!this.#database) throw new Error(`Can't create table ${this.#name}: Database not selected`);
|
||||
if (!this.#name) throw new Error(`Can't create table on ${this.#database}: No name given`);
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
@ -177,6 +191,9 @@ class CreateTable {
|
||||
* @property {Boolean} [unsigned] - Only on numerical: Whether this numerical field should be unsigned
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new table-structure
|
||||
*/
|
||||
class Structure {
|
||||
#columns = [];
|
||||
constructor(tableDescription) {
|
||||
@ -187,8 +204,9 @@ class Structure {
|
||||
/**
|
||||
* Drops (Removes) a column name
|
||||
* @param {String} name - The name of the column
|
||||
* @returns {this}
|
||||
*/
|
||||
drop = (name) => {
|
||||
drop(name){
|
||||
let index;
|
||||
for (let i = 0; i < this.#columns.length; i++){
|
||||
if (this.#columns[i].Field === name) {
|
||||
@ -200,6 +218,10 @@ class Structure {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns
|
||||
* @returns {Array<ColumnStructure>}
|
||||
*/
|
||||
get(){
|
||||
return this.#columns;
|
||||
}
|
||||
@ -207,10 +229,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'char' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @param {Number} [size] - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
char = (name, size=1, options) => {
|
||||
char(name, size=1, options){
|
||||
if (size < 0 || size > 255){
|
||||
throw new Error(`Column datatype 'char' size must be a number between 0 and 255. Received: ${size}`);
|
||||
}
|
||||
@ -221,10 +244,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'varchar' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @param {Number} [size] - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
varchar = (name, size=8, options) => {
|
||||
varchar(name, size=8, options){
|
||||
if (size < 0 || size > 255){
|
||||
throw new Error(`Column datatype 'varchar' size must be a number between 0 and 65535. Received: ${size}`);
|
||||
}
|
||||
@ -235,11 +259,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'binary' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of data. Min 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Length of data. Min 1
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
binary = (name, size=1, options) => {
|
||||
binary(name, size=1, options){
|
||||
if (size < 1){
|
||||
throw new Error(`Column datatype 'binary' size must be a number above 0. Received: ${size}`);
|
||||
}
|
||||
@ -250,11 +274,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'varbinary' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of data. Min 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Length of data. Min 1
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
varbinary = (name, size=1, options) => {
|
||||
varbinary(name, size=1, options){
|
||||
if (size < 1){
|
||||
throw new Error(`Column datatype 'varbinary' size must be a number above 0. Received: ${size}`);
|
||||
}
|
||||
@ -266,10 +290,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'tinyblob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
tinyblob = (name, options) => {
|
||||
tinyblob(name, options){
|
||||
this.#columns.push(parseColumnData(name, `tinyblob`, options));
|
||||
return this;
|
||||
}
|
||||
@ -277,10 +301,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'tinytext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
tinytext = (name, options) => {
|
||||
tinytext(name, options){
|
||||
this.#columns.push(parseColumnData(name, `tinytext`, options));
|
||||
return this;
|
||||
}
|
||||
@ -288,10 +312,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'text' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
text = (name, options) => {
|
||||
text(name, options){
|
||||
this.#columns.push(parseColumnData(name, `text`, options));
|
||||
return this;
|
||||
}
|
||||
@ -299,11 +323,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'blob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Size in bytes. Min 1, Max 65535. Defaults to 65535
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Size in bytes. Min 1, Max 65535. Defaults to 65535
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
blob = (name, size=65535, options) => {
|
||||
blob(name, size=65535, options){
|
||||
if (size < 1 || size > 65535) throw new Error(`Column datatype 'blob' size must be a number between 1 and 65535. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `blob(${size})`, options));
|
||||
return this;
|
||||
@ -312,10 +336,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'mediumtext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
mediumtext = (name, options) => {
|
||||
mediumtext(name, options){
|
||||
this.#columns.push(parseColumnData(name, `mediumtext`, options));
|
||||
return this;
|
||||
}
|
||||
@ -323,10 +347,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'longtext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
longtext = (name, options) => {
|
||||
longtext(name, options){
|
||||
this.#columns.push(parseColumnData(name, `longtext`, options));
|
||||
return this;
|
||||
}
|
||||
@ -334,10 +358,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'longblob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
longblob = (name, options) => {
|
||||
longblob(name, options){
|
||||
this.#columns.push(parseColumnData(name, `longblob`, options));
|
||||
return this;
|
||||
}
|
||||
@ -345,39 +369,39 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'enum' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Array} vals - Array of possible values
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Array<String>} vals - Array of possible values
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
enum = (name, vals=[], options) => {
|
||||
enum(name, vals=[], options){
|
||||
if (!Array.isArray(vals)) throw new Error(`Column datatype 'enum': 'vals' must be of type Array`);
|
||||
if (vals.length<=0) throw new Error(`Column datatype 'enum' must contain a list of possible values. Received undefined`);
|
||||
this.#columns.push(parseColumnData(name, `enum(${vals.map(val=>`"${val}"`)})`, options));
|
||||
this.#columns.push(parseColumnData(name, `enum(${vals.map(val=>`'${val}'`)})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'enum' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Array} vals - Array of possible values
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Array<String>} vals - Array of possible values
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
set = (name, vals=[], options) => {
|
||||
set(name, vals=[], options){
|
||||
if (!Array.isArray(vals)) throw new Error(`Column datatype 'set': 'vals' must be of type Array`);
|
||||
if (vals.length<=0) throw new Error(`Column datatype 'set' must contain a list of possible values. Received undefined`);
|
||||
this.#columns.push(parseColumnData(name, `set(${vals.map(val=>`"${val}"`)})`, options));
|
||||
this.#columns.push(parseColumnData(name, `set(${vals.map(val=>`'${val}'`)})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'bit' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 64. Default to 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Min 1, Max 64. Default to 1
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
bit = (name, size=1, options) => {
|
||||
bit(name, size=1, options){
|
||||
if (size < 1 || size > 64) throw new Error(`Column datatype 'bit' size must be a number between 1 and 64. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `bit(${size})`, options));
|
||||
return this;
|
||||
@ -386,11 +410,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'tinyint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
tinyint = (name, size=255, options) => {
|
||||
tinyint(name, size=255, options){
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'tinyint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `tinyint(${size})`, options));
|
||||
@ -400,10 +424,10 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'bool' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
bool = (name, options) => {
|
||||
bool(name, options){
|
||||
this.#columns.push(parseColumnData(name, `bool`, options));
|
||||
return this;
|
||||
}
|
||||
@ -411,11 +435,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'smallint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
smallint = (name, size=255, options) => {
|
||||
smallint(name, size=255, options){
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'smallint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `smallint(${size})`, options));
|
||||
return this;
|
||||
@ -424,11 +448,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'mediumint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
mediumint = (name, size=255, options) => {
|
||||
mediumint(name, size=255, options){
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'mediumint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `mediumint(${size})`, options));
|
||||
return this;
|
||||
@ -437,11 +461,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'int' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
int = (name, size=255, options) => {
|
||||
int(name, size=255, options){
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'int' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `int(${size})`, options));
|
||||
return this;
|
||||
@ -450,11 +474,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'bigint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
bigint = (name, size=255, options) => {
|
||||
bigint(name, size=255, options){
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'bigint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `bigint(${size})`, options));
|
||||
return this;
|
||||
@ -463,11 +487,11 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'float' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} p - Precision. Min 1, Max 53. Defaults to 25
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [p] - Precision. Min 1, Max 53. Defaults to 25
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
float = (name, p=25, options) => {
|
||||
float(name, p=25, options){
|
||||
if (p < 1 || p > 53) throw new Error(`Column datatype 'float' size must be a number between 1 and 53. Received: ${p}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `float(${p})`, options));
|
||||
@ -477,12 +501,12 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'double' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1. Defaults to 16
|
||||
* @param {Number} d - Double precision. Min 1. Defaults to 8
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Min 1. Defaults to 16
|
||||
* @param {Number} [d] - Double precision. Min 1. Defaults to 8
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
double = (name, size=16, d=8, options) => {
|
||||
double(name, size=16, d=8, options){
|
||||
if (size < 1) throw new Error(`Column datatype 'double' size must be greater than 0. Received: ${p}`);
|
||||
if (d < 1) throw new Error(`Column datatype 'double' d must be greater than 0. Received: ${p}`);
|
||||
this.#columns.push(parseColumnData(name, `double(${size},${d})`, options));
|
||||
@ -492,32 +516,97 @@ class Structure {
|
||||
/**
|
||||
* Adds a new column of data type 'decimal' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 65. Defaults to 10
|
||||
* @param {Number} d - Double precision. Min 0. Defaults to 0.
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
* @param {Number} [size] - Min 1, Max 65. Defaults to 10
|
||||
* @param {Number} [d] - Double precision. Min 0. Defaults to 0.
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
decimal = (name, size=10, d=0, options) => {
|
||||
decimal(name, size=10, d=0, options){
|
||||
if (size < 1 || size > 65) throw new Error(`Column datatype 'decimal' size must be a number between 1 and 65. Received: ${size}`);
|
||||
if (d < 0) throw new Error(`Column datatype 'decimal' d must be positive. Received: ${d}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `decimal(${size},${d})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'date' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
date(name, options){
|
||||
this.#columns.push(parseColumnData(name, `date`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} type
|
||||
* Adds a new column of data type 'datetime' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
datetime(name, fsp=0, options){
|
||||
if (fsp < 0 || fsp > 6) throw new Error(`Column datatype 'fsp' size must be a number between 0 and 6. Received: ${size}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `datetime(${fsp})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'timestamp' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
timestamp(name, fsp=0, options){
|
||||
if (fsp < 0 || fsp > 6) throw new Error(`Column datatype 'fsp' size must be a number between 0 and 6. Received: ${size}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `timestamp(${fsp})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'time' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
time(name, fsp=0, options){
|
||||
if (fsp < 0 || fsp > 6) throw new Error(`Column datatype 'fsp' size must be a number between 0 and 6. Received: ${size}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `time(${fsp})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'year' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} [options] - Extra constraint options
|
||||
* @returns {this}
|
||||
*/
|
||||
year(name, options){
|
||||
this.#columns.push(parseColumnData(name, `time`, options));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given column data
|
||||
* @private
|
||||
* @param {String} name - Column name
|
||||
* @param {String} type - Column type
|
||||
* @param {ConstraintOptions} options
|
||||
* @returns
|
||||
* @returns {ColumnStructure}
|
||||
*/
|
||||
function parseColumnData(name, type, options={}){
|
||||
if (options.primary + options.unique + options.index > 1) throw new Error(`ConstrainError: Only one of 'primary', 'unique' or 'index' allowed`);
|
||||
return {
|
||||
Field: name,
|
||||
Type: `${type}${options.unsigned?" UNSIGNED":""}`,
|
||||
Type: `${type}${options.unsigned?" unsigned":""}`,
|
||||
Null: options.null?"YES":"NO",
|
||||
Key: options.primary?"PRI":options.index?"MUL":options.unique?"UNI":"",
|
||||
Default: options.default?options.default:null,
|
||||
@ -525,5 +614,15 @@ function parseColumnData(name, type, options={}){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ColumnStructure
|
||||
* @property {String} Field - Column name
|
||||
* @property {String} Type - Column type
|
||||
* @property {"YES"|"NO"} Null - Whether the column allows null-values
|
||||
* @property {""|"PRI"|"MUL"|"UNI"} Key - PRI = Primary, MUL = Indexed, UNI = Unique
|
||||
* @property {String|null} Default - Default values for this row
|
||||
* @property {""|"auto_increment"} Extra - Any extra
|
||||
*/
|
||||
|
||||
|
||||
module.exports = { CreateTable, Structure, AlterTable }
|
||||
@ -1,3 +1,7 @@
|
||||
|
||||
/**
|
||||
* Prepares a new Update
|
||||
*/
|
||||
class Update{
|
||||
#instance;
|
||||
#database;
|
||||
@ -16,8 +20,9 @@ class Update{
|
||||
/**
|
||||
* Updates all matching rows with the given object
|
||||
* @param {Object} object - The object with the data to update. Keys represent column names
|
||||
* @returns {this}
|
||||
*/
|
||||
data = (object) => {
|
||||
data(object){
|
||||
this.#data = object;
|
||||
return this;
|
||||
}
|
||||
@ -27,15 +32,16 @@ class Update{
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
selectDatabase(database){
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables update of all rows
|
||||
* @returns {this}
|
||||
*/
|
||||
force = () => {
|
||||
force(){
|
||||
this.#force = true;
|
||||
return this;
|
||||
}
|
||||
@ -44,19 +50,20 @@ class Update{
|
||||
* Adds a where-clause to the query
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @param {Array<Any>} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
where(string, values=[]){
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
* Executes the prepared querry
|
||||
* @returns {Any}
|
||||
*/
|
||||
execute = async () => {
|
||||
async execute(){
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
if (!this.#where && !this.#force) throw new Error("Update: Tried to update all rows. If this was intentional, use .force() on Update");
|
||||
|
||||
@ -3,16 +3,16 @@
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"generate-docs": "jsdoc --configure jsdoc.json --verbose"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"mysql": "^2.18.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
"devDependencies": {
|
||||
"clean-jsdoc-theme": "^4.3.0"
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user