@twobirds/microcomponents
Version:
Micro Components Organization Class
516 lines (502 loc) • 17.2 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["MC"] = factory();
else
root["MC"] = factory();
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ // The require scope
/******/ var __webpack_require__ = {};
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
DC: () => (/* binding */ DC),
MC: () => (/* binding */ MC),
McBase: () => (/* binding */ McBase),
McEvent: () => (/* reexport */ McEvent),
addListener: () => (/* binding */ addListener),
autoload: () => (/* binding */ autoload),
removeListener: () => (/* binding */ removeListener)
});
;// ./build/McEvent.js
var isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
let nativeEventNames = new Set();
if (isBrowser()) {
Object.keys(HTMLElement.prototype).filter(key => /^on/.test(key)).forEach(eventName => nativeEventNames.add(eventName));
}
function isDOM(element) {
return element && element instanceof HTMLElement;
}
class McEvent {
#type;
#stopped = false;
#immediateStopped = false;
constructor(type, data = {}, bubble = 'l') {
this.#type = type;
this.data = data;
this.bubble = bubble;
}
get type() {
return this.#type;
}
get stopped() {
return this.#stopped;
}
get immediateStopped() {
return this.#immediateStopped;
}
stopPropagation() {
this.#stopped = true;
}
stopImmediatePropagation() {
this.stopPropagation();
this.#immediateStopped = true;
}
}
function getMicroComponents(elements, search) {
let mcValues = [];
elements.forEach(e => {
if (e) {
if (e instanceof HTMLElement) {
const _mc = e?._mc;
if (!search && _mc) {
mcValues = mcValues.concat(Object.values(e._mc));
}
else if (search && _mc && _mc[search]) {
mcValues.push(_mc[search]);
}
}
}
});
return mcValues;
}
function traverseNodes(node, search, firstonly = true) {
let mcValues = [];
while (node) {
if (node._mc) {
if (!search) {
mcValues = mcValues.concat(Object.values(node._mc));
}
else if (search && node._mc[search]) {
mcValues.push(node._mc[search]);
}
if (firstonly)
return mcValues;
}
node = node.parentNode;
}
return mcValues;
}
;// ./build/MC.js
var MC_isBrowser = new Function('try {return this===window;}catch(e){ return false;}');
let MC_nativeEventNames = new Set();
if (MC_isBrowser()) {
Object.keys(HTMLElement.prototype)
.filter((key) => /^on/.test(key))
.forEach((eventName) => MC_nativeEventNames.add(eventName));
}
function MC_getMicroComponents(elements, search) {
let mcValues = [];
elements.forEach((e) => {
if (e) {
if (e instanceof HTMLElement) {
const _mc = e?._mc;
if (!search && _mc) {
mcValues = mcValues.concat(Object.values(e._mc));
}
else if (search && _mc && _mc[search]) {
mcValues.push(_mc[search]);
}
}
}
});
return mcValues;
}
function MC_traverseNodes(node, search, firstonly = true) {
let mcValues = [];
while (node) {
if (node._mc) {
if (!search) {
mcValues = mcValues.concat(Object.values(node._mc));
}
else if (search && node._mc[search]) {
mcValues.push(node._mc[search]);
}
if (firstonly)
return mcValues;
}
node = node.parentNode;
}
return mcValues;
}
function MC_isDOM(element) {
return element && element instanceof HTMLElement;
}
function addListener(domNode, eventName, handler, capture = false, once = false) {
let options = { once: false };
if (capture) {
options.capture = capture;
}
if (once) {
options.once = once;
}
domNode.addEventListener(eventName, handler, options);
}
function removeListener(domNode, eventName, handler) {
domNode.removeEventListener(eventName, handler);
}
function add(target, key, element = {}) {
let node = target;
if (!node._mc) {
let debug = DC?.debug;
DC.debug = false;
node._mc = new DC(target);
DC.debug = debug;
delete node._mc._mc;
}
const _mc = node._mc;
if (key.length && !_mc[key]) {
_mc[key] = element;
if (target instanceof HTMLElement && !target.getAttribute('_mc')) {
target.setAttribute('_mc', '');
}
}
return element;
}
function remove(target, key) {
if (!key)
return;
const _mc = target?._mc || {};
if (_mc && _mc[key]) {
delete _mc[key];
}
if (!Object.keys(_mc).length &&
target instanceof HTMLElement &&
target.hasAttribute('_mc')) {
target.removeAttribute('_mc');
}
return;
}
function trigger(target, ev, data = {}, bubble = 'l') {
let _mc = target?._mc;
if (!_mc)
return;
let event = ev instanceof McEvent
? new McEvent(ev.type, ev.data, 'l')
: new McEvent(ev, data, 'l'), mcEvent = typeof ev !== 'string' ? ev : new McEvent(ev, data, bubble);
[...Object.values(_mc)].forEach((mc) => {
if (mc?.trigger && typeof mc?.trigger === 'function') {
mc?.trigger(event);
}
});
if (/[ud]/.test(mcEvent.bubble)) {
bubbleEvent(target, mcEvent);
}
return;
}
function init(element) {
if (element.constructor === DC)
return;
setTimeout(() => {
element.trigger('Init');
}, 0);
}
function autoAttachListeners(element) {
if (!MC_isBrowser() || element.constructor === DC)
return;
const target = element.target;
if (target instanceof HTMLElement) {
let that = element;
Object.getOwnPropertyNames(that.constructor.prototype)
.filter((key) => /^on[A-Z]|one[A-Z]/.test(key) &&
typeof that.constructor.prototype[key] === 'function')
.forEach((key) => {
let once = /^one/.test(key), withoutOnOne = key.replace(/^on[e]{0,1}/, ''), nativeEventName = withoutOnOne.toLowerCase(), listener = (ev) => {
that.trigger(withoutOnOne, ev);
};
if (once) {
Object.defineProperty(element, 'on' + withoutOnOne, {
configurable: true,
enumerable: false,
writable: true,
value: (ev) => {
that.constructor.prototype[key].bind(that)(ev);
},
});
}
if (MC_nativeEventNames.has('on' + nativeEventName)) {
addListener(target, nativeEventName, listener, false, once);
}
});
}
}
function bubbleEvent(target, ev) {
if (/[ud]/.test(ev.bubble)) {
setTimeout(() => {
if (ev.bubble.indexOf('l') === -1) {
ev.bubble += 'l';
}
if (ev.bubble.indexOf('u') > -1) {
let parent = MC_traverseNodes(target, '', true)?.[0].target;
if (parent)
DC.trigger(parent, ev);
}
if (ev.bubble.indexOf('d') > -1) {
let targets = new Set(target._mc
.children()
.map((c) => c?.target));
targets.forEach((target) => {
if (target)
DC.trigger(target, ev);
});
}
}, 0);
}
}
class McBase {
#target;
constructor(target, debug = false) {
let that = this, element = that;
this.#target = new WeakRef(target || {});
init(element);
autoAttachListeners(element);
}
get target() {
return this.#target.deref();
}
}
class MC extends McBase {
_mc = {};
constructor(target) {
super(target);
}
static trigger = trigger;
trigger(ev, data = {}, bubble = 'l') {
let that = this;
if (that.constructor === MC || that.constructor === DC) {
let event = ev instanceof McEvent
? new McEvent(ev.type, ev.data, 'l')
: new McEvent(ev, data, 'l'), mcEvent = typeof ev !== 'string'
? ev
: new McEvent(ev, data, bubble);
[...Object.getOwnPropertyNames(that)].forEach((mc) => {
if (that[mc]?.trigger &&
typeof that[mc]?.trigger === 'function') {
that[mc]?.trigger(event);
}
});
if (/[ud]/.test(mcEvent.bubble)) {
bubbleEvent(that.target, mcEvent);
}
return;
}
if (that instanceof MC || that instanceof DC) {
const that = this, mcEvent = typeof ev !== 'string'
? ev
: new McEvent(ev, data, bubble), handlerName = 'on' + mcEvent.type[0].toUpperCase() + mcEvent.type.slice(1);
if (mcEvent.bubble.indexOf('l') > -1) {
if (!mcEvent.immediateStopped &&
typeof that[handlerName] === 'function') {
that[handlerName](mcEvent);
if (that.hasOwnProperty(handlerName)) {
delete that[handlerName];
}
}
}
if (mcEvent.stopped || mcEvent.bubble === 'l') {
return that;
}
if (/[ud]/.test(mcEvent.bubble)) {
bubbleEvent(that.target, mcEvent);
}
}
}
}
class DC extends McBase {
_mc = {};
constructor(target, debug = false) {
super(target);
}
static add = add;
static remove = remove;
static trigger = trigger;
trigger(ev, data = {}, bubble) {
MC.prototype.trigger.call(this, ev, data, bubble);
}
parent(search = '') {
let target = this.target;
if (!MC_isDOM(target))
return [];
return MC_traverseNodes(target.parentNode, search, true);
}
ancestors(search = '') {
let target = this?.target;
if (!MC_isDOM(target))
return [];
return MC_traverseNodes(this.target.parentNode, search, false);
}
children(search = '') {
let target = this?.target;
if (!MC_isDOM(target))
return [];
const id = Math.random().toString().replace('.', '');
const selector = '[_mc]:not([temp_id="' + id + '"] [_mc] [_mc])';
target.setAttribute('temp_id', id);
const myDomElements = target.querySelectorAll(selector);
const mcValues = MC_getMicroComponents([...myDomElements], search);
target.removeAttribute('temp_id');
return mcValues;
}
descendants(search = '') {
let target = this?.target;
if (!MC_isDOM(target))
return [];
const myDomElements = target.querySelectorAll('*[_mc]');
return MC_getMicroComponents([...myDomElements], search);
}
}
const CEs = new Map();
const MCs = new Map();
function makeLoadScript(element) {
const fileName = './' + element.split('-').join('/') + '.js';
const se = document.createElement('script');
se.setAttribute('src', fileName);
se.setAttribute('blocking', 'render');
se.async = true;
se.setAttribute('type', 'module');
se.setAttribute('name', element);
se.setAttribute('loading', '');
addListener(se, 'load', (ev) => {
se.removeAttribute('loading');
se.setAttribute('loaded', '');
});
addListener(se, 'error', (ev) => {
console.error('could not load Custom Element code from', fileName);
});
return se;
}
function onLoadCallback(script) {
setTimeout(function () {
const importFileName = script.getAttribute('src');
const elementName = script.getAttribute('name');
const se = document.createElement('script');
se.async = true;
se.setAttribute('type', 'module');
const code = `
import _ from "${importFileName}";
import { DC } from "./microcomponents.js";
const elements = Array.from(document.querySelectorAll('[_mc*="${elementName}"]')).filter( element => element instanceof HTMLElement );
elements.forEach((element) => {
// add class to MCs repository
autoload.state.MCs.set( '${elementName}', _ );
// add instance to HTMLElement
DC.add(element, _.name, new _(element));
// remove instance name from elements "_mc" attribute
element.setAttribute('_mc', element.getAttribute('_mc').replace('${elementName}', '').replace( /\w\w/g, ' ').trim());
});
setTimeout( () => {
document.body.querySelector('script[name="${elementName}]"')?.remove();
},0);
`;
se.innerHTML = code;
document.body.append(se);
}, 0);
}
function loadUndefinedMicroComponents() {
[...document.querySelectorAll(':not(:defined)')]
.filter((element) => !!element
&& !customElements.get(element.tagName.toLowerCase())
&& !CEs.has(element.tagName.toLowerCase()))
.forEach((el) => {
const elementName = el.tagName.toLowerCase(), fileName = './' + elementName.split('-').join('/') + '.js';
const se = makeLoadScript(elementName);
addListener(se, 'load', () => {
CEs.set(elementName, customElements.get(el.tagName.toLowerCase()));
});
CEs.set(elementName, 'loading');
document.head.append(se);
});
[...document.querySelectorAll('[_mc]:not([_mc=""]')]
.filter((element) => !!element)
.forEach((el) => {
const elements = el.getAttribute('_mc')?.split(' ').filter(m => !!m) || [];
const elementsToBeLoaded = elements.filter(m => !MCs.has(m));
elementsToBeLoaded?.forEach(elementName => {
const se = makeLoadScript(elementName);
addListener(se, 'load', () => onLoadCallback(document.head.querySelector(`script[name="${elementName}"]`)));
MCs.set(elementName, 'loading');
document.head.append(se);
});
const elementsAlreadyLoaded = elements.filter(m => MCs.has(m) && !MCs.get(m).length);
elementsAlreadyLoaded?.forEach(elementName => {
setTimeout(() => {
DC.add(el, elementName, new (autoload.state.MCs.get(elementName))(el));
const newAttribute = el.getAttribute('_mc')
.replace('${elementName}', '')
.replace(/\w\w/g, ' ')
.trim() || '_mc';
el.setAttribute('_mc');
}, 5);
});
});
}
let autoloadEnabled = false;
let observer = null;
function autoload(enable = true) {
if (!enable) {
observer?.disconnect();
observer = null;
return autoloadEnabled;
}
autoloadEnabled = enable;
observer = new MutationObserver(loadUndefinedMicroComponents);
setTimeout(() => {
observer.observe(document.body, { childList: true, subtree: true });
loadUndefinedMicroComponents();
}, 5);
window.autoload = window.autoload || autoload;
return autoloadEnabled;
}
autoload.state = { CEs, MCs };
/******/ return __webpack_exports__;
/******/ })()
;
});