icinga-php-library/asset/js/notjQuery.js
2023-04-12 11:00:54 +00:00

162 lines
5.0 KiB
JavaScript

define(function () {
"use strict";
class notjQuery {
/**
* Create a new notjQuery object
*
* @param {Element} element
*/
constructor(element) {
if (! element) {
throw new Error("Can't create a notjQuery object for `" + element + "`");
}
this.element = element;
}
/**
* Add an event listener to the element
*
* @param {string} type
* @param {string} selector
* @param {function} handler
* @param {object} context
*/
on(type, selector, handler, context = null) {
if (typeof selector === 'function') {
context = handler;
handler = selector;
selector = null;
}
if (selector === null) {
this.element.addEventListener(type, e => {
if (type === 'focusin' && e.target.receivesCustomFocus) {
// Ignore native focus event if a custom one follows
if (e instanceof FocusEvent) {
delete e.target.receivesCustomFocus;
e.stopImmediatePropagation();
return;
}
}
if (context === null) {
handler.apply(e.currentTarget, [e]);
} else {
handler.apply(context, [e]);
}
});
} else {
this.element.addEventListener(type, e => {
if (type === 'focusin' && e.target.receivesCustomFocus) {
// Ignore native focus event if a custom one follows
if (e instanceof FocusEvent) {
delete e.target.receivesCustomFocus;
e.stopImmediatePropagation();
return;
}
}
Object.defineProperty(e, 'currentTarget', { value: e.currentTarget, writable: true });
let currentParent = e.currentTarget.parentNode;
for (let target = e.target; target && target !== currentParent; target = target.parentNode) {
if (target.matches(selector)) {
e.currentTarget = target;
if (context === null) {
handler.apply(target, [e]);
} else {
handler.apply(context, [e]);
}
break;
}
}
}, false);
}
}
/**
* Trigger a custom event on the element, asynchronously
*
* The event will bubble and is not cancelable.
*
* @param {string} type
* @param {{}} detail
*/
trigger(type, detail = null) {
setTimeout(() => {
this.element.dispatchEvent(new CustomEvent(type, {
cancelable: true, // TODO: this should depend on whether it's a native or custom event
bubbles: true,
detail: detail
}));
}, 0);
}
/**
* Focus the element
*
* Any other option than `preventScroll` is used as `event.detail`.
*
* @param {{}} options
*/
focus(options = {}) {
let { preventScroll = false, ...data } = options;
const hasData = Object.keys(data).length > 0;
if (hasData) {
this.element.receivesCustomFocus = true;
}
// Put separately on the event loop because focus() forces layout.
setTimeout(() => this.element.focus({ preventScroll: preventScroll }), 0);
if (hasData) {
this.trigger('focusin', data);
}
}
/**
* Render the element string as DOM Element
*
* @param {string} html
* @return {Element}
*/
static render(html) {
if (typeof html !== 'string') {
throw new Error("Can\'t render `" + html + "`");
}
let template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
}
/**
* Return a notjQuery object for the given element
*
* @param {Element} element
* @return {notjQuery}
*/
let factory = function (element) {
return new notjQuery(element);
}
// Define the static methods on the factory
for (let name of Object.getOwnPropertyNames(notjQuery)) {
if (['length', 'prototype', 'name'].includes(name)) {
continue;
}
Object.defineProperty(factory, name, {
value: notjQuery[name]
});
}
return factory;
});