/*! Client lib for manipulating on DOM elements. Author: CrazyDoctor (Oleg Karataev) */ function isEmpty(val) { return val === null || val === undefined || val === '' || val.length === 0; } function isJsonString(str) { try { JSON.parse(str); } catch (e) { return false; } return true; } 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.cdelement) return el.cdelement; return el.cdelement = new CDElement(el); } 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 (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 (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; } getParent() { return CDElement.get(this.get().parentElement); } getValue() { return this.get().value || this.getInnerHTML(); } 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 = 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 (isEmpty(size)) return this; this.get().style.height = size + 'px'; return this; } expandWidth(size){ if (isEmpty(size)) return this; this.get().style.width = size + 'px'; return this; } isMinimized(el) { return isEmpty(this.get().style.height); } isDisabled(el) { return isEmpty(this.get().style.display); } setId(id) { if (isEmpty(id)) return this; this.get().id = id; return id; } nextSibling() { return CDElement.get(this.get().nextElementSibling); } addClass(cls) { if (isEmpty(cls)) return this; cls.split(' ').forEach((c) => { if(c.length > 0) this.get().classList.add(c); }); return this; } getClass() { if (isEmpty(this.get().classList)) return ''; let classList = ''; this.get().classList.forEach((cls) => { classList += cls + " "; }); return classList.trim(); } removeClass(cls) { if (isEmpty(cls)) return this; this.get().classList.remove(cls); return this; } hasClass(cls) { if (isEmpty(this.get().classList)) return false; let has = false; this.get().classList.forEach((c) => { if (c === cls) has = true; }); return has; } removeClass(cls) { if (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 (isEmpty(prop)) return this; this.get().style.setProperty(prop, ''); return this; } setAttribute(attr, value) { this.get().setAttribute(attr, isEmpty(value) ? '' : value); return this; } getAttribute(attr) { return this.get().getAttribute(attr); } setInnerHTML(value) { this.get().innerHTML = 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(); } style(property, value, priority) { this.get().style.setProperty(property, value, priority); return this; } on(event, callback) { if (isEmpty(event) || isEmpty(callback)) return this; this.get().addEventListener(event, callback); return this; } un(event, callback) { if (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 goTo(url) { window.location = url; } 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; } 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() { history.replaceState(null, null, this.toLocalString()); } } 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' }; static Tags = { Div: 'div', Span: 'span', H1: 'h1', H2: 'h2', H3: 'h3', P: 'p' }; static get(selector) { if (isEmpty(selector)) throw "DOM.get() invalid selector."; const element = document.querySelector(selector); if (isEmpty(element)) return null; return CDElement.get(element); } static getAll(selector) { if (isEmpty(selector)) throw "DOM.getAll() invalid selector."; const elements = document.querySelectorAll(selector); if(isEmpty(elements)) return []; return Array.from(elements).map((element) => CDElement.get(element)); } static create(config, container, prepend) { if (isEmpty(config) || isEmpty(config.tag)) return; const element = CDElement.get(document.createElement(config.tag)); if (!isEmpty(config.attr)) { Object.keys(config.attr).forEach((name) => { element.setAttribute(name, config.attr[name]); }); } if (!isEmpty(config.cls)) element.addClass(config.cls); if (!isEmpty(config.id)) element.setId(config.id); if (!isEmpty(config.style)) Style.apply(element, config.style); if (!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 (!isEmpty(config.innerHTML)) element.setInnerHTML(element.getInnerHTML() + config.innerHTML); if (!isEmpty(container)) (prepend === true ? container.prepend(element) : container.append(element)); return element; } static append(container, element) { if (isEmpty(element) || 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(isEmpty(cookieValue)) return null; if(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); } } } class CDUtils { /** * 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); } static toHtml(str) { return str.replace(/(?:\r\n|\r|\n)/g, '
'); } } const window_ = CDElement.get(window);