function createJApp(setting = {}){
const _root = setting.root ? setting.root : document;
const data = {};
const registeredApps = {};
const storagePrefix = "jData-";
let els = prepareEls();
function setData(key,value, toUpdateViewAndStorage = 1){
if(getData(key) == value)return;
data[key] = value;
if(!toUpdateViewAndStorage)return;
sessionStorage.setItem(storagePrefix + key,JSON.stringify(value));
updateView(key);
}
function getData(key){
return data[key];
}
function prepareEls(){
let obj = {
htmlEls:{},
checkedIfEls:{},
disabledIfEls:{},
showIfEls:{},
showFlexIfEls:{},
styleEls:{},
classEls:{},
modelEls:{},
};
const htmlAttrKey = "j-html";
const htmlEls = _root.querySelectorAll(`[${htmlAttrKey}]`);
htmlEls.forEach(htmlEl => {
const attributeValue = htmlEl.getAttribute(htmlAttrKey);
obj.htmlEls[attributeValue] = _root.querySelectorAll(`[${htmlAttrKey}="${attributeValue}"]`);
})
const checkedIfAttrKey = "j-checked-if";
const checkedIfEls = _root.querySelectorAll(`[${checkedIfAttrKey}]`);
checkedIfEls.forEach(checkedIfEl => {
const attributeValue = checkedIfEl.getAttribute(checkedIfAttrKey);
obj.checkedIfEls[attributeValue] = _root.querySelectorAll(`[${checkedIfAttrKey}="${attributeValue}"]`);
})
const disabledIfAttrKey = "j-disabled-if";
const disabledIfEls = _root.querySelectorAll(`[${disabledIfAttrKey}]`);
disabledIfEls.forEach(disabledIfEl => {
const attributeValue = disabledIfEl.getAttribute(disabledIfAttrKey);
obj.disabledIfEls[attributeValue] = _root.querySelectorAll(`[${disabledIfAttrKey}="${attributeValue}"]`);
})
const showIfAttrKey = "j-show-if";
const showIfEls = _root.querySelectorAll(`[${showIfAttrKey}]`);
showIfEls.forEach(showIfEl => {
const attributeValue = showIfEl.getAttribute(showIfAttrKey);
obj.showIfEls[attributeValue] = _root.querySelectorAll(`[${showIfAttrKey}="${attributeValue}"]`);
})
const showFlexIfAttrKey = "j-show-Flex-if";
const showFlexIfEls = _root.querySelectorAll(`[${showFlexIfAttrKey}]`);
showFlexIfEls.forEach(showIfEl => {
const attributeValue = showIfEl.getAttribute(showFlexIfAttrKey);
obj.showFlexIfEls[attributeValue] = _root.querySelectorAll(`[${showFlexIfAttrKey}="${attributeValue}"]`);
})
const styleAttrKey = "j-style";
const styleEls = _root.querySelectorAll(`[${styleAttrKey}]`);
styleEls.forEach(styleEl => {
const attributeValue = styleEl.getAttribute(styleAttrKey);
obj.styleEls[attributeValue] = _root.querySelectorAll(`[${styleAttrKey}='${attributeValue}']`);
})
const classAttrKey = "j-class";
const classEls = _root.querySelectorAll(`[${classAttrKey}]`);
classEls.forEach(classEl => {
const attributeValue = classEl.getAttribute(classAttrKey);
obj.classEls[attributeValue] = _root.querySelectorAll(`[${classAttrKey}='${attributeValue}']`);
})
const modelAttrKey = "j-model";
const modelEls = _root.querySelectorAll(`[${modelAttrKey}]`);
modelEls.forEach(modelEl => {
const attributeValue = modelEl.getAttribute(modelAttrKey);
obj.modelEls[attributeValue] = _root.querySelectorAll(`[${modelAttrKey}='${attributeValue}']`);
})
for (const [key, app] of Object.entries(registeredApps)) {
const attributeKey = "j-" + key;
obj[attributeKey] = _root.querySelectorAll(`[${attributeKey}]`);
}
return obj;
}
function findAllIndexesOfSubstring(str, subStr) {
const indices = [];
let startIndex = 0;
const subStrLength = subStr.length;
while ((startIndex = str.indexOf(subStr, startIndex)) !== -1) {
indices.push(startIndex);
startIndex += subStrLength;
}
return indices;
}
function breakByString(s){
let stringIndeicis = findAllIndexesOfSubstring(s,"`");
const sil = stringIndeicis.length;
if(sil % 2 != 0){
throw new Error(s + " is missing a quote as a string!");
}
let currentIndex = 0;
const result = [];
for(let i = 0; i < sil; i++){
let endIndex = stringIndeicis[i];
let sub;
if( i%2 == 0){
sub = s.substring(currentIndex,endIndex);
currentIndex = endIndex;
}else{
sub = s.substring(currentIndex,endIndex+1);
currentIndex = endIndex+1;
}
result.push(sub);
}
result.push(s.substring(currentIndex));
return result;
}
function prepareEvalStatement(s){
const seperatedString = breakByString(s);
for(let i = 0; i < seperatedString.length; i += 2){
seperatedString[i] = seperatedString[i].replaceAll("#", "data.");
}
return seperatedString.join("");
}
const internalApps = {
appHtml(){
for (const [key, elNodes] of Object.entries(els.htmlEls)) {
elNodes.forEach(el => {
el.innerHTML = eval(prepareEvalStatement(key));
})
}
},
appCheckedIf(){
for (const [key, elNodes] of Object.entries(els.checkedIfEls)) {
elNodes.forEach(el => {
el.checked = eval(prepareEvalStatement(key));
})
}
},
appDisabledIf(){
for (const [key, elNodes] of Object.entries(els.disabledIfEls)) {
elNodes.forEach(el => {
el.disabled = eval(prepareEvalStatement(key));
})
}
},
appShowIf(){
for (const [key, elNodes] of Object.entries(els.showIfEls)) {
elNodes.forEach(el => {
el.style.display = eval(prepareEvalStatement(key)) ? "block" : "none";
})
}
},
appShowFlexIf(){
for (const [key, elNodes] of Object.entries(els.showFlexIfEls)) {
elNodes.forEach(el => {
el.style.display = eval(prepareEvalStatement(key)) ? "flex" : "none";
})
}
},
appStyle(){
for (const [key, elNodes] of Object.entries(els.styleEls)) {
elNodes.forEach(el => {
const tempArr = key.split(';');
const separator = "=";
tempArr.forEach(a => {
const index = a.indexOf(separator);
if (index !== -1) {
const style = a.slice(0, index).trim();
const rule = a.slice(index + separator.length).trim();
el.style.setProperty(style, eval(prepareEvalStatement(rule)));
}
})
})
}
},
appClass(){
for (const [key, elNodes] of Object.entries(els.classEls)) {
elNodes.forEach(el => {
const tempArr = key.split(';');
tempArr.forEach(a => {
const tempA = a.split('if');
const classMame = tempA[0].trim();
const rule = tempA[1];
const preparedRule = prepareEvalStatement(rule);
if(eval(preparedRule)){
el.classList.add(classMame);
}else{
el.classList.remove(classMame);
}
})
})
}
},
appModel(){
for (const [key, elNodes] of Object.entries(els.modelEls)) {
elNodes.forEach(el => {
const dataKey = el.getAttribute("j-model");
el.value = eval(prepareEvalStatement(dataKey));
if(!el.hasInputListener){
el.addEventListener("input", function (e) {
setData(dataKey.replaceAll('#',''), e.target.value);
});
el.hasInputListener = true;
}
})
}
},
}
function updateView(dataKey){
for (const [key, effect] of Object.entries(internalApps)) {
effect();
}
for (const [key, app] of Object.entries(registeredApps)) {
const {effect, deps} = app;
const k = `j-${key}`;
if(!dataKey || !Array.isArray(deps) || !deps.length || deps.includes(dataKey)){
const els = _root.querySelectorAll(`[${k}]`);
if(!els.length){
effect();
}else{
els.forEach(el => {
const prop = el.getAttribute(k);
if(key.includes('for')){
el.innerHTML = "";
}
effect(el, prop);
})
}
}
}
}
function useData(key,initialValue){
setData(key,initialValue, 0);
function setDataFunc(value){
setData(key, value);
}
function getDataFunc(){
return getData(key);
}
return [getDataFunc,setDataFunc];
}
function useApp(appName, effect, deps){
registeredApps[appName] = {
appName,effect,deps
}
els = prepareEls();
updateView();
}
function setDataFromSessionStorage(){
const dataKeys = Object.keys(data);
dataKeys.forEach(k => {
const sessionData = sessionStorage.getItem(storagePrefix + k);
if(sessionData)setData(k, JSON.parse(sessionData), 0) ;
})
}
function appDataReady(setting = {}){
if(setting.useSessionStorage){
setDataFromSessionStorage()
}
updateView();
}
return {useData, useApp, updateView, appDataReady};
}
留言
發佈留言