/*! Client lib for manipulating on DOM elements. Author: CrazyDoctor (Oleg Karataev) */ class CDElement { constructor(el) { this.element = el; el.cdelement = this; try { this.events = Object.keys(getEventListeners(el)); } catch(e) { this.events = []; } } static get(el) { if(el == null) return null; if(el instanceof CDElement) return el; if(el.cdelement) return el.cdelement; if(el instanceof Element || el instanceof Window) return el.cdelement = new CDElement(el); throw 'CDElement.get() error'; } get() { return this.element; } copy() { return new CDElement(this.get().cloneNode(true)); } getFirstChild(selector) { const children = Array.from(this.get().children); if (children.length == 0) return null; if (CDUtils.isEmpty(selector)) return CDElement.get(children[0]); const child = this.get().querySelector(selector); if(child) return CDElement.get(child); return null; } hasChildren() { return Array.from(this.get().children).length > 0; } getChildren(selector) { if (CDUtils.isEmpty(selector)) return Array.from(this.get().children).map((element) => CDElement.get(element)); return Array.from(this.get().querySelectorAll(selector)).map((element) => CDElement.get(element)); } getChildrenRecursive() { let children = this.getChildren(); for(const child of children) { if(this.hasChildren()) children = children.concat(child.getChildrenRecursive()); } return children; } getTag() { return this.get().tagName.toLowerCase(); } getParent() { return CDElement.get(this.get().parentElement); } getValue() { return this.get().value || this.getInnerHTML(); } setValue(value) { this.get().value = value; return this; } append(element) { this.get().append(element.get()); } prepend(element) { this.get().prepend(element.get()); } remove() { this.get().remove(); } disable() { this.removeCssProperty('display'); this.addClass('disabled'); return this; } enable(display) { this.removeClass('disabled'); this.get().style.display = CDUtils.isEmpty(display) ? 'block' : display; return this; } minimizeHeight() { this.get().style.height = '0px'; return this; } minimizeWidth() { this.get().style.width = '0px'; return this; } expandHeight(size) { if (CDUtils.isEmpty(size)) return this; this.get().style.height = size + 'px'; return this; } expandWidth(size){ if (CDUtils.isEmpty(size)) return this; this.get().style.width = size + 'px'; return this; } isMinimized(el) { return CDUtils.isEmpty(this.get().style.height); } isDisabled(el) { return CDUtils.isEmpty(this.get().style.display); } setId(id) { if (CDUtils.isEmpty(id)) return this; this.get().id = id; return id; } previousSibling() { return CDElement.get(this.get().previousElementSibling); } nextSibling() { return CDElement.get(this.get().nextElementSibling); } addClass(cls) { if (CDUtils.isEmpty(cls)) return this; cls.split(' ').forEach((c) => { if(c.length > 0 && !this.hasClass(c)) this.get().classList.add(c); }); return this; } getClass() { if (CDUtils.isEmpty(this.get().classList)) return ''; let classList = ''; this.get().classList.forEach((cls) => { classList += cls + " "; }); return classList.trim(); } removeClass(cls) { if (CDUtils.isEmpty(cls)) return this; this.get().classList.remove(cls); return this; } hasClass(cls) { if (CDUtils.isEmpty(this.get().classList)) return false; let has = false; this.get().classList.forEach((c) => { if (c === cls) has = true; }); return has; } removeClass(cls) { if (CDUtils.isEmpty(cls)) return this; this.get().classList.remove(cls); return this; } switchClass(cls, condition) { if(condition != null) return condition ? this.addClass(cls) : this.removeClass(cls); return this.hasClass(cls) ? this.removeClass(cls) : this.addClass(cls); } removeCssProperty(prop) { if (CDUtils.isEmpty(prop)) return this; this.get().style.setProperty(prop, ''); return this; } setAttribute(attr, value) { this.get().setAttribute(attr, CDUtils.isEmpty(value) ? '' : value); return this; } getAttribute(attr) { return this.get().getAttribute(attr); } setInnerHTML(value) { this.get().innerHTML = CDUtils.isEmpty(value) ? '' : value; return this; } getInnerHTML() { return this.get().innerHTML; } getInnerWidth() { return this.get().innerWidth; } getInnerHeight() { return this.get().innerHeight; } getOffsetTop() { return this.get().offsetTop; } scrollTo(selector) { const child = this.getFirstChild(selector); if(child) this.get().scrollTop = child.getOffsetTop() - this.getOffsetTop(); } scrollIntoView() { this.get().scrollIntoView(); return this; } style(property, value, priority) { this.get().style.setProperty(property, value, priority); return this; } focus() { this.get().focus(); return this; } blur() { this.get().blur(); return this; } click() { this.get().click(); return this; } on(event, callback) { if (CDUtils.isEmpty(event) || CDUtils.isEmpty(callback)) return this; this.get().addEventListener(event, callback); return this; } un(event, callback) { if (CDUtils.isEmpty(event)) return this; this.get().removeEventListener(event, callback); return this; } } class Url { constructor(url) { this.url = new URL(url || location); this.urlSearch = new URLSearchParams(this.url.search); } static getHash() { return new Url().getHash(); } static setHash(hash) { return new Url().setHash(hash); } static getOrigin() { return new Url().getOrigin(); } static goTo(url, blank) { window.open(url, blank ? '_blank' : '_self'); } static reload() { location.reload(); } static getPath() { return new Url().getPath(); } static getFullPath() { const url = new Url().url; return `${url.origin}${url.pathname}`; } getHash() { const hash = this.url.hash.substring(1); return hash.length > 0 ? hash : null; } setHash(hash) { this.url.hash = !hash || hash.length == 0 ? '' : `#${hash}`; return this; } setSearchParams(params) { const paramsArr = []; for(const key of Object.keys(params)) { paramsArr.push(`${key}=${params[key]}`); } this.url.search = paramsArr.join('&'); } getSearchParams() { const params = {}; Array.from(this.url.searchParams).forEach((pair) => { params[pair[0]] = pair[1]; }); return params; } setPath(path) { this.url.pathname = path; return this; } getPath() { return this.url.pathname; } getOrigin() { return this.url.origin; } getProtocol() { return this.url.protocol; } setProtocol(protocol) { this.url.protocol = protocol; return this; } toString() { this.url.search = this.urlSearch.toString(); return this.url.toString(); } toLocalString() { this.url.search = this.urlSearch.toString(); return this.toString().substring(this.url.origin.length); } updateLocation() { const hashChanged = Url.getHash() !== this.getHash(); history.replaceState(null, null, this.toLocalString()); hashChanged && window.dispatchEvent(new HashChangeEvent('hashchange')); } } class Style { static apply(element, styleStr) { element = element instanceof CDElement ? element : CDElement.get(element); const propertiesMap = this.getCssPropertiesMap(styleStr); for(const prop of Object.keys(propertiesMap)) element.style(prop, propertiesMap[prop].value, propertiesMap[prop].priority); } static getCssPropertiesMap(styleStr) { const parts = styleStr.split(';'); const map = {}; for(let part of parts) { part = part.trim(); if(part.length == 0) continue; const propVal = part.split(':'); const property = propVal[0].trim(); const value = propVal[1].trim().split('!'); map[property] = { value: value[0], priority: value.length > 1 ? value[1] : '' }; } return map; } } class DOM { static Events = { Click: 'click', Load: 'load', KeyDown: 'keydown', KeyUp: 'keyup', KeyPress: 'keypress', Change: 'change', Cut: 'cut', Drop: 'drop', Paste: 'paste', Input: 'input', HashChange: 'hashchange', MouseDown: 'mousedown', ContextMenu: 'contextmenu', Blur: 'blur' }; static Tags = { A: 'a', Div: 'div', Span: 'span', H1: 'h1', H2: 'h2', H3: 'h3', P: 'p', Textarea: 'textarea', Input: 'input', Table: 'table', Tr: 'tr', Th: 'th', Tbody: 'tbody', Td: 'td', Select: 'select', Option: 'option' }; static Keys = { Enter: 'Enter', Escape: 'Escape', Control: 'Control', Shift: 'Shift', Backspace: 'Backspace' }; static MouseButtons = { Left: 1, Right: 2, Middle: 4 }; static get(selector) { if (CDUtils.isEmpty(selector)) throw "DOM.get() invalid selector."; const element = document.querySelector(selector); if (CDUtils.isEmpty(element)) return null; return CDElement.get(element); } static getAll(selector) { if (CDUtils.isEmpty(selector)) throw "DOM.getAll() invalid selector."; const elements = document.querySelectorAll(selector); if(CDUtils.isEmpty(elements)) return []; return Array.from(elements).map((element) => CDElement.get(element)); } static create(config, container, prepend) { if (CDUtils.isEmpty(config) || CDUtils.isEmpty(config.tag)) return; const element = CDElement.get(document.createElement(config.tag)); if (!CDUtils.isEmpty(config.attr)) { Object.keys(config.attr).forEach((name) => { if(config.attr[name] !== undefined) element.setAttribute(name, config.attr[name]); }); } if (!CDUtils.isEmpty(config.cls)) element.addClass(config.cls); if (!CDUtils.isEmpty(config.id)) element.setId(config.id); if (!CDUtils.isEmpty(config.style)) Style.apply(element, config.style); if (!CDUtils.isEmpty(config.cn)) { config.cn.forEach((el) => { if (el instanceof Element) { element.append(CDElement.get(el)); } else if (el instanceof CDElement) { element.append(el); } else this.create(el, element); }); } // innerHTML appends after cn if (!CDUtils.isEmpty(config.innerHTML)) element.setInnerHTML(element.getInnerHTML() + config.innerHTML); if (!CDUtils.isEmpty(container)) (prepend === true ? container.prepend(element) : container.append(element)); return element; } static append(container, element) { if (CDUtils.isEmpty(element) || CDUtils.isEmpty(container) || (!(element instanceof Element) && !(element instanceof CDElement)) || (!(container instanceof Element) && !(container instanceof CDElement))) return; (container instanceof CDElement ? container.get() : container).append((element instanceof CDElement ? element.get() : element)); } static setTitle(title) { document.title = title; } static setCookie(name, value, hours) { document.cookie = name + "=" + JSON.stringify(value) + "; path=/; expires=" + (new Date(Date.now() + hours * 3600000).toGMTString()); } static getCookie(name) { const cookies = {}; document.cookie.split(';').forEach(function(el) { const [key, value] = el.split('='); cookies[key.trim()] = value; }); const cookieValue = cookies[name]; if(CDUtils.isEmpty(cookieValue)) return null; if(CDUtils.isJsonString(cookieValue)) return JSON.parse(cookieValue); else return cookieValue; } static getCookieProperty(name, property) { const cookie = DOM.getCookie(name); if(cookie && !(cookie instanceof Object)) throw 'DOM.getCookieProperty(): cookie value is not a JSON'; return cookie ? cookie[property] : null; } static setCookieProperty(name, property, value, hours) { const cookie = DOM.getCookie(name); if(cookie) { if(!(cookie instanceof Object)) throw 'DOM.setCookieProperty(): initial cookie value is not a JSON'; cookie[property] = value; DOM.setCookie(name, cookie, hours || 24); } else { DOM.setCookie(name, { [property]: value }, hours || 24); } } static documentOn(event, callback) { if (CDUtils.isEmpty(event) || CDUtils.isEmpty(callback)) return; document.addEventListener(event, callback); } static copyToClipboard(str) { const body = DOM.get('body'); const input = DOM.create({ tag: DOM.Tags.Input, style: 'display: none;' }, body); input.setValue(str); input.get().select(); input.get().setSelectionRange(0, 99999); navigator.clipboard.writeText(input.get().value).then(() => { input.remove(); }); } } class CDUtils { static isEmpty(val) { return val === null || val === undefined || val === '' || val.length === 0; } static isJsonString(str) { try { JSON.parse(str); } catch (e) { return false; } return true; } static nl2br(str) { return str.replaceAll(/(?:\r\n|\r|\n)/g, '
'); } static br2nl(str) { return str.replaceAll(//g, '\n'); } static async SHA256(input) { return crypto.subtle.digest('SHA-256', new TextEncoder('utf8').encode(input)).then(h => { const hexes = [], view = new DataView(h); for (let i = 0; i < view.byteLength; i += 4) hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8)); return hexes.join(''); }); } /** * Returns string representation of x with leading zeros.
* Length of the resulting string will be equal to length or 2 if length was not specified.
* If length of the initial number is greater or equal to length parameter, nothing will be changed. *

* Examples:
CDUtils.pad(5) -> "05"
CDUtils.pad(21) -> "21"
CDUtils.pad(21, 3) -> "021" * @param {number|String} x * @param {number} [length] * @return string */ static pad(x, length) { const count = length != null && length >= 2 ? length : 2; const str = x.toString(); const diff = count - str.length; return `${"0".repeat(diff < 0 ? 0 : diff)}${str}`; } /** * Returns formatted date.
* H - hours
I - minutes
S - seconds
s - milliseconds
D - day
M - month
Y - year (0000)
y - year (00) * @param {number} timestamp * @param {string} format * @return string */ static dateFormat(timestamp, format) { const date = new Date(timestamp); const day = date.getDate(); const month = date.getMonth()+1; const year = date.getFullYear(); const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); const mseconds = date.getMilliseconds(); return format.replaceAll('H', CDUtils.pad(hours.toString())) .replaceAll('I', CDUtils.pad(minutes.toString())) .replaceAll('S', CDUtils.pad(seconds.toString())) .replaceAll('s', CDUtils.pad(mseconds.toString())) .replaceAll('D', CDUtils.pad(day.toString())) .replaceAll('M', CDUtils.pad(month.toString())) .replaceAll('Y', CDUtils.pad(year.toString(), 4)) .replaceAll('y', CDUtils.pad((year % 100).toString())); } static dateFormatUTC(date, timeZoneOffsetHours, format) { date = date instanceof Date ? date : new Date(date); date.setUTCHours(date.getUTCHours() + timeZoneOffsetHours); var year = date.getUTCFullYear(); var month = date.getUTCMonth() + 1; var day = date.getUTCDate(); var hours = date.getUTCHours(); var minutes = date.getUTCMinutes(); var seconds = date.getUTCSeconds(); return format.replaceAll('H', CDUtils.pad(hours.toString())) .replaceAll('I', CDUtils.pad(minutes.toString())) .replaceAll('S', CDUtils.pad(seconds.toString())) .replaceAll('D', CDUtils.pad(day.toString())) .replaceAll('M', CDUtils.pad(month.toString())) .replaceAll('Y', CDUtils.pad(year.toString(), 4)) .replaceAll('y', CDUtils.pad((year % 100).toString())) + ` UTC${timeZoneOffsetHours > 0 ? '+' : '-'}${timeZoneOffsetHours}`; } static repeat(str, n) { let res = ''; for(let i = 0; i < n; i++) res += str; return res; } static randInt(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); } static arrayShuffle(unshuffled) { return unshuffled .map(value => ({ value, sort: Math.random() })) .sort((a, b) => a.sort - b.sort) .map(({ value }) => value); } } const window_ = CDElement.get(window);