@cimo/jsmvcfw
Version:
Javascript mvc framework. Light, fast and secure.
351 lines • 14.7 kB
JavaScript
;
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