Add HTML sanitization when injecting code in pages in the UI
This commit is contained in:
parent
4d50026744
commit
ece5ce1cdf
|
@ -6,7 +6,8 @@ paths:
|
||||||
- src/ui
|
- src/ui
|
||||||
- src/common
|
- src/common
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- src/ui/static/tsparticles.bundle.min.js
|
- src/ui/static/js/tsparticles.bundle.min.js
|
||||||
- src/ui/static/js/utils/flatpickr.js
|
|
||||||
- src/ui/static/js/editor
|
- src/ui/static/js/editor
|
||||||
|
- src/ui/static/js/utils/flatpickr.js
|
||||||
|
- src/ui/static/js/utils/purify
|
||||||
- src/common/core/modsecurity/files
|
- src/common/core/modsecurity/files
|
||||||
|
|
|
@ -13,6 +13,7 @@ SECURITY.md
|
||||||
tsparticles.bundle.min.js
|
tsparticles.bundle.min.js
|
||||||
flatpickr.*
|
flatpickr.*
|
||||||
src/ui/static/js/editor/*
|
src/ui/static/js/editor/*
|
||||||
|
src/ui/static/js/utils/purify/*
|
||||||
src/ui/templates/*
|
src/ui/templates/*
|
||||||
datepicker-foundation.css
|
datepicker-foundation.css
|
||||||
examples/*
|
examples/*
|
||||||
|
|
|
@ -72,12 +72,13 @@ class News {
|
||||||
excerpt,
|
excerpt,
|
||||||
tags,
|
tags,
|
||||||
date,
|
date,
|
||||||
lastUpdate,
|
lastUpdate
|
||||||
);
|
);
|
||||||
|
let cleanHTML = DOMPurify.sanitize(cardHTML);
|
||||||
//add to DOM
|
//add to DOM
|
||||||
document
|
document
|
||||||
.querySelector("[data-news-container]")
|
.querySelector("[data-news-container]")
|
||||||
.insertAdjacentHTML("afterbegin", cardHTML);
|
.insertAdjacentHTML("afterbegin", cleanHTML);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +191,7 @@ class darkMode {
|
||||||
};
|
};
|
||||||
const send = await fetch(
|
const send = await fetch(
|
||||||
`${location.href.split("/").slice(0, -1).join("/")}/darkmode`,
|
`${location.href.split("/").slice(0, -1).join("/")}/darkmode`,
|
||||||
data,
|
data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +231,7 @@ class FlashMsg {
|
||||||
flashEl.remove();
|
flashEl.remove();
|
||||||
//update count
|
//update count
|
||||||
this.flashCount.textContent = document.querySelectorAll(
|
this.flashCount.textContent = document.querySelectorAll(
|
||||||
"[data-flash-message]",
|
"[data-flash-message]"
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
@ -299,7 +300,7 @@ const setMenu = new Menu();
|
||||||
const setNewsSidebar = new Sidebar(
|
const setNewsSidebar = new Sidebar(
|
||||||
"[data-sidebar-info]",
|
"[data-sidebar-info]",
|
||||||
"[data-sidebar-info-open]",
|
"[data-sidebar-info-open]",
|
||||||
"[data-sidebar-info-close]",
|
"[data-sidebar-info-close]"
|
||||||
);
|
);
|
||||||
|
|
||||||
const setCheckbox = new Checkbox();
|
const setCheckbox = new Checkbox();
|
||||||
|
@ -310,7 +311,7 @@ const setDisabledPop = new DisabledPop();
|
||||||
const setFlashSidebar = new Sidebar(
|
const setFlashSidebar = new Sidebar(
|
||||||
"[data-flash-sidebar]",
|
"[data-flash-sidebar]",
|
||||||
"[data-flash-sidebar-open]",
|
"[data-flash-sidebar-open]",
|
||||||
"[data-flash-sidebar-close]",
|
"[data-flash-sidebar-close]"
|
||||||
);
|
);
|
||||||
const setNews = new News();
|
const setNews = new News();
|
||||||
const setDarkM = new darkMode();
|
const setDarkM = new darkMode();
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Dropdown {
|
||||||
const btn = e.target.closest("button");
|
const btn = e.target.closest("button");
|
||||||
const btnValue = btn.getAttribute("value");
|
const btnValue = btn.getAttribute("value");
|
||||||
const btnSetting = btn.getAttribute(
|
const btnSetting = btn.getAttribute(
|
||||||
`data-${this.prefix}-setting-select-dropdown-btn`,
|
`data-${this.prefix}-setting-select-dropdown-btn`
|
||||||
);
|
);
|
||||||
//stop if same value to avoid new fetching
|
//stop if same value to avoid new fetching
|
||||||
const isSameVal = this.isSameValue(btnSetting, btnValue);
|
const isSameVal = this.isSameValue(btnSetting, btnValue);
|
||||||
|
@ -57,7 +57,7 @@ class Dropdown {
|
||||||
|
|
||||||
closeAllDrop() {
|
closeAllDrop() {
|
||||||
const drops = document.querySelectorAll(
|
const drops = document.querySelectorAll(
|
||||||
`[data-${this.prefix}-setting-select-dropdown]`,
|
`[data-${this.prefix}-setting-select-dropdown]`
|
||||||
);
|
);
|
||||||
drops.forEach((drop) => {
|
drops.forEach((drop) => {
|
||||||
drop.classList.add("hidden");
|
drop.classList.add("hidden");
|
||||||
|
@ -65,8 +65,8 @@ class Dropdown {
|
||||||
document
|
document
|
||||||
.querySelector(
|
.querySelector(
|
||||||
`svg[data-${this.prefix}-setting-select="${drop.getAttribute(
|
`svg[data-${this.prefix}-setting-select="${drop.getAttribute(
|
||||||
`data-${this.prefix}-setting-select-dropdown`,
|
`data-${this.prefix}-setting-select-dropdown`
|
||||||
)}"]`,
|
)}"]`
|
||||||
)
|
)
|
||||||
.classList.remove("rotate-180");
|
.classList.remove("rotate-180");
|
||||||
});
|
});
|
||||||
|
@ -74,7 +74,7 @@ class Dropdown {
|
||||||
|
|
||||||
isSameValue(btnSetting, value) {
|
isSameValue(btnSetting, value) {
|
||||||
const selectCustom = document.querySelector(
|
const selectCustom = document.querySelector(
|
||||||
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`,
|
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`
|
||||||
);
|
);
|
||||||
const currVal = selectCustom.textContent;
|
const currVal = selectCustom.textContent;
|
||||||
return currVal === value ? true : false;
|
return currVal === value ? true : false;
|
||||||
|
@ -82,30 +82,30 @@ class Dropdown {
|
||||||
|
|
||||||
setSelectNewValue(btnSetting, value) {
|
setSelectNewValue(btnSetting, value) {
|
||||||
const selectCustom = document.querySelector(
|
const selectCustom = document.querySelector(
|
||||||
`[data-${this.prefix}-setting-select="${btnSetting}"]`,
|
`[data-${this.prefix}-setting-select="${btnSetting}"]`
|
||||||
);
|
);
|
||||||
selectCustom.querySelector(
|
selectCustom.querySelector(
|
||||||
`[data-${this.prefix}-setting-select-text]`,
|
`[data-${this.prefix}-setting-select-text]`
|
||||||
).textContent = value;
|
).textContent = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
hideDropdown(btnSetting) {
|
hideDropdown(btnSetting) {
|
||||||
//hide dropdown
|
//hide dropdown
|
||||||
const dropdownEl = document.querySelector(
|
const dropdownEl = document.querySelector(
|
||||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
|
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
|
||||||
);
|
);
|
||||||
dropdownEl.classList.add("hidden");
|
dropdownEl.classList.add("hidden");
|
||||||
dropdownEl.classList.remove("flex");
|
dropdownEl.classList.remove("flex");
|
||||||
//svg effect
|
//svg effect
|
||||||
const dropdownChevron = document.querySelector(
|
const dropdownChevron = document.querySelector(
|
||||||
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`,
|
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`
|
||||||
);
|
);
|
||||||
dropdownChevron.classList.remove("rotate-180");
|
dropdownChevron.classList.remove("rotate-180");
|
||||||
}
|
}
|
||||||
|
|
||||||
changeDropBtnStyle(btnSetting, selectedBtn) {
|
changeDropBtnStyle(btnSetting, selectedBtn) {
|
||||||
const dropdownEl = document.querySelector(
|
const dropdownEl = document.querySelector(
|
||||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
|
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
|
||||||
);
|
);
|
||||||
//reset dropdown btns
|
//reset dropdown btns
|
||||||
const btnEls = dropdownEl.querySelectorAll("button");
|
const btnEls = dropdownEl.querySelectorAll("button");
|
||||||
|
@ -116,7 +116,7 @@ class Dropdown {
|
||||||
"bg-primary",
|
"bg-primary",
|
||||||
"bg-primary",
|
"bg-primary",
|
||||||
"text-gray-300",
|
"text-gray-300",
|
||||||
"text-gray-300",
|
"text-gray-300"
|
||||||
);
|
);
|
||||||
btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
|
btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
|
||||||
});
|
});
|
||||||
|
@ -124,7 +124,7 @@ class Dropdown {
|
||||||
selectedBtn.classList.remove(
|
selectedBtn.classList.remove(
|
||||||
"bg-white",
|
"bg-white",
|
||||||
"dark:bg-slate-700",
|
"dark:bg-slate-700",
|
||||||
"text-gray-700",
|
"text-gray-700"
|
||||||
);
|
);
|
||||||
selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
|
selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
|
||||||
}
|
}
|
||||||
|
@ -135,10 +135,10 @@ class Dropdown {
|
||||||
.getAttribute(`data-${this.prefix}-setting-select`);
|
.getAttribute(`data-${this.prefix}-setting-select`);
|
||||||
//toggle dropdown
|
//toggle dropdown
|
||||||
const dropdownEl = document.querySelector(
|
const dropdownEl = document.querySelector(
|
||||||
`[data-${this.prefix}-setting-select-dropdown="${attribut}"]`,
|
`[data-${this.prefix}-setting-select-dropdown="${attribut}"]`
|
||||||
);
|
);
|
||||||
const dropdownChevron = document.querySelector(
|
const dropdownChevron = document.querySelector(
|
||||||
`svg[data-${this.prefix}-setting-select="${attribut}"]`,
|
`svg[data-${this.prefix}-setting-select="${attribut}"]`
|
||||||
);
|
);
|
||||||
dropdownEl.classList.toggle("hidden");
|
dropdownEl.classList.toggle("hidden");
|
||||||
dropdownEl.classList.toggle("flex");
|
dropdownEl.classList.toggle("flex");
|
||||||
|
@ -300,7 +300,7 @@ class Upload {
|
||||||
this.dropZoneElement.classList.remove(
|
this.dropZoneElement.classList.remove(
|
||||||
"border-solid",
|
"border-solid",
|
||||||
"bg-gray-100",
|
"bg-gray-100",
|
||||||
"dark:bg-slate-700/50",
|
"dark:bg-slate-700/50"
|
||||||
);
|
);
|
||||||
this.dropZoneElement.classList.add("border-dashed");
|
this.dropZoneElement.classList.add("border-dashed");
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ class Upload {
|
||||||
this.dropZoneElement.classList.add(
|
this.dropZoneElement.classList.add(
|
||||||
"border-solid",
|
"border-solid",
|
||||||
"bg-gray-100",
|
"bg-gray-100",
|
||||||
"dark:bg-slate-700/50",
|
"dark:bg-slate-700/50"
|
||||||
);
|
);
|
||||||
this.dropZoneElement.classList.remove("border-dashed");
|
this.dropZoneElement.classList.remove("border-dashed");
|
||||||
}
|
}
|
||||||
|
@ -345,13 +345,13 @@ class Upload {
|
||||||
if (xhr.status == 201) {
|
if (xhr.status == 201) {
|
||||||
this.uploadedArea.insertAdjacentHTML(
|
this.uploadedArea.insertAdjacentHTML(
|
||||||
"afterbegin",
|
"afterbegin",
|
||||||
this.fileSuccess(name, fileSize),
|
this.fileSuccess(name, fileSize)
|
||||||
);
|
);
|
||||||
this.allowReload();
|
this.allowReload();
|
||||||
} else {
|
} else {
|
||||||
this.uploadedArea.insertAdjacentHTML(
|
this.uploadedArea.insertAdjacentHTML(
|
||||||
"afterbegin",
|
"afterbegin",
|
||||||
this.fileFail(name, fileSize),
|
this.fileFail(name, fileSize)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,8 @@ class Upload {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
return str;
|
let cleanHTML = DOMPurify.sanitize(str);
|
||||||
|
return cleanHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileFail(name, fileSize) {
|
fileFail(name, fileSize) {
|
||||||
|
@ -435,7 +436,8 @@ class Upload {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
return str;
|
let cleanHTML = DOMPurify.sanitize(str);
|
||||||
|
return cleanHTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,10 +450,10 @@ class Modal {
|
||||||
this.modalExtInp = this.modal.querySelector("input#external");
|
this.modalExtInp = this.modal.querySelector("input#external");
|
||||||
|
|
||||||
this.modalTitle = this.modal.querySelector(
|
this.modalTitle = this.modal.querySelector(
|
||||||
`[data-${this.prefix}-modal-title]`,
|
`[data-${this.prefix}-modal-title]`
|
||||||
);
|
);
|
||||||
this.modalTxt = this.modal.querySelector(
|
this.modalTxt = this.modal.querySelector(
|
||||||
`[data-${this.prefix}-modal-text]`,
|
`[data-${this.prefix}-modal-text]`
|
||||||
);
|
);
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,18 +52,18 @@ class Select {
|
||||||
try {
|
try {
|
||||||
if (!e.target.closest("button")) {
|
if (!e.target.closest("button")) {
|
||||||
const selectEls = document.querySelectorAll(
|
const selectEls = document.querySelectorAll(
|
||||||
"div[data-setting-select-dropdown]",
|
"div[data-setting-select-dropdown]"
|
||||||
);
|
);
|
||||||
selectEls.forEach((select) => {
|
selectEls.forEach((select) => {
|
||||||
select.classList.add("hidden");
|
select.classList.add("hidden");
|
||||||
select.classList.remove("flex");
|
select.classList.remove("flex");
|
||||||
});
|
});
|
||||||
const btnEls = document.querySelectorAll(
|
const btnEls = document.querySelectorAll(
|
||||||
"button[data-setting-select]",
|
"button[data-setting-select]"
|
||||||
);
|
);
|
||||||
btnEls.forEach((btn) => {
|
btnEls.forEach((btn) => {
|
||||||
const dropdownChevron = btn.querySelector(
|
const dropdownChevron = btn.querySelector(
|
||||||
`svg[data-setting-select]`,
|
`svg[data-setting-select]`
|
||||||
);
|
);
|
||||||
dropdownChevron.classList.remove("rotate-180");
|
dropdownChevron.classList.remove("rotate-180");
|
||||||
});
|
});
|
||||||
|
@ -87,7 +87,7 @@ class Select {
|
||||||
.hasAttribute(`data-setting-select-dropdown-btn`)
|
.hasAttribute(`data-setting-select-dropdown-btn`)
|
||||||
) {
|
) {
|
||||||
const btn = e.target.closest(
|
const btn = e.target.closest(
|
||||||
`button[data-setting-select-dropdown-btn]`,
|
`button[data-setting-select-dropdown-btn]`
|
||||||
);
|
);
|
||||||
const btnValue = btn.getAttribute("value");
|
const btnValue = btn.getAttribute("value");
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class Select {
|
||||||
|
|
||||||
//close dropdown
|
//close dropdown
|
||||||
const dropdownChevron = selectCustom.querySelector(
|
const dropdownChevron = selectCustom.querySelector(
|
||||||
`svg[data-setting-select]`,
|
`svg[data-setting-select]`
|
||||||
);
|
);
|
||||||
dropdownChevron.classList.remove("rotate-180");
|
dropdownChevron.classList.remove("rotate-180");
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class Select {
|
||||||
});
|
});
|
||||||
//select new one
|
//select new one
|
||||||
const newOption = selectEl.querySelector(
|
const newOption = selectEl.querySelector(
|
||||||
`option[value="${selectedValue}"]`,
|
`option[value="${selectedValue}"]`
|
||||||
);
|
);
|
||||||
newOption.selected = true;
|
newOption.selected = true;
|
||||||
newOption.setAttribute("selected", "");
|
newOption.setAttribute("selected", "");
|
||||||
|
@ -255,7 +255,8 @@ class DisabledPop {
|
||||||
} bg-blue-500 absolute right-2 rounded-lg px-2 py-1 z-20 dark:brightness-90">
|
} bg-blue-500 absolute right-2 rounded-lg px-2 py-1 z-20 dark:brightness-90">
|
||||||
<p class="m-0 text-xs text-white dark:text-gray-100">disabled by ${method}</p>
|
<p class="m-0 text-xs text-white dark:text-gray-100">disabled by ${method}</p>
|
||||||
</div>`;
|
</div>`;
|
||||||
el.insertAdjacentHTML("beforebegin", popupHTML);
|
let cleanHTML = DOMPurify.sanitize(popupHTML);
|
||||||
|
el.insertAdjacentHTML("beforebegin", cleanHTML);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,362 @@
|
||||||
|
import { freeze } from './utils.js';
|
||||||
|
|
||||||
|
export const html = freeze([
|
||||||
|
'accept',
|
||||||
|
'action',
|
||||||
|
'align',
|
||||||
|
'alt',
|
||||||
|
'autocapitalize',
|
||||||
|
'autocomplete',
|
||||||
|
'autopictureinpicture',
|
||||||
|
'autoplay',
|
||||||
|
'background',
|
||||||
|
'bgcolor',
|
||||||
|
'border',
|
||||||
|
'capture',
|
||||||
|
'cellpadding',
|
||||||
|
'cellspacing',
|
||||||
|
'checked',
|
||||||
|
'cite',
|
||||||
|
'class',
|
||||||
|
'clear',
|
||||||
|
'color',
|
||||||
|
'cols',
|
||||||
|
'colspan',
|
||||||
|
'controls',
|
||||||
|
'controlslist',
|
||||||
|
'coords',
|
||||||
|
'crossorigin',
|
||||||
|
'datetime',
|
||||||
|
'decoding',
|
||||||
|
'default',
|
||||||
|
'dir',
|
||||||
|
'disabled',
|
||||||
|
'disablepictureinpicture',
|
||||||
|
'disableremoteplayback',
|
||||||
|
'download',
|
||||||
|
'draggable',
|
||||||
|
'enctype',
|
||||||
|
'enterkeyhint',
|
||||||
|
'face',
|
||||||
|
'for',
|
||||||
|
'headers',
|
||||||
|
'height',
|
||||||
|
'hidden',
|
||||||
|
'high',
|
||||||
|
'href',
|
||||||
|
'hreflang',
|
||||||
|
'id',
|
||||||
|
'inputmode',
|
||||||
|
'integrity',
|
||||||
|
'ismap',
|
||||||
|
'kind',
|
||||||
|
'label',
|
||||||
|
'lang',
|
||||||
|
'list',
|
||||||
|
'loading',
|
||||||
|
'loop',
|
||||||
|
'low',
|
||||||
|
'max',
|
||||||
|
'maxlength',
|
||||||
|
'media',
|
||||||
|
'method',
|
||||||
|
'min',
|
||||||
|
'minlength',
|
||||||
|
'multiple',
|
||||||
|
'muted',
|
||||||
|
'name',
|
||||||
|
'nonce',
|
||||||
|
'noshade',
|
||||||
|
'novalidate',
|
||||||
|
'nowrap',
|
||||||
|
'open',
|
||||||
|
'optimum',
|
||||||
|
'pattern',
|
||||||
|
'placeholder',
|
||||||
|
'playsinline',
|
||||||
|
'poster',
|
||||||
|
'preload',
|
||||||
|
'pubdate',
|
||||||
|
'radiogroup',
|
||||||
|
'readonly',
|
||||||
|
'rel',
|
||||||
|
'required',
|
||||||
|
'rev',
|
||||||
|
'reversed',
|
||||||
|
'role',
|
||||||
|
'rows',
|
||||||
|
'rowspan',
|
||||||
|
'spellcheck',
|
||||||
|
'scope',
|
||||||
|
'selected',
|
||||||
|
'shape',
|
||||||
|
'size',
|
||||||
|
'sizes',
|
||||||
|
'span',
|
||||||
|
'srclang',
|
||||||
|
'start',
|
||||||
|
'src',
|
||||||
|
'srcset',
|
||||||
|
'step',
|
||||||
|
'style',
|
||||||
|
'summary',
|
||||||
|
'tabindex',
|
||||||
|
'title',
|
||||||
|
'translate',
|
||||||
|
'type',
|
||||||
|
'usemap',
|
||||||
|
'valign',
|
||||||
|
'value',
|
||||||
|
'width',
|
||||||
|
'xmlns',
|
||||||
|
'slot',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const svg = freeze([
|
||||||
|
'accent-height',
|
||||||
|
'accumulate',
|
||||||
|
'additive',
|
||||||
|
'alignment-baseline',
|
||||||
|
'ascent',
|
||||||
|
'attributename',
|
||||||
|
'attributetype',
|
||||||
|
'azimuth',
|
||||||
|
'basefrequency',
|
||||||
|
'baseline-shift',
|
||||||
|
'begin',
|
||||||
|
'bias',
|
||||||
|
'by',
|
||||||
|
'class',
|
||||||
|
'clip',
|
||||||
|
'clippathunits',
|
||||||
|
'clip-path',
|
||||||
|
'clip-rule',
|
||||||
|
'color',
|
||||||
|
'color-interpolation',
|
||||||
|
'color-interpolation-filters',
|
||||||
|
'color-profile',
|
||||||
|
'color-rendering',
|
||||||
|
'cx',
|
||||||
|
'cy',
|
||||||
|
'd',
|
||||||
|
'dx',
|
||||||
|
'dy',
|
||||||
|
'diffuseconstant',
|
||||||
|
'direction',
|
||||||
|
'display',
|
||||||
|
'divisor',
|
||||||
|
'dur',
|
||||||
|
'edgemode',
|
||||||
|
'elevation',
|
||||||
|
'end',
|
||||||
|
'fill',
|
||||||
|
'fill-opacity',
|
||||||
|
'fill-rule',
|
||||||
|
'filter',
|
||||||
|
'filterunits',
|
||||||
|
'flood-color',
|
||||||
|
'flood-opacity',
|
||||||
|
'font-family',
|
||||||
|
'font-size',
|
||||||
|
'font-size-adjust',
|
||||||
|
'font-stretch',
|
||||||
|
'font-style',
|
||||||
|
'font-variant',
|
||||||
|
'font-weight',
|
||||||
|
'fx',
|
||||||
|
'fy',
|
||||||
|
'g1',
|
||||||
|
'g2',
|
||||||
|
'glyph-name',
|
||||||
|
'glyphref',
|
||||||
|
'gradientunits',
|
||||||
|
'gradienttransform',
|
||||||
|
'height',
|
||||||
|
'href',
|
||||||
|
'id',
|
||||||
|
'image-rendering',
|
||||||
|
'in',
|
||||||
|
'in2',
|
||||||
|
'k',
|
||||||
|
'k1',
|
||||||
|
'k2',
|
||||||
|
'k3',
|
||||||
|
'k4',
|
||||||
|
'kerning',
|
||||||
|
'keypoints',
|
||||||
|
'keysplines',
|
||||||
|
'keytimes',
|
||||||
|
'lang',
|
||||||
|
'lengthadjust',
|
||||||
|
'letter-spacing',
|
||||||
|
'kernelmatrix',
|
||||||
|
'kernelunitlength',
|
||||||
|
'lighting-color',
|
||||||
|
'local',
|
||||||
|
'marker-end',
|
||||||
|
'marker-mid',
|
||||||
|
'marker-start',
|
||||||
|
'markerheight',
|
||||||
|
'markerunits',
|
||||||
|
'markerwidth',
|
||||||
|
'maskcontentunits',
|
||||||
|
'maskunits',
|
||||||
|
'max',
|
||||||
|
'mask',
|
||||||
|
'media',
|
||||||
|
'method',
|
||||||
|
'mode',
|
||||||
|
'min',
|
||||||
|
'name',
|
||||||
|
'numoctaves',
|
||||||
|
'offset',
|
||||||
|
'operator',
|
||||||
|
'opacity',
|
||||||
|
'order',
|
||||||
|
'orient',
|
||||||
|
'orientation',
|
||||||
|
'origin',
|
||||||
|
'overflow',
|
||||||
|
'paint-order',
|
||||||
|
'path',
|
||||||
|
'pathlength',
|
||||||
|
'patterncontentunits',
|
||||||
|
'patterntransform',
|
||||||
|
'patternunits',
|
||||||
|
'points',
|
||||||
|
'preservealpha',
|
||||||
|
'preserveaspectratio',
|
||||||
|
'primitiveunits',
|
||||||
|
'r',
|
||||||
|
'rx',
|
||||||
|
'ry',
|
||||||
|
'radius',
|
||||||
|
'refx',
|
||||||
|
'refy',
|
||||||
|
'repeatcount',
|
||||||
|
'repeatdur',
|
||||||
|
'restart',
|
||||||
|
'result',
|
||||||
|
'rotate',
|
||||||
|
'scale',
|
||||||
|
'seed',
|
||||||
|
'shape-rendering',
|
||||||
|
'specularconstant',
|
||||||
|
'specularexponent',
|
||||||
|
'spreadmethod',
|
||||||
|
'startoffset',
|
||||||
|
'stddeviation',
|
||||||
|
'stitchtiles',
|
||||||
|
'stop-color',
|
||||||
|
'stop-opacity',
|
||||||
|
'stroke-dasharray',
|
||||||
|
'stroke-dashoffset',
|
||||||
|
'stroke-linecap',
|
||||||
|
'stroke-linejoin',
|
||||||
|
'stroke-miterlimit',
|
||||||
|
'stroke-opacity',
|
||||||
|
'stroke',
|
||||||
|
'stroke-width',
|
||||||
|
'style',
|
||||||
|
'surfacescale',
|
||||||
|
'systemlanguage',
|
||||||
|
'tabindex',
|
||||||
|
'targetx',
|
||||||
|
'targety',
|
||||||
|
'transform',
|
||||||
|
'transform-origin',
|
||||||
|
'text-anchor',
|
||||||
|
'text-decoration',
|
||||||
|
'text-rendering',
|
||||||
|
'textlength',
|
||||||
|
'type',
|
||||||
|
'u1',
|
||||||
|
'u2',
|
||||||
|
'unicode',
|
||||||
|
'values',
|
||||||
|
'viewbox',
|
||||||
|
'visibility',
|
||||||
|
'version',
|
||||||
|
'vert-adv-y',
|
||||||
|
'vert-origin-x',
|
||||||
|
'vert-origin-y',
|
||||||
|
'width',
|
||||||
|
'word-spacing',
|
||||||
|
'wrap',
|
||||||
|
'writing-mode',
|
||||||
|
'xchannelselector',
|
||||||
|
'ychannelselector',
|
||||||
|
'x',
|
||||||
|
'x1',
|
||||||
|
'x2',
|
||||||
|
'xmlns',
|
||||||
|
'y',
|
||||||
|
'y1',
|
||||||
|
'y2',
|
||||||
|
'z',
|
||||||
|
'zoomandpan',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const mathMl = freeze([
|
||||||
|
'accent',
|
||||||
|
'accentunder',
|
||||||
|
'align',
|
||||||
|
'bevelled',
|
||||||
|
'close',
|
||||||
|
'columnsalign',
|
||||||
|
'columnlines',
|
||||||
|
'columnspan',
|
||||||
|
'denomalign',
|
||||||
|
'depth',
|
||||||
|
'dir',
|
||||||
|
'display',
|
||||||
|
'displaystyle',
|
||||||
|
'encoding',
|
||||||
|
'fence',
|
||||||
|
'frame',
|
||||||
|
'height',
|
||||||
|
'href',
|
||||||
|
'id',
|
||||||
|
'largeop',
|
||||||
|
'length',
|
||||||
|
'linethickness',
|
||||||
|
'lspace',
|
||||||
|
'lquote',
|
||||||
|
'mathbackground',
|
||||||
|
'mathcolor',
|
||||||
|
'mathsize',
|
||||||
|
'mathvariant',
|
||||||
|
'maxsize',
|
||||||
|
'minsize',
|
||||||
|
'movablelimits',
|
||||||
|
'notation',
|
||||||
|
'numalign',
|
||||||
|
'open',
|
||||||
|
'rowalign',
|
||||||
|
'rowlines',
|
||||||
|
'rowspacing',
|
||||||
|
'rowspan',
|
||||||
|
'rspace',
|
||||||
|
'rquote',
|
||||||
|
'scriptlevel',
|
||||||
|
'scriptminsize',
|
||||||
|
'scriptsizemultiplier',
|
||||||
|
'selection',
|
||||||
|
'separator',
|
||||||
|
'separators',
|
||||||
|
'stretchy',
|
||||||
|
'subscriptshift',
|
||||||
|
'supscriptshift',
|
||||||
|
'symmetric',
|
||||||
|
'voffset',
|
||||||
|
'width',
|
||||||
|
'xmlns',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const xml = freeze([
|
||||||
|
'xlink:href',
|
||||||
|
'xml:id',
|
||||||
|
'xlink:title',
|
||||||
|
'xml:space',
|
||||||
|
'xmlns:xlink',
|
||||||
|
]);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
||||||
|
import { seal } from './utils.js';
|
||||||
|
|
||||||
|
// eslint-disable-next-line unicorn/better-regex
|
||||||
|
export const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
||||||
|
export const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
||||||
|
export const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
||||||
|
export const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
||||||
|
export const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
||||||
|
export const IS_ALLOWED_URI = seal(
|
||||||
|
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
||||||
|
);
|
||||||
|
export const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
||||||
|
export const ATTR_WHITESPACE = seal(
|
||||||
|
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
||||||
|
);
|
||||||
|
export const DOCTYPE_NAME = seal(/^html$/i);
|
|
@ -0,0 +1,280 @@
|
||||||
|
import { freeze } from './utils.js';
|
||||||
|
|
||||||
|
export const html = freeze([
|
||||||
|
'a',
|
||||||
|
'abbr',
|
||||||
|
'acronym',
|
||||||
|
'address',
|
||||||
|
'area',
|
||||||
|
'article',
|
||||||
|
'aside',
|
||||||
|
'audio',
|
||||||
|
'b',
|
||||||
|
'bdi',
|
||||||
|
'bdo',
|
||||||
|
'big',
|
||||||
|
'blink',
|
||||||
|
'blockquote',
|
||||||
|
'body',
|
||||||
|
'br',
|
||||||
|
'button',
|
||||||
|
'canvas',
|
||||||
|
'caption',
|
||||||
|
'center',
|
||||||
|
'cite',
|
||||||
|
'code',
|
||||||
|
'col',
|
||||||
|
'colgroup',
|
||||||
|
'content',
|
||||||
|
'data',
|
||||||
|
'datalist',
|
||||||
|
'dd',
|
||||||
|
'decorator',
|
||||||
|
'del',
|
||||||
|
'details',
|
||||||
|
'dfn',
|
||||||
|
'dialog',
|
||||||
|
'dir',
|
||||||
|
'div',
|
||||||
|
'dl',
|
||||||
|
'dt',
|
||||||
|
'element',
|
||||||
|
'em',
|
||||||
|
'fieldset',
|
||||||
|
'figcaption',
|
||||||
|
'figure',
|
||||||
|
'font',
|
||||||
|
'footer',
|
||||||
|
'form',
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'head',
|
||||||
|
'header',
|
||||||
|
'hgroup',
|
||||||
|
'hr',
|
||||||
|
'html',
|
||||||
|
'i',
|
||||||
|
'img',
|
||||||
|
'input',
|
||||||
|
'ins',
|
||||||
|
'kbd',
|
||||||
|
'label',
|
||||||
|
'legend',
|
||||||
|
'li',
|
||||||
|
'main',
|
||||||
|
'map',
|
||||||
|
'mark',
|
||||||
|
'marquee',
|
||||||
|
'menu',
|
||||||
|
'menuitem',
|
||||||
|
'meter',
|
||||||
|
'nav',
|
||||||
|
'nobr',
|
||||||
|
'ol',
|
||||||
|
'optgroup',
|
||||||
|
'option',
|
||||||
|
'output',
|
||||||
|
'p',
|
||||||
|
'picture',
|
||||||
|
'pre',
|
||||||
|
'progress',
|
||||||
|
'q',
|
||||||
|
'rp',
|
||||||
|
'rt',
|
||||||
|
'ruby',
|
||||||
|
's',
|
||||||
|
'samp',
|
||||||
|
'section',
|
||||||
|
'select',
|
||||||
|
'shadow',
|
||||||
|
'small',
|
||||||
|
'source',
|
||||||
|
'spacer',
|
||||||
|
'span',
|
||||||
|
'strike',
|
||||||
|
'strong',
|
||||||
|
'style',
|
||||||
|
'sub',
|
||||||
|
'summary',
|
||||||
|
'sup',
|
||||||
|
'table',
|
||||||
|
'tbody',
|
||||||
|
'td',
|
||||||
|
'template',
|
||||||
|
'textarea',
|
||||||
|
'tfoot',
|
||||||
|
'th',
|
||||||
|
'thead',
|
||||||
|
'time',
|
||||||
|
'tr',
|
||||||
|
'track',
|
||||||
|
'tt',
|
||||||
|
'u',
|
||||||
|
'ul',
|
||||||
|
'var',
|
||||||
|
'video',
|
||||||
|
'wbr',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// SVG
|
||||||
|
export const svg = freeze([
|
||||||
|
'svg',
|
||||||
|
'a',
|
||||||
|
'altglyph',
|
||||||
|
'altglyphdef',
|
||||||
|
'altglyphitem',
|
||||||
|
'animatecolor',
|
||||||
|
'animatemotion',
|
||||||
|
'animatetransform',
|
||||||
|
'circle',
|
||||||
|
'clippath',
|
||||||
|
'defs',
|
||||||
|
'desc',
|
||||||
|
'ellipse',
|
||||||
|
'filter',
|
||||||
|
'font',
|
||||||
|
'g',
|
||||||
|
'glyph',
|
||||||
|
'glyphref',
|
||||||
|
'hkern',
|
||||||
|
'image',
|
||||||
|
'line',
|
||||||
|
'lineargradient',
|
||||||
|
'marker',
|
||||||
|
'mask',
|
||||||
|
'metadata',
|
||||||
|
'mpath',
|
||||||
|
'path',
|
||||||
|
'pattern',
|
||||||
|
'polygon',
|
||||||
|
'polyline',
|
||||||
|
'radialgradient',
|
||||||
|
'rect',
|
||||||
|
'stop',
|
||||||
|
'style',
|
||||||
|
'switch',
|
||||||
|
'symbol',
|
||||||
|
'text',
|
||||||
|
'textpath',
|
||||||
|
'title',
|
||||||
|
'tref',
|
||||||
|
'tspan',
|
||||||
|
'view',
|
||||||
|
'vkern',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const svgFilters = freeze([
|
||||||
|
'feBlend',
|
||||||
|
'feColorMatrix',
|
||||||
|
'feComponentTransfer',
|
||||||
|
'feComposite',
|
||||||
|
'feConvolveMatrix',
|
||||||
|
'feDiffuseLighting',
|
||||||
|
'feDisplacementMap',
|
||||||
|
'feDistantLight',
|
||||||
|
'feDropShadow',
|
||||||
|
'feFlood',
|
||||||
|
'feFuncA',
|
||||||
|
'feFuncB',
|
||||||
|
'feFuncG',
|
||||||
|
'feFuncR',
|
||||||
|
'feGaussianBlur',
|
||||||
|
'feImage',
|
||||||
|
'feMerge',
|
||||||
|
'feMergeNode',
|
||||||
|
'feMorphology',
|
||||||
|
'feOffset',
|
||||||
|
'fePointLight',
|
||||||
|
'feSpecularLighting',
|
||||||
|
'feSpotLight',
|
||||||
|
'feTile',
|
||||||
|
'feTurbulence',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// List of SVG elements that are disallowed by default.
|
||||||
|
// We still need to know them so that we can do namespace
|
||||||
|
// checks properly in case one wants to add them to
|
||||||
|
// allow-list.
|
||||||
|
export const svgDisallowed = freeze([
|
||||||
|
'animate',
|
||||||
|
'color-profile',
|
||||||
|
'cursor',
|
||||||
|
'discard',
|
||||||
|
'font-face',
|
||||||
|
'font-face-format',
|
||||||
|
'font-face-name',
|
||||||
|
'font-face-src',
|
||||||
|
'font-face-uri',
|
||||||
|
'foreignobject',
|
||||||
|
'hatch',
|
||||||
|
'hatchpath',
|
||||||
|
'mesh',
|
||||||
|
'meshgradient',
|
||||||
|
'meshpatch',
|
||||||
|
'meshrow',
|
||||||
|
'missing-glyph',
|
||||||
|
'script',
|
||||||
|
'set',
|
||||||
|
'solidcolor',
|
||||||
|
'unknown',
|
||||||
|
'use',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const mathMl = freeze([
|
||||||
|
'math',
|
||||||
|
'menclose',
|
||||||
|
'merror',
|
||||||
|
'mfenced',
|
||||||
|
'mfrac',
|
||||||
|
'mglyph',
|
||||||
|
'mi',
|
||||||
|
'mlabeledtr',
|
||||||
|
'mmultiscripts',
|
||||||
|
'mn',
|
||||||
|
'mo',
|
||||||
|
'mover',
|
||||||
|
'mpadded',
|
||||||
|
'mphantom',
|
||||||
|
'mroot',
|
||||||
|
'mrow',
|
||||||
|
'ms',
|
||||||
|
'mspace',
|
||||||
|
'msqrt',
|
||||||
|
'mstyle',
|
||||||
|
'msub',
|
||||||
|
'msup',
|
||||||
|
'msubsup',
|
||||||
|
'mtable',
|
||||||
|
'mtd',
|
||||||
|
'mtext',
|
||||||
|
'mtr',
|
||||||
|
'munder',
|
||||||
|
'munderover',
|
||||||
|
'mprescripts',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Similarly to SVG, we want to know all MathML elements,
|
||||||
|
// even those that we disallow by default.
|
||||||
|
export const mathMlDisallowed = freeze([
|
||||||
|
'maction',
|
||||||
|
'maligngroup',
|
||||||
|
'malignmark',
|
||||||
|
'mlongdiv',
|
||||||
|
'mscarries',
|
||||||
|
'mscarry',
|
||||||
|
'msgroup',
|
||||||
|
'mstack',
|
||||||
|
'msline',
|
||||||
|
'msrow',
|
||||||
|
'semantics',
|
||||||
|
'annotation',
|
||||||
|
'annotation-xml',
|
||||||
|
'mprescripts',
|
||||||
|
'none',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const text = freeze(['#text']);
|
|
@ -0,0 +1,193 @@
|
||||||
|
const {
|
||||||
|
entries,
|
||||||
|
setPrototypeOf,
|
||||||
|
isFrozen,
|
||||||
|
getPrototypeOf,
|
||||||
|
getOwnPropertyDescriptor,
|
||||||
|
} = Object;
|
||||||
|
|
||||||
|
let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
|
||||||
|
let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
|
||||||
|
|
||||||
|
if (!freeze) {
|
||||||
|
freeze = function (x) {
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seal) {
|
||||||
|
seal = function (x) {
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apply) {
|
||||||
|
apply = function (fun, thisValue, args) {
|
||||||
|
return fun.apply(thisValue, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!construct) {
|
||||||
|
construct = function (Func, args) {
|
||||||
|
return new Func(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrayForEach = unapply(Array.prototype.forEach);
|
||||||
|
const arrayIndexOf = unapply(Array.prototype.indexOf);
|
||||||
|
const arrayPop = unapply(Array.prototype.pop);
|
||||||
|
const arrayPush = unapply(Array.prototype.push);
|
||||||
|
const arraySlice = unapply(Array.prototype.slice);
|
||||||
|
|
||||||
|
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
||||||
|
const stringToString = unapply(String.prototype.toString);
|
||||||
|
const stringMatch = unapply(String.prototype.match);
|
||||||
|
const stringReplace = unapply(String.prototype.replace);
|
||||||
|
const stringIndexOf = unapply(String.prototype.indexOf);
|
||||||
|
const stringTrim = unapply(String.prototype.trim);
|
||||||
|
|
||||||
|
const regExpTest = unapply(RegExp.prototype.test);
|
||||||
|
|
||||||
|
const typeErrorCreate = unconstruct(TypeError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
||||||
|
*
|
||||||
|
* @param {Function} func - The function to be wrapped and called.
|
||||||
|
* @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
|
||||||
|
*/
|
||||||
|
function unapply(func) {
|
||||||
|
return (thisArg, ...args) => apply(func, thisArg, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
||||||
|
*
|
||||||
|
* @param {Function} func - The constructor function to be wrapped and called.
|
||||||
|
* @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
|
||||||
|
*/
|
||||||
|
function unconstruct(func) {
|
||||||
|
return (...args) => construct(func, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add properties to a lookup table
|
||||||
|
*
|
||||||
|
* @param {Object} set - The set to which elements will be added.
|
||||||
|
* @param {Array} array - The array containing elements to be added to the set.
|
||||||
|
* @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
||||||
|
* @returns {Object} The modified set with added elements.
|
||||||
|
*/
|
||||||
|
function addToSet(set, array, transformCaseFunc = stringToLowerCase) {
|
||||||
|
if (setPrototypeOf) {
|
||||||
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
||||||
|
// independent of any properties defined on Object.prototype.
|
||||||
|
// Prevent prototype setters from intercepting set as a this value.
|
||||||
|
setPrototypeOf(set, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let l = array.length;
|
||||||
|
while (l--) {
|
||||||
|
let element = array[l];
|
||||||
|
if (typeof element === 'string') {
|
||||||
|
const lcElement = transformCaseFunc(element);
|
||||||
|
if (lcElement !== element) {
|
||||||
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
||||||
|
if (!isFrozen(array)) {
|
||||||
|
array[l] = lcElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = lcElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set[element] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shallow clone an object
|
||||||
|
*
|
||||||
|
* @param {Object} object - The object to be cloned.
|
||||||
|
* @returns {Object} A new object that copies the original.
|
||||||
|
*/
|
||||||
|
export function clone(object) {
|
||||||
|
const newObject = create(null);
|
||||||
|
|
||||||
|
for (const [property, value] of entries(object)) {
|
||||||
|
if (getOwnPropertyDescriptor(object, property) !== undefined) {
|
||||||
|
newObject[property] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
||||||
|
*
|
||||||
|
* @param {Object} object - The object to look up the getter function in its prototype chain.
|
||||||
|
* @param {String} prop - The property name for which to find the getter function.
|
||||||
|
* @returns {Function} The getter function found in the prototype chain or a fallback function.
|
||||||
|
*/
|
||||||
|
function lookupGetter(object, prop) {
|
||||||
|
while (object !== null) {
|
||||||
|
const desc = getOwnPropertyDescriptor(object, prop);
|
||||||
|
|
||||||
|
if (desc) {
|
||||||
|
if (desc.get) {
|
||||||
|
return unapply(desc.get);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof desc.value === 'function') {
|
||||||
|
return unapply(desc.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object = getPrototypeOf(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallbackValue(element) {
|
||||||
|
console.warn('fallback value for', element);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
// Array
|
||||||
|
arrayForEach,
|
||||||
|
arrayIndexOf,
|
||||||
|
arrayPop,
|
||||||
|
arrayPush,
|
||||||
|
arraySlice,
|
||||||
|
// Object
|
||||||
|
entries,
|
||||||
|
freeze,
|
||||||
|
getPrototypeOf,
|
||||||
|
getOwnPropertyDescriptor,
|
||||||
|
isFrozen,
|
||||||
|
setPrototypeOf,
|
||||||
|
seal,
|
||||||
|
create,
|
||||||
|
// RegExp
|
||||||
|
regExpTest,
|
||||||
|
// String
|
||||||
|
stringIndexOf,
|
||||||
|
stringMatch,
|
||||||
|
stringReplace,
|
||||||
|
stringToLowerCase,
|
||||||
|
stringToString,
|
||||||
|
stringTrim,
|
||||||
|
// Errors
|
||||||
|
typeErrorCreate,
|
||||||
|
// Other
|
||||||
|
lookupGetter,
|
||||||
|
addToSet,
|
||||||
|
// Reflect
|
||||||
|
unapply,
|
||||||
|
unconstruct,
|
||||||
|
};
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
<script type="module" src="./js/global.js"></script>
|
<script type="module" src="./js/global.js"></script>
|
||||||
|
|
||||||
<script src="./js/editor/ace.js"></script>
|
<script async src="./js/utils/purify/purify.min.js"></script>
|
||||||
|
<script async src="./js/editor/ace.js"></script>
|
||||||
|
|
||||||
{% if current_endpoint == "global_config" %}
|
{% if current_endpoint == "global_config" %}
|
||||||
<script type="module" src="./js/global_config.js"></script>
|
<script type="module" src="./js/global_config.js"></script>
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
{% elif current_endpoint == "logs" %}
|
{% elif current_endpoint == "logs" %}
|
||||||
<link rel="stylesheet" type="text/css" href="./css/flatpickr.css" />
|
<link rel="stylesheet" type="text/css" href="./css/flatpickr.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="./css/flatpickr.dark.css" />
|
<link rel="stylesheet" type="text/css" href="./css/flatpickr.dark.css" />
|
||||||
<script type="module" src="./js/utils/flatpickr.js"></script>
|
<script async type="module" src="./js/utils/flatpickr.js"></script>
|
||||||
<script type="module" src="./js/logs.js"></script>
|
<script type="module" src="./js/logs.js"></script>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|
Loading…
Reference in New Issue