// Utilities for Photonic
export const hasClass = (element, className) => {
if (element.classList) {
return element.classList.contains(className);
}
else {
return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className);
}
};
function ajax(method, url, args, callback) {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
const data = xhr.responseText;
callback(data);
}
}
};
let form = new FormData();
for (const [key, value] of Object.entries(args)) {
form.append(key, value);
}
xhr.send(form);
}
export const post = (url, args, callback) => {
ajax('POST', url, args, callback);
};
export const get = (url, args, callback) => {
ajax('GET', url, args, callback);
};
export const next = (elem, selector) => {
let sibling = elem.nextElementSibling;
if (!selector) return sibling;
while (sibling) {
if (sibling.matches(selector)) return sibling;
sibling = sibling.nextElementSibling;
}
};
export const getElement = value => {
const parser = new DOMParser();
const doc = parser.parseFromString(value, 'text/html');
return doc.body;
};
export const getText = value => {
// Not using innerHTML because of vulnerability to XSS
if (value == null) {
return '';
}
return value.replace(/<[^>]+>/g, '');
};
export const slideUpDown = (element, state) => {
if (element != null && element.classList) {
if (!element.classList.contains('photonic-can-slide')) {
element.classList.add('photonic-can-slide');
}
if ('show' === state) {
element.classList.remove('photonic-can-slide-hide');
element.style.height = `${element.scrollHeight}px`;
}
else {
element.classList.add('photonic-can-slide-hide');
element.style.height = 0
}
}
};
export const slideUpTitle = (element, state) => {
if (element && element.classList) {
if ('show' === state) {
let currentPadding = 0;
if (element.offsetHeight) {
currentPadding = parseInt(getComputedStyle(element).paddingTop.slice(0, -2)) * 2;
}
element.style.height = (element.scrollHeight + 6 - currentPadding) + 'px';
element.classList.add('slideup-show');
}
else {
element.style.height = '';
element.classList.remove('slideup-show');
}
}
}
export const fadeIn = (el) => {
if (!hasClass(el, 'fade-in')) {
el.style.display = 'block';
el.style.visibility = 'visible';
el.classList.add('fade-in');
}
}
export const fadeOut = (el, duration) => {
let s = el.style,
step = 25/(duration || 500);
s.opacity = s.opacity || 1;
(function fade() {
s.opacity -= step;
if (s.opacity < 0) {
s.display = "none";
el.classList.remove('fade-in');
}
else {
setTimeout(fade, 25);
}
})();
}
// get the default display style of an element
const defaultDisplay = tag => {
const iframe = document.createElement('iframe');
iframe.setAttribute('frameborder', 0);
iframe.setAttribute('width', 0);
iframe.setAttribute('height', 0);
document.documentElement.appendChild(iframe);
const doc = (iframe.contentWindow || iframe.contentDocument).document;
// IE support
doc.write();
doc.close();
const testEl = doc.createElement(tag);
doc.documentElement.appendChild(testEl);
const display = (window.getComputedStyle ? getComputedStyle(testEl, null) : testEl.currentStyle).display;
iframe.parentNode.removeChild(iframe);
return display;
};
// actual show/hide function used by show() and hide() below
const showHide = (el, show) => {
let value = el.getAttribute('data-olddisplay'),
display = el.style.display,
computedDisplay = (window.getComputedStyle ? getComputedStyle(el, null) : el.currentStyle).display;
if (show) {
if (!value && display === 'none') el.style.display = '';
if (el.style.display === '' && (computedDisplay === 'none')) value = value || defaultDisplay(el.nodeName);
}
else {
if (display && display !== 'none' || !(computedDisplay === 'none'))
el.setAttribute('data-olddisplay', (computedDisplay === 'none') ? display : computedDisplay);
}
if (!show || el.style.display === 'none' || el.style.display === '')
el.style.display = show ? value || '' : 'none';
};
// helper functions
export const show = (el) => showHide(el, true);
export const hide = (el) => showHide(el);
//JavaScript HTML Sanitizer v2.0.3, (c) Alexander Yumashev, Jitbit Software.
//homepage https://github.com/jitbit/HtmlSanitizer
//License: MIT https://github.com/jitbit/HtmlSanitizer/blob/master/LICENSE
export const HTMLSanitizer = new (function () {
const _tagWhitelist = {
'A': true, 'ABBR': true, 'B': true, 'BLOCKQUOTE': true, 'BODY': true, 'BR': true, 'CENTER': true, 'CODE': true, 'DD': true, 'DIV': true, 'DL': true, 'DT': true, 'EM': true, 'FONT': true,
'H1': true, 'H2': true, 'H3': true, 'H4': true, 'H5': true, 'H6': true, 'HR': true, 'I': true, 'IMG': true, 'LABEL': true, 'LI': true, 'OL': true, 'P': true, 'PRE': true,
'SMALL': true, 'SOURCE': true, 'SPAN': true, 'STRONG': true, 'SUB': true, 'SUP': true, 'TABLE': true, 'TBODY': true, 'TR': true, 'TD': true, 'TH': true, 'THEAD': true, 'UL': true, 'U': true, 'VIDEO': true
};
const _contentTagWhiteList = { 'FORM': true, 'GOOGLE-SHEETS-HTML-ORIGIN': true }; //tags that will be converted to DIVs
const _attributeWhitelist = { 'align': true, 'color': true, 'controls': true, 'height': true, 'href': true, 'id': true, 'src': true, 'style': true, 'target': true, 'title': true, 'type': true, 'width': true };
const _cssWhitelist = { 'background-color': true, 'color': true, 'font-size': true, 'font-weight': true, 'text-align': true, 'text-decoration': true, 'width': true };
const _schemaWhiteList = [ 'http:', 'https:', 'data:', 'm-files:', 'file:', 'ftp:', 'mailto:', 'pw:' ]; //which "protocols" are allowed in "href", "src" etc
const _uriAttributes = { 'href': true, 'action': true };
const _parser = new DOMParser();
this.SanitizeHTML = (input, extraSelector) => {
if (input == null) return null;
input = input.trim();
if (input === "") return ""; //to save performance
//firefox "bogus node" workaround for wysiwyg's
if (input === "
") return "";
if (input.indexOf("