注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
?_=1
来访问最新页面。https://mzh.moegirl.org.cn/User:%E4%B9%9D%E6%B1%9F%E6%9C%88/js/mv.js?_=1
window.onload=function(){init();} var mv = {}; function MVVM(options) { this.$options = options; let data = this.data = this.$options.data; this.catch = []; let depool = this.depool = {}; observe(data); //initComputed.call(this); //options.mounted.call(this); function observe(data) { if (!data || typeof(data) !== "object") return; return new Observe(data); } function Observe(data) { for (let key in data) { let val = data[key]; observe(val); let dep = new Dep(key); depool[key] = (dep); Object.defineProperty(data,key,{ configurable: true, get(){ Dep.target && dep.addSub(Dep.target); return val; }, set(newVal) { if (val === newVal) return; val = newVal; observe(newVal); dep.notify(); } }); } } function Dep(name) { this.subs = []; //事件池 this.func = ''; this.hocks = {}; this.name = name?name:""; } Dep.prototype.addSub = function(sub) { this.subs.push(sub); } Dep.prototype.notify = function(from2) { this.subs.forEach((sub) => {sub.update(from2);}); for(let [k,v] of Object.entries(this.hocks)){depool[k].notify(true);} } Dep.prototype.setfunc = function(func){this.func = func} Dep.prototype.getfunc = function(){return this.func;} Dep.prototype.sethock = function(name,hock){ if(this.hocks[name]){this.hocks[name].push(hock);} else{this.hocks[name]=[hock];}; } Dep.prototype.gethock = function(){return this.func;} function Watcher(vm, exp, fn) { this.fn = fn; this.vm = vm; this.exp = exp; Dep.target = this; let arr = exp.split('.'); let val = vm.data; arr.forEach(key => { val = val[key]; }); Dep.target = null; } Watcher.prototype.update = function(from2) { let arr = this.exp.split('.'); let val = this.vm; arr.forEach(key => { val = val[key]; }) this.fn(val); } this.$options.computed = { sum() { return this.num.a + this.num.b; }, noop() {} }; function initComputed() { let vm = this; let computed = this.$options.computed; Object.keys(computed).forEach(key => { Object.defineProperty(vm, key, { get: typeof computed[key] === 'function' ? computed[key] : computed[key].get, set() {} }); }); } new compile(options.el,this); function compile(el,vm) { vm.$el = document.querySelector(el); let fragment = document.createDocumentFragment(); while(child = vm.$el.firstChild) { fragment.appendChild(child); } function replace(frag) { Array.from(frag.childNodes).forEach((node) => { let reg = /\{\{#([^{}]+)\}\}/g; let content = node.textContent; let style = ''; if(node.nodeType!=3){ style=(node.nodeType!=3)?node.getAttribute("style"):''; } if (node.nodeType === 1 && reg.test(content)) { function replaceTxt(notself){ node.textContent = content.replace(reg,(matched,placeholder)=>{ let sets = /([^=]+)=(.*)/g.exec(placeholder); if(sets){ placeholder = sets[1]; let dep = new Dep(placeholder); vm.depool[placeholder] = (dep); dep.setfunc(sets[2]); var hocks = []; var temp = execMathExpress(dep.getfunc(),vm.data); hocks = temp.items; Array.from(hocks).forEach((hock)=>{ if(hock.indexOf('_')<0){if(vm.depool[hock]){vm.depool[hock].sethock(placeholder,hock);}} }); Object.defineProperty(vm.data,sets[1],{ configurable: true, get(){ Dep.target && dep.addSub(Dep.target); return temp.num/temp.den; }, set(newVal) { if ((temp.num/temp.den) == newVal) return; val = execMathExpress(dep.getfunc(),vm.data).num; dep.notify(); } }); Object.defineProperty(vm.data,sets[0],{ configurable: true, get(){ Dep.target && dep.addSub(Dep.target); return temp.num/temp.den; }, set(newVal) { if ((temp.num/temp.den) == newVal) return; val = execMathExpress(dep.getfunc(),vm.data).num;console.log('change'); dep.notify();vm.depool[0].notify(); } }); vm.catch[sets[0]]=new Watcher(vm,sets[0],replaceTxt); //添加表达式观察 } vm.catch[placeholder]=new Watcher(vm,placeholder,replaceTxt); return placeholder.split('.').reduce((val,key)=>{ return val[key]; },vm.data); }); } replaceTxt(true); } if (node.nodeType === 1 && reg.test(style) ){ function replaceTxt(notself){ node.setAttribute("style",style.replace(reg,(matched,placeholder)=>{ if (notself){new Watcher(vm,placeholder,replaceTxt);} return placeholder.split('.').reduce((val,key)=>{return val[key]},vm.data); })); } replaceTxt(true); } if (node.childNodes && node.childNodes.length) {replace(node);} }) } replace(fragment); vm.$el.appendChild(fragment); } } function init(state){ this.items = document.querySelector('.moecalc'); let datas = this.items.getElementsByClassName('data'); if(datas.length>0){ for (var index = 0; index<datas.length; index++){ var name = '#'+datas[index].getAttribute("title"); var data = dataExpand(name,datas[index].childNodes); if (state==1) { function deep(origin, target) { if (!mv[name].data){return} for(var index in origin){ var item = origin[index]; if (item !== 'null' && typeof (item) == 'object'){ deep(item,target[index]); }else{ target[index] = item; } } } deep(data,mv[name].data); }else{ mv[name] = new MVVM({el: name,data: data}); } } } } function dataExpand(domain,obj){ var item = {}; if (obj){ for(var index = 0; index<obj.length; index++){ if(obj[index].nodeType!=3){ var name = obj[index].className.split(' ')[0]; if(/(?<=to-)[^ ]+/g.exec(obj[index].className)){ let ndtype = /(?<=to-)[^ ]+/g.exec(obj[index].className)[0]; let attrs = obj[index].attributes; let cdnd = obj[index].childNodes; let newNode = document.createElement(ndtype); Array.from(attrs).forEach(attr =>{ newNode.setAttribute(attr.name,attr.name=="class"?attr.value.replace(/(?=to-)[^ ]+/g,'').replace(' ',' '):attr.value); }); Array.from(cdnd).forEach(cdd =>{ newNode.appendChild(cdd); }); obj[index].parentNode.replaceChild(newNode,obj[index]); } if(/event/g.exec(obj[index].className)){ switch(obj[index].tagName){ case "INPUT": obj[index].addEventListener('input',e =>{ let newVal = e.target.value; let target = e.target.className.split(' ')[0]; mv[domain].data[target] = newVal; }); break; case "SELECT": obj[index].addEventListener('input',e =>{ let newVal = e.target.options[e.target.selectedIndex].value; let target = e.target.className.split(' ')[0]; mv[domain].data[target] = newVal; }); break; } } var text = obj[index].innerHTML; var info = text?text:obj[index].value; item[name] = info; if(obj[index].childNodes.length>0){ if(obj[index].childNodes[0].nodeType!=3 || obj[index].childNodes.length>1){ item[name] = dataExpand(domain,obj[index].childNodes); if(obj[index].tagName=="SELECT"){ item[name] = obj[index].options[0].value; obj[index].options[0].selected=true; } } } } } } return item; } //this is a calc from https://www.cnblogs.com/caoke/p/11053253.html //https://github.com/hausen/math.diff.js/blob/master/math.diff.js 支持三角函数 function gcd(a,b){ return b===0?a:gcd(b,a%b) } //分数类 分子,分母 class Fraction{ static create(num,den=1) { if(num instanceof Fraction){ return num; }else if(/(-?\d+)\/(\d+)/.test(num)){ return new Fraction(parseInt(RegExp.$1),parseInt(RegExp.$2)) }else{ if(/\.(\d+)/.test(num)){ num=num*Math.pow(10,RegExp.$1.length); den=den*Math.pow(10,RegExp.$1.length); } if(/\.(\d+)/.test(den)){ num=num*Math.pow(10,RegExp.$1.length); den=den*Math.pow(10,RegExp.$1.length); } return new Fraction(num,den) } } constructor(num=0,den=1){ if(den<0){ num=-num; den=-den; } if(den===0){ throw '分母不能为0' } let g=gcd(Math.abs(num),den) this.num=num/g; this.den=den/g; } //加 add(o){ return new Fraction(this.num*o.den+this.den*o.num,this.den*o.den) } //减 sub(o){ return new Fraction(this.num*o.den-this.den*o.num,this.den*o.den) } //乘 multiply(o){ return new Fraction(this.num*o.num,this.den*o.den); } //除 divide(o){ return new Fraction(this.num*o.den,this.den*o.num); } //小于 lessThan(o){ return this.num*o.den<this.den*o.num; } //等于 equal(o){ return this.num*o.den===this.den*o.num; } //向上取整 mathCeil(){ let rt = Math.ceil(this.num/this.den); return new Fraction(rt,1); } //向下取整 mathFloor(){ let rt = Math.floor(this.num/this.den); return new Fraction(rt,1); } //四舍五入取整 mathRound(){ let rt = Math.round(this.num/this.den); return new Fraction(rt,1); } mathMoreThan(o){ let rt = ((this.num/this.den)>(o.num/o.den))?1:0; return new Fraction(rt,1); } mathMorePThan(o){ let rt = ((this.num/this.den)>=(o.num/o.den))?1:0; return new Fraction(rt,1); } mathLessThan(o){ let rt = ((this.num/this.den)<(o.num/o.den))?1:0; return new Fraction(rt,1); } mathLessPThan(o){ let rt = ((this.num/this.den)<=(o.num/o.den))?1:0; return new Fraction(rt,1); } mathEqual(o){ let rt = ((this.num/this.den)==(o.num/o.den))?1:0; return new Fraction(rt,1); } mathIf(){ return new Fraction(this.num,this.den); } toString() { return this.num+'/'+this.den; } toNum(){ return this.num / this.den; } } //解析数学表达式 function execMathExpress(formula,obj){ obj['#e']='2.718281828'; //局部变量 const tempObj=Object.assign({ _0:0 },obj); //计算缓存 const keyCache={}; let index=1; var packval = { items:[],get:function(){return this.items;}, add:function(pickup){this.items.push(pickup);} }; formula=formula.replace(/ /g,'');//清理空格 //解析数字 formula=formula.replace(/(^|[(*+/->=<])(\d+\.\d+|\d+)/g,function (m,p1,p2) { if(keyCache[p2]){ return p1+keyCache[p2]; } const key=keyCache[p2]='_'+index++; tempObj[key]=Fraction.create(p2); return p1+key; }); function getKey(p1,p2,p3) { const keyC=p1+p2+p3; if(keyCache[keyC]){ return keyCache[keyC]; } const key=keyCache[keyC]='_'+index++; const fA=Fraction.create(tempObj[p1]); const fB=Fraction.create(tempObj[p3]); switch(p2){ case '*': tempObj[key]=fA.multiply(fB); break; case '/': tempObj[key]=fA.divide(fB); break; case '+': tempObj[key]=fA.add(fB); break; case '-': tempObj[key]=fA.sub(fB); break; case 'ceil': tempObj[key]=fA.mathCeil(); break; case 'floor': tempObj[key]=fA.mathFloor(); break; case 'round': tempObj[key]=fA.mathRound(); break; case 'if': tempObj[key]=fA.mathIf(); break; case '>': tempObj[key]=fA.mathMoreThan(fB); break; case '>=': tempObj[key]=fA.mathMorePThan(fB); break; case '<': tempObj[key]=fA.mathLessThan(fB); break; case '<=': tempObj[key]=fA.mathLessPThan(fB); break; case '==': tempObj[key]=fA.mathEqual(fB); break; } return key; } function run(s) { //子表达式 if(/\(([^\(]+?)\)/.test(s)){ s=s.replace(/\(([^\(]+?)\)/g,function (m,p1,p2) { return run(p1); }) } //二元运算 自定义函数 const expArr2 = ['>=','<=','==','>','<']; for(let i=0;i<expArr2.length;i++){ const p=expArr2[i]; const reg=new RegExp('(\\w+)'+p+'(\\w+)'); let iii = 0; while (reg.test(s)){ s=s.replace(reg,function (m,p1,p2) { if(iii==0){packval.add(p1);packval.add(p2);iii++;}else{packval.add(p2);} return getKey(p1,p,p2); }) } } //负号 s=s.replace(/([*/+]|^)-(\w+)/g,function (m,p1,p2) { return getKey('_0','-',p2); }); //一元运算 自定义函数 嵌套括号以及度量单位待优化 const expArr1=['ceil','floor','round']; for(let i=0;i<expArr1.length;i++){ const p=expArr1[i]; const reg=new RegExp('('+p+')(\\w+)'); let iii = 0; while (reg.test(s)){ s=s.replace(reg,function (m,p1,p2) { if(iii==0){packval.add(p1);packval.add(p2);iii++;}else{packval.add(p2);} return getKey(p2,p1); }) } } //返回 if(/(^\w+$)/.test(s)){ return RegExp.$1; } //乘法、除法、加法、减法 const expArr=['*','/','+','-']; for(let i=0;i<expArr.length;i++){ const p=expArr[i]; const reg=new RegExp('(\\w+)['+p+'](\\w+)'); let iii = 0; while (reg.test(s)){ s=s.replace(reg,function (m,p1,p2) { if(iii==0){packval.add(p1);packval.add(p2);iii++;}else{packval.add(p2);} return getKey(p1,p,p2); }) } } //返回 if(/(^\w+$)/.test(s)){ return RegExp.$1; } return run(s); } var rt = tempObj[run(formula)]; if (typeof rt == "object"){rt.items = packval.get();} else{var re = rt;rt = {num:re,den:1,items:[formula]};} return rt; }