venom/public/theme/admin/js/scripts.js

453 lines
14 KiB
JavaScript

class VUtils {
static makePublic() {
if (VUtils.isUsed) {
return;
}
this.initHandlers();
VUtils.isUsed = true;
console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed");
}
static initHandlers() {
window.$ = this.$;
window.$$ = this.$$;
window.tryCatch = this.tryCatch;
VUtils.nodePrototypes();
}
static $(selector, from) {
from = from || document;
return from.querySelector(selector);
}
static $$(selector, from) {
from = from || document;
return from.querySelectorAll(selector);
}
static tryCatch(data, callback, error) {
data = VUtils.wrap(data, []);
error = error || console.error;
callback = callback || console.log;
try {
callback(...data);
} catch (e) {
error(e);
}
}
static forEach(items, cb, error) {
for (let i = 0; i < items.length; i++) {
VUtils.tryCatch([items[i], i], cb, error);
}
}
static get(valueOne, value) {
return this.wrap(valueOne, value);
}
static mergeKeys(root, target) {
root = root || {};
let keys = Object.keys(root);
for (let key of keys) {
target[key] = root[key];
}
return target;
}
static mergeOptions(target, root) {
root = root || {};
let keys = Object.keys(root);
for (let key of keys) {
target[key] = VUtils.get(root[key], target[key]);
}
return target;
}
static wrap(valueOne, valueTwo) {
let x = typeof valueTwo;
if (!(valueOne instanceof Array) && valueTwo instanceof Array) {
return [valueOne];
}
if (x === 'string' && valueOne instanceof Array) {
return valueOne.join(".");
}
return valueOne === undefined ? valueTwo : valueOne;
}
static nodePrototypes() {
Node.prototype.find = function (selector) {
return this.closest(selector);
};
Node.prototype.createNew = function (tag, options) {
let el = document.createElement(tag);
el.classList.add(...VUtils.get(options.classes, []));
el.id = VUtils.get(options.id, '');
el.innerHTML = VUtils.get(options.content, "");
VUtils.mergeKeys(options.dataset, el.dataset);
if (VUtils.get(options.append, true) === true) {
this.appendChild(el);
}
return el;
};
Node.prototype.addDelegatedEventListener = function (type, aim, callback, err) {
if (!callback || !type || !aim)
return;
this.addMultiListener(type, (event) => {
let target = event.target;
if (event.detail instanceof HTMLElement) {
target = event.detail;
}
if (target instanceof HTMLElement) {
if (target.matches(aim)) {
VUtils.tryCatch([event, target], callback, err);
} else {
const parent = target.find(aim);
if (parent) {
VUtils.tryCatch([event, parent], callback, err);
}
}
}
});
};
Node.prototype.addMultiListener = function (listener, cb, options = {}) {
let splits = listener.split(" ");
for (let split of splits) {
this.addEventListener(split, cb, options);
}
};
}
}
VUtils.makePublic();
class VRipple {
constructor(options = {}) {
if (!VUtils.isUsed) {
throw "VRipply is only with Public VUtils usable!"
}
let self = this;
self.options = JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}');
VUtils.mergeOptions(self.options, options);
if (self.options.selector.indexOf("#") > -1) {
throw "ID's are not allowed as selector!";
}
this.instanceCheck();
this.ripples = [];
requestAnimationFrame(this.initHandler.bind(this));
}
instanceCheck() {
let opts = this.options;
const rawKey = [opts.target, opts.selector, opts.classes.join(".")].join(" ");
VRipple.instances = VRipple.instances || {};
VRipple.instances[rawKey] = this;
}
initHandler() {
let self = this;
let selector = self.options.selector;
let target = $(self.options.target);
target.addDelegatedEventListener('mousedown touchstart', selector, (e, el) => {
let pos = e.touches ? e.touches[0] : e;
let parent = el.parentNode;
let circle = el.createNew('span', self.options);
let bounds = parent.getBoundingClientRect();
let x = pos.clientX - bounds.left;
let y = pos.clientY - bounds.top;
circle.style.top = y + 'px';
circle.style.left = x + 'px';
circle._mouseDown = true;
circle._animationEnded = false;
self.ripples.push(circle);
});
document.body.addDelegatedEventListener('animationend', '.' + VUtils.get(self.options.classes, ''), self.rippleEnd.bind(self))
if (!document.body._vRippleInit) {
document.body.addMultiListener('mouseup touchend mouseleave rippleClose', e => {
let keys = Object.keys(VRipple.instances);
for (let key of keys) {
for (let ripple of VRipple.instances[key].ripples) {
self.rippleEnd.bind(VRipple.instances[key])(e, ripple);
}
}
})
document.body._vRippleInit = true;
}
}
rippleEnd(ev, el) {
const parent = el.parentNode;
if (parent) {
if (ev.type === 'animationend') {
el._animationEnded = true;
} else {
el._mouseDown = false;
}
if (!el._mouseDown && el._animationEnded) {
if (el.classList.contains('to-remove')) {
el.parentNode.removeChild(el);
this.ripples.splice(this.ripples.indexOf(el), 1)
} else {
el.classList.add('to-remove');
}
}
}
}
}
const rippler = new VRipple();
(function () {
window._openVSelect = null;
requestAnimationFrame(e => {
document.body.addEventListener('click', ev => {
if (window._openVSelect && ev.target.closest('v-select') !== window._openVSelect) {
window._openVSelect.toggle(false);
}
})
})
class VSelectElement extends HTMLElement {
constructor() {
super();
let self = this;
self._in = this.attachInternals();
self._in.role = 'select';
self.setAttribute('tabindex', 0);
self.update();
}
static get formAssociated() {
return true;
}
static get observedAttributes() {
return ['required', 'validity'];
}
get required() {
return this.hasAttribute('required');
}
set required(flag) {
this.toggleAttribute('required', Boolean(flag));
}
get name() {
return this.getAttribute('name');
}
set name(val) {
this.toggleAttribute('name', val);
}
get form() {
return this._in.form;
}
get options() {
return $$('v-options v-option', this);
}
get selected() {
return $$('v-options v-option[selected]', this);
}
update() {
let selected = [],
lbl = $('v-label', this),
fd = new FormData();
this.selected.forEach(e => {
selected.push(e.innerText);
fd.append(this.name, e.value);
})
lbl.attributeChangedCallback('value', '', selected.join(", "));
if (this.required && selected.length === 0) {
this._in.setValidity({customError: true}, "Option is needed");
} else {
this._in.setValidity({});
}
this._in.setFormValue(fd);
}
checkValidity() {
return this._in.checkValidity();
}
reportValidity() {
return this._in.reportValidity();
}
toggle(open) {
if (window._openVSelect && open) {
window._openVSelect.toggleSelect(false);
}
const options = $('v-options', this);
if (!open || this.isOpen) {
options.style.maxHeight = '0';
window._openVSelect = false;
this.isOpen = false;
this.update();
} else {
options.focus();
let height = 0,
children = options.children;
for (let i = 0; i < children.length; i++) {
height += children[i].offsetHeight;
}
options.style.maxHeight = height + 'px';
window._openVSelect = this;
this.isOpen = true;
}
}
}
class VSelectOptionElement extends HTMLElement {
constructor() {
super();
this._in = this.attachInternals();
this._in.role = 'option';
this.addEventListener('click', e => {
let parent = this.parentNode.parentNode,
select = !this.selected;
if (!parent.hasAttribute('multiple')) {
parent.toggle(false);
for (let item of parent.selected) {
if (item !== this) {
item.removeAttribute('selected');
}
}
}
if (!this.disabled) {
this.attributeChangedCallback('selected', false, select, true);
this.parentNode.parentNode.update();
}
});
}
static get observedAttributes() {
return ['selected', 'disabled', 'value'];
}
attributeChangedCallback(name, oldValue, newValue, force) {
if (name === 'selected' && this.hasAttribute('disabled')) {
this.removeAttribute(name);
return;
}
if (name === 'disabled' && newValue === true && this.hasAttribute('selected')) {
this.attributeChangedCallback('selected', false, false);
}
if (force) {
if (newValue) {
this.setAttribute(name, newValue);
} else {
this.removeAttribute(name);
}
}
this[name] = newValue;
}
}
class VLabel extends HTMLElement {
constructor() {
super();
this.empty = this.getAttribute('empty') || "";
this.innerHTML = this.getAttribute("value") || this.empty;
this.addEventListener('click', this.openPopUp.bind(this));
}
static get observedAttributes() {
return ['empty', 'value'];
}
openPopUp() {
this.parentNode.toggle(true);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.innerHTML = newValue || this.empty;
}
this[name] = newValue;
}
}
customElements.define("v-label", VLabel);
customElements.define("v-option", VSelectOptionElement);
customElements.define("v-select", VSelectElement);
})();
class FormHandler {
constructor(selector, parent, cb, err) {
this.cb = cb || console.log;
this.err = err || console.err;
$(parent).addDelegatedEventListener('submit', selector, this.handleEvent.bind(this));
}
handleEvent(e, el) {
e.preventDefault();
if (el.checkValidity()) {
const url = el.action ?? '';
if (url === '') {
console.error("No URL Found on Form", el);
return;
}
fetch(el.action, {
method: el.method.toUpperCase(),
credentials: 'same-origin',
body: new FormData(el),
redirect: 'manual'
}).then(res => {
if (!res.ok) {
throw new Error('Network response errored');
}
return res.json()
}).then(ev => this.cb(ev, el)).catch(ev => this.err(ev, el));
} else {
VUtils.forEach($$('input', el), ele => {
if (!ele.checkValidity()) {
let parent = ele.parentNode;
parent.classList.remove('valid');
parent.classList.add('invalid');
}
});
}
}
}
(function () {
const body = $('body');
body.addDelegatedEventListener('change input', 'input', (e, el) => {
let errorMessage = $('.error-message', el.find('form'));
if (errorMessage) {
errorMessage.classList.add('hide')
}
let parent = el.parentNode;
if (el.value === "") {
parent.classList.remove('focus')
} else {
parent.classList.add('focus')
}
if (el.checkValidity()) {
parent.classList.add('valid');
parent.classList.remove('invalid');
} else {
parent.classList.remove('valid');
parent.classList.add('invalid');
}
})
if ($('#login')) {
new FormHandler('form#login', 'body', () => {
location.reload();
}, (e, el) => {
$('.error-message', el).classList.remove('hide');
})
}
})();