UNPKG

@cimo/jsmvcfw

Version:

Javascript mvc framework. Light, fast and secure.

351 lines 14.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.resetFramework = exports.elementObserverOn = exports.elementObserverOff = exports.elementObserver = exports.variableBind = exports.variableHook = exports.renderAfter = exports.renderTemplate = exports.getControllerList = void 0; const JsMvcFwDom_1 = require("./JsMvcFwDom"); const JsMvcFwEmitter_1 = __importDefault(require("./JsMvcFwEmitter")); const virtualNodeObject = {}; const renderTriggerObject = {}; const variableLoadedList = {}; const variableEditedList = {}; const variableRenderUpdateObject = {}; const variableHookObject = {}; const controllerList = []; let cacheVariableProxyWeakMap = new WeakMap(); const emitterObject = {}; let observerWeakMap = new WeakMap(); let callbackObserverWeakMap = new WeakMap(); const variableRenderUpdate = (controllerName) => { if (emitterObject[controllerName] && !variableRenderUpdateObject[controllerName]) { variableRenderUpdateObject[controllerName] = true; Promise.resolve().then(() => { const renderTrigger = renderTriggerObject[controllerName]; if (renderTrigger) { renderTrigger(); } emitterObject[controllerName].emit("variableChanged"); variableRenderUpdateObject[controllerName] = false; }); } }; const variableProxy = (stateLabel, stateValue, controllerName) => { if (typeof stateValue !== "object" || stateValue === null) { return stateValue; } const cache = cacheVariableProxyWeakMap.get(stateValue); if (cache) { return cache; } const proxy = new Proxy(stateValue, { get(target, property, receiver) { const result = Reflect.get(target, property, receiver); if (typeof result === "object" && result !== null) { return variableProxy(stateLabel, result, controllerName); } return result; }, set(target, property, newValue, receiver) { if (variableEditedList[controllerName] && !variableEditedList[controllerName].includes(stateLabel)) { variableEditedList[controllerName].push(stateLabel); } const isSuccess = Reflect.set(target, property, newValue, receiver); if (isSuccess) { variableRenderUpdate(controllerName); } return isSuccess; }, deleteProperty(target, property) { if (variableEditedList[controllerName] && !variableEditedList[controllerName].includes(stateLabel)) { variableEditedList[controllerName].push(stateLabel); } const isSuccess = Reflect.deleteProperty(target, property); if (isSuccess) { variableRenderUpdate(controllerName); } return isSuccess; } }); cacheVariableProxyWeakMap.set(stateValue, proxy); return proxy; }; const variableBindItem = (label, stateValue, controllerName) => { let _state = variableProxy(label, stateValue, controllerName); let _listener = null; return { get state() { return _state; }, set state(value) { if (variableEditedList[controllerName] && !variableEditedList[controllerName].includes(label)) { variableEditedList[controllerName].push(label); } _state = variableProxy(label, value, controllerName); if (_listener) { _listener(_state); } variableRenderUpdate(controllerName); }, listener(callback) { _listener = callback; } }; }; const variableWatch = (controllerName, callback) => { if (!emitterObject[controllerName]) { emitterObject[controllerName] = new JsMvcFwEmitter_1.default(); } const emitter = emitterObject[controllerName]; emitter.on("variableChanged", () => { const editedList = variableEditedList[controllerName] || []; callback((groupObject) => { for (const group of groupObject) { let isAllEdited = true; for (let b = 0; b < group.list.length; b++) { const key = group.list[b]; if (editedList.indexOf(key) === -1) { isAllEdited = false; break; } } if (isAllEdited) { group.action(); for (const key of group.list) { const index = editedList.indexOf(key); if (index !== -1) { editedList.splice(index, 1); } } } } variableEditedList[controllerName] = editedList; }); }); }; const elementHook = (elementContainer, controllerValue) => { const elementHookList = elementContainer.querySelectorAll("[jsmvcfw-elementHook]"); const elementHookObject = {}; for (const elementHook of elementHookList) { const attribute = elementHook.getAttribute("jsmvcfw-elementHook"); if (attribute) { const matchList = attribute.match(/^([a-zA-Z0-9]+)_\d+$/); const baseKey = matchList ? matchList[1] : attribute; if (elementHookObject[baseKey]) { if (Array.isArray(elementHookObject[baseKey])) { elementHookObject[baseKey].push(elementHook); } else { elementHookObject[baseKey] = [elementHookObject[baseKey], elementHook]; } } else { if (matchList) { elementHookObject[baseKey] = [elementHook]; } else { elementHookObject[attribute] = elementHook; } } } } controllerValue.elementHookObject = elementHookObject; }; const getControllerList = () => controllerList; exports.getControllerList = getControllerList; const renderTemplate = (controllerValue, controllerParent, callback) => { const controllerName = controllerValue.constructor.name; if (!controllerParent) { controllerList.push({ parent: controllerValue, childrenList: [] }); } else { for (const controller of controllerList) { if (controllerParent.constructor.name === controller.parent.constructor.name) { controller.childrenList.push(controllerValue); break; } } } controllerValue.variable(); const renderTrigger = () => { const virtualNodeNew = controllerValue.view(); if (!virtualNodeNew || typeof virtualNodeNew !== "object" || !virtualNodeNew.tag) { throw new Error(`@cimo/jsmvcfw - JsMvcFw.ts - renderTrigger() => Invalid virtual node returned by controller "${controllerName}"!`); } let elementContainer = null; if (!controllerParent) { const elementRoot = document.getElementById("jsmvcfw_app"); if (!elementRoot) { throw new Error("@cimo/jsmvcfw - JsMvcFw.ts - renderTrigger() => Root element #jsmvcfw_app not found!"); } elementContainer = elementRoot; } else { const parentContainer = document.querySelector(`[jsmvcfw-controllerName="${controllerParent.constructor.name}"]`); if (!parentContainer) { throw new Error(`@cimo/jsmvcfw - JsMvcFw.ts - renderTrigger() => Tag jsmvcfw-controllerName="${controllerParent.constructor.name}" not found!`); } elementContainer = parentContainer.querySelector(`[jsmvcfw-controllerName="${controllerName}"]`); if (!elementContainer) { throw new Error(`@cimo/jsmvcfw - JsMvcFw.ts - renderTrigger() => Tag jsmvcfw-controllerName="${controllerName}" not found inside jsmvcfw-controllerName="${controllerParent.constructor.name}"!`); } } const virtualNodeOld = virtualNodeObject[controllerName]; if (!virtualNodeOld) { if (elementContainer) { const elementVirtualNode = (0, JsMvcFwDom_1.createVirtualNode)(virtualNodeNew); elementContainer.innerHTML = ""; elementContainer.appendChild(elementVirtualNode); if (callback) { callback(); } } } else { if (elementContainer) { const elementFirstChild = elementContainer.firstElementChild; if (elementFirstChild) { (0, JsMvcFwDom_1.updateVirtualNode)(elementFirstChild, virtualNodeOld, virtualNodeNew); } } } virtualNodeObject[controllerName] = virtualNodeNew; elementHook(elementContainer, controllerValue); }; renderTriggerObject[controllerName] = renderTrigger; renderTrigger(); if (controllerValue.subControllerList) { const subControllerList = controllerValue.subControllerList(); for (const subController of subControllerList) { (0, exports.renderTemplate)(subController, controllerValue, () => { subController.event(); (0, exports.renderAfter)(subController).then(() => { subController.rendered(); }); }); } } variableWatch(controllerName, (watch) => { controllerValue.variableEffect.call(controllerValue, watch); }); }; exports.renderTemplate = renderTemplate; const renderAfter = (controller) => { return new Promise((resolve) => { const check = () => { const controllerName = controller.constructor.name; if (!variableLoadedList[controllerName]) { return; } const variableLoadedLength = variableLoadedList[controllerName].length; const isRendering = variableRenderUpdateObject[controllerName]; if (variableLoadedLength > 0 && !isRendering) { resolve(); } else { Promise.resolve().then(check); } }; check(); }); }; exports.renderAfter = renderAfter; const variableHook = (label, stateValue, controllerName) => { if (!(controllerName in variableHookObject)) { if (!variableLoadedList[controllerName]) { variableLoadedList[controllerName] = []; variableEditedList[controllerName] = []; } if (variableLoadedList[controllerName].includes(label)) { throw new Error(`@cimo/jsmvcfw - JsMvcFw.ts - variableHook() => The method variableHook use existing label "${label}"!`); } variableLoadedList[controllerName].push(label); variableHookObject[controllerName] = variableProxy(label, stateValue, controllerName); } return { state: variableHookObject[controllerName], setState: (value) => { if (variableEditedList[controllerName] && !variableEditedList[controllerName].includes(label)) { variableEditedList[controllerName].push(label); } variableHookObject[controllerName] = variableProxy(label, value, controllerName); variableRenderUpdate(controllerName); } }; }; exports.variableHook = variableHook; const variableBind = (variableObject, controllerName) => { const result = {}; if (!variableLoadedList[controllerName]) { variableLoadedList[controllerName] = []; variableEditedList[controllerName] = []; } for (const key in variableObject) { if (Object.prototype.hasOwnProperty.call(variableObject, key)) { if (variableLoadedList[controllerName].includes(key)) { throw new Error(`@cimo/jsmvcfw - JsMvcFw.ts - variableBind() => The method variableBind use existing label "${key}"!`); } variableLoadedList[controllerName].push(key); result[key] = variableBindItem(key, variableObject[key], controllerName); } } return result; }; exports.variableBind = variableBind; const elementObserver = (element, callback) => { const callbackList = callbackObserverWeakMap.get(element) || []; callbackObserverWeakMap.set(element, [...callbackList, callback]); if (!observerWeakMap.has(element)) { const observer = new MutationObserver((mutationRecordList) => { const callbackList = callbackObserverWeakMap.get(element); if (!callbackList) { return; } for (const mutationRecord of mutationRecordList) { for (const callback of callbackList) { callback(element, mutationRecord); } } }); observer.observe(element, { subtree: true, childList: true, attributes: true }); observerWeakMap.set(element, observer); } }; exports.elementObserver = elementObserver; const elementObserverOff = (element) => { const observer = observerWeakMap.get(element); if (observer) { observer.disconnect(); } }; exports.elementObserverOff = elementObserverOff; const elementObserverOn = (element) => { const observer = observerWeakMap.get(element); if (observer) { observer.observe(element, { subtree: true, childList: true, attributes: true }); } }; exports.elementObserverOn = elementObserverOn; const resetFramework = () => { Object.keys(virtualNodeObject).forEach((key) => delete virtualNodeObject[key]); Object.keys(renderTriggerObject).forEach((key) => delete renderTriggerObject[key]); Object.keys(variableLoadedList).forEach((key) => delete variableLoadedList[key]); Object.keys(variableEditedList).forEach((key) => delete variableEditedList[key]); Object.keys(variableRenderUpdateObject).forEach((key) => delete variableRenderUpdateObject[key]); Object.keys(variableHookObject).forEach((key) => delete variableHookObject[key]); controllerList.length = 0; cacheVariableProxyWeakMap = new WeakMap(); Object.keys(emitterObject).forEach((key) => delete emitterObject[key]); observerWeakMap = new WeakMap(); callbackObserverWeakMap = new WeakMap(); }; exports.resetFramework = resetFramework; //# sourceMappingURL=JsMvcFw.js.map