注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
?_=1来访问最新页面。https://mobile.moegirl.org.cn/User:%E4%B9%9D%E6%B1%9F%E6%9C%88/js/mv.js?_=1window.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;
}