250 lines
8 KiB
JavaScript
250 lines
8 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();
|
|
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(this.cb).catch(this.err);
|
|
}
|
|
}
|
|
}
|
|
(function () {
|
|
const body = $('body');
|
|
body.addDelegatedEventListener('change input', 'input', (e, el) => {
|
|
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', e => {
|
|
console.log(e);
|
|
})
|
|
}
|
|
})() |