adajs
Version:
Integrated Web Framework
1,200 lines (1,190 loc) • 69 kB
JavaScript
let eventHelper = require("../lib/event");
let Collector = require("./collector");
let { encodeHTML, isFunction, isPlainObject, randomid, isString, hashCode } = require("../util/helper");
let { PROXYSTATE } = require("./../util/const");
const REGS = {
k: /\r/g,
l: /\n/g,
a: /</g,
b: />/g,
d: /<%|%>/g,
e: /^=.*;$/,
i: /\r\n/g,
f: />[\s]+</g,
g: /<%[\s\S]*%>/,
h: /\<\!\-\-[\s\S]*?\-\-\>/g,
startdot: /</g,
enddot: />/g,
isDoctype: /\<\!DOCTYPE[\s\S]*?\>/g,
isNote: /\<\!\-\-[\s\S]*?\-\-\>/g,
isXmlTag: /\<\?[\s\S]*?\?\>/g,
propcode: /\@\[\[[0-9]+\]\]\@/g,
eventcode: /\$\[\[[0-9]+\]\]\$/g,
assigncode: /\(\(\[[0-9]+\]\)\)/g,
assigncodex: /\(\(\[[0-9]+\]\)\)/,
assigncodeonly: /^\(\(\[[0-9]+\]\)\)$/,
expresscode: /\[\[[0-9]+\]\]/g,
nodecode: /\{\{node\}\}/g,
prop: /[0-9a-zA-Z_]+((\.)([0-9a-zA-Z_]+))+/g,
customA: /<[\s\S]+?>/g,
customB: /<[0-9a-z_-]+?>/,
customC: /<[0-9a-z_-]+ .*?>/,
customD: /<\/[0-9a-z_-]+?>/,
customE: /<[0-9a-z_-]+?\/>/,
macro: /this\._macro\(\{[\s\S]+?\}\);/g,
macroTag: /tag:"[\s\S]+?"/,
syntaxs: /\{\{[\s\S]+?\}\}/g,
threeIf: /[\s\S]+\?[\s\S]+?:/
};
const SINGLETAG = ["br", "hr", "img", "input", "param", "link", "meta", "area", "base", "basefont", "param", "col", "frame", "embed", "keygen", "source"];
const IDNAME = "$$ID";
let SelfCloseTags = [...SINGLETAG];
let TemplateCache = {
map: {},
set(key, info) {
this.map[key] = Object.assign(this.map[key] || {}, info);
},
get(key) {
return this.map[key];
},
has(key) {
return this.map[key] !== undefined;
}
};
let TemplateParseCache = {
map: {},
has(key) {
return this.map[key] !== undefined;
},
set(key, info) {
this.map[key] = info;
},
get(key) {
return this.map[key];
}
};
let Parser = {
beautySyntax: {
syntaxs: {
defaults(str) {
return `<%=${str.substring(2, str.length - 2)};%>`;
},
log(str) {
return `<%window.Ada.log?window.Ada.log(${str.join(" ")}):console.log(${str.join(" ")});%>`;
},
html(str) {
return `<%=assign(html,${str.filter(a => !!a).join(",")});%>`;
},
map(a) {
let dataname = a.shift();
a.shift();
let keyname = a.shift() || "$value",
indexname = a.shift() || "$key",
iname = "_" + randomid(8);
return `<%for(var ${iname} in ${dataname}){var ${keyname}=${dataname}[${iname}];var ${indexname}=${iname};%>`;
},
"/map"() {
return "<%}%>";
},
list(a) {
var dataname = a.shift();
a.shift();
let keyname = a.shift() || "$item",
indexname = a.shift() || "$index",
iname = "_" + randomid(8),
lenname = "_" + randomid(6);
return `<%if(${dataname}&&${dataname}.length>=0)for(var ${iname}=0,${indexname}=0,${lenname}=${dataname}.length;${iname}<${lenname};${iname}++){var ${keyname}=${dataname}[${iname}];${indexname}=${iname};%>`;
},
"/list"(str) {
return "<%}%>";
},
"if"(str, index) {
return `<%if(${str.join(" ")}){%>`;
},
"elseif"(str, index) {
return `<%}else if(${str.join(" ")}){%>`;
},
"else"(str, index) {
return `<%}else{%>`;
},
"/if"() {
return "<%}%>";
},
break() {
return "<%break;%>";
},
set(str) {
return `<%var ${str.join(" ")};%>`;
}
},
parse(strs = "", directives) {
return strs.replace(REGS.syntaxs, (str) => {
let a = str.substring(2, str.length - 2),
b = a.split(" "),
c = b.shift();
try {
let _directives = {};
Reflect.ownKeys(directives || {}).forEach(directive => {
_directives[directive] = (str) => {
return `<%=assign(${directive},${str.filter(a => !!a).join(",")});%>`;
}
});
let syntaxs = Object.assign(Parser.beautySyntax.syntaxs, _directives);
if (syntaxs[c]) {
return syntaxs[c](b);
} else {
return syntaxs.defaults(str);
}
} catch (e) {
console.log(e);
}
});
}
},
nodeParser: {
getNodeStr(node) {
let _events = {}, _attrs = {}, _props = {}, _binders = [];
Reflect.ownKeys(node.props).forEach(function (key) {
if (key.startsWith("on")) {
let _key = key.substring(2);
_events[_key] = node.props[key];
_binders.push(_key);
} else if (key.startsWith("@")) {
let _key = key.substring(1);
_props[_key] = node.props[key];
} else if (key === "data-find") {
_attrs["data-find"] = `${IDNAME}+":${node.props["data-find"]}"`;
} else if (key === "data-group") {
_attrs["data-group"] = `${IDNAME}+":${node.props["data-group"]}"`;
} else if (key === "data-module") {
_attrs["data-module"] = `${IDNAME}`;
} else {
_attrs[key] = `"${node.props[key]}"`;
}
});
if (_binders.length > 0) {
_attrs["data-bind"] = `${IDNAME}+":${_binders.join(",")}"`;
}
return `{tag:"${node.tag}",attrs:{${
Reflect.ownKeys(_attrs).map(i => `"${i}":${_attrs[i]}`).join(",")
}},events:{${
Reflect.ownKeys(_events).map(i => `${i}:$${_events[i]}$`).join(",")
}},props:{${
Reflect.ownKeys(_props).map(i => {
if (_props[i][0] === "\"") {
return `${i}:${_props[i]}`;
} else {
return `${i}:@${_props[i]}@`;
}
}).join(",")
}},children:[]}`;
},
code(node, lev) {
if (!node.isTextNode) {
let pname = "", t = "";
let tempstr = Parser.nodeParser.getNodeStr(node);
if (!node.parent) {
pname = "$$NODE" + lev;
t = `var ${pname}=${tempstr};$$CURRENT=${pname};`;
} else {
pname = "$$NODE" + lev;
if (tempstr) {
t = `${tempstr};$$CURRENT=${pname};`;
} else {
t = `$$CURRENT=${pname};`;
}
}
node.children.forEach(child => {
if (child.isTextNode && (/\(\[[0-9]+\]\)/).test(child.content)) {
t += child.content;
} else {
let at = lev.split("_");
lev = `${at[0]}_${at[1] / 1 + 1}`;
let q = `$$NODE${lev}`;
let _tempstr = Parser.nodeParser.code(child, lev);
if (_tempstr[_tempstr.length - 1] === ";") {
t += `var ${q}=${_tempstr} ${pname}.children.push(${q}); $$CURRENT=${pname};`;
} else {
t += `var ${q}=${_tempstr}; ${pname}.children.push(${q}); $$CURRENT=${pname};`;
}
}
});
return t;
} else {
let tempstr = "";
if (!(/\(\[[0-9]+\]\)/).test(node.content)) {
tempstr = `{content:"${node.content || ""}"}`;
} else {
tempstr = node.content;
}
return tempstr;
}
}
},
getHTMLString(context, nodes, directives) {
let str = "";
let html = (node) => {
let result = "", issingletag = SINGLETAG.indexOf(node.tag) !== -1;
if (node.content !== undefined) {
if (node.type === undefined) {
result = encodeHTML(node.content, context) || "";
} else {
let _adtype = node.type;
let _code = node.content;
let _directives = Object.assign({}, context.ddm.defaultAssignDirectives, directives);
if (_directives[_adtype]) {
_code = _directives[_adtype](node.content);
}
result = "<div class=\"ada-assign-directive\" data-assign-directive-type=\"" + _adtype + "\">" + (_code || '') + "</div>";
}
} else {
let _props = [];
if (node.attrs) {
Reflect.ownKeys(node.attrs).forEach(function (prop, i) {
if (node.attrs[prop]) {
_props.push(prop + "=\"" + node.attrs[prop] + "\"");
}
});
}
if (issingletag) {
result = "<" + node.tag + " " + _props.join(" ") + "/>";
} else {
let _content = [];
node.children.forEach(function (child) {
if (child) {
_content.push(html(child));
}
});
result = "<" + node.tag + " " + _props.join(" ") + ">" + _content.join("") + "</" + node.tag + ">";
}
}
return result;
};
nodes.forEach(function (node) {
str += html(node);
});
return str;
},
preparse(context, str = "", tags = {}) {
str = this.parseCustomTag(context, str.trim(), tags)
.replace(REGS.isNote, "")
.replace(REGS.isDoctype, "")
.replace(REGS.isXmlTag, "")
.replace(REGS.a, "<")
.replace(REGS.b, ">")
.replace(REGS.h, "")
.replace(REGS.f, "><")
.replace(REGS.i, "")
.replace(REGS.k, "")
.replace(REGS.l, "");
[...SelfCloseTags, ...Reflect.ownKeys(tags).filter(name => {
let generator = tags[name];
if (!isFunction(generator)) {
return generator.selfClose === true;
}
})].forEach((tag) => {
let reg = new RegExp(`<${tag} .*?>`, "g");
str = str.replace(reg, function (a) {
return a.substring(0, a.length - 1) + "/>";
});
});
return str;
},
parseCustomTag(context, code, tags = {}) {
return code.replace(REGS.customA, (str) => {
return str.replace(REGS.customB, (str) => {
let tagName = str.substring(1, str.length - 1);
if (context.tags.has(tagName) || tags[tagName] !== undefined) {
return `<@custom @customTagName="${tagName}">`;
} else {
if (!context.tags.isRegularTag(tagName)) {
console.warn(`[ada] tag [${tagName}] seemed not supported`);
}
return str;
}
}).replace(REGS.customE, (str) => {
let tagName = str.substring(1, str.length - 2);
if (context.tags.has(tagName) || tags[tagName] !== undefined) {
return `<@custom @customTagName="${tagName}"/>`;
} else {
if (!context.tags.isRegularTag(tagName)) {
console.warn(`[ada] tag [${tagName}] seemed not supported`);
}
return str;
}
}).replace(REGS.customC, (str) => {
let info = str.split(" ");
let tagName = info.shift().substring(1);
if (context.tags.has(tagName) || tags[tagName] !== undefined) {
info.unshift(`<@custom @customTagName="${tagName}"`);
return info.join(" ");
} else {
if (!context.tags.isRegularTag(tagName)) {
console.warn(`[ada] tag [${tagName}] seemed not supported`);
}
return str;
}
}).replace(REGS.customD, (str) => {
let tagName = str.substring(2, str.length - 1);
if (context.tags.has(tagName) || tags[tagName] !== undefined) {
return "</@custom>";
} else {
if (!context.tags.isRegularTag(tagName)) {
console.warn(`[ada] tag [${tagName}] seemed not supported`);
}
return str;
}
});
});
},
parseMacro(str = "") {
if (str.indexOf("<@") !== -1) {
let i = -1, current = "", state = "start", tagname = "", propname = "", propnamestart, propvalue = "";
let isbody = true, endtagname = "", props = {}, tagindex = 0, tagendindex = 0, endtagindex = 0,
endtagendindex = 0, obj = [];
while (i < str.length) {
i++;
current = str[i];
if (state === "start" && current === "<" && str[i + 1] === "@") {
state = "tagstart";
tagindex = i;
continue;
}
if (state === "tagstart" && current === "@") {
state = "tagname";
tagname = "";
props = {};
continue;
}
if (state === "start" && current === "<" && str[i + 1] === "/" && str[i + 2] === "@") {
endtagindex = i;
state = "endtag";
endtagname = "";
i += 2;
continue;
}
if (state === "endtag" && current === ">") {
state = "start";
endtagendindex = i + 1;
obj.push({
type: "endtag",
tagname: endtagname,
start: endtagindex,
end: endtagendindex
});
continue;
}
if (state === "tagname" && current === " ") {
state = "propname";
propname = "";
continue;
}
if (state === "tagname" && (current === "/" || current === ">")) {
if (current === ">") {
tagendindex = i + 1;
state = "start";
isbody = true;
} else if (current === "/") {
tagendindex = i + 2;
state = "start";
isbody = false;
}
if (tagname !== "") {
obj.push({
type: "tag",
tagname: tagname,
props: props,
body: isbody,
start: tagindex,
end: tagendindex
});
}
continue;
}
if (state === "propname" && current === "=") {
state = "propvalue";
continue;
}
if (state === "propvalue" && (current === "'" || current === "\"")) {
state = "propvalueing";
propnamestart = current;
propvalue = "";
continue;
}
if (state === "propvalueing" && current === propnamestart) {
state = "tagname";
props[propname] = propvalue;
continue;
}
if (state === "endtag") {
endtagname += current;
}
if (state === "tagname") {
tagname += current;
}
if (state === "propname") {
propname += current;
}
if (state === "propvalueing") {
propvalue += current;
}
}
let index = 0, start = 0, end = 0, inner = false, _current = null, result = [], vt = "", startin = 0;
Reflect.ownKeys(obj).forEach(key => {
let _info = obj[key];
if (_info.type === "tag" && _info.body === false && inner === false) {
_info.bodystr = "";
_info.from = _info.start;
_info.to = _info.end;
result.push(_info);
}
if (_info.type === "tag" && _info.body === true) {
inner = true;
if (_current === null) {
_current = _info;
_current.from = _info.start;
}
if (index === 0) {
start = _info.start;
end = _info.end;
}
index++;
}
if (_info.type === "endtag") {
index--;
if (index === 0) {
_current.to = _info.end;
_current.bodystr = str.substring(end, _info.start);
result.push(_current);
_current = null;
inner = false;
}
}
});
result.forEach(item => {
let macroProps = item.props;
let _event = [], _props = [], _attrs = [], _useprops = [], _usepropsmap = [];
Reflect.ownKeys(macroProps).forEach(macroAttrName => {
let macroPropsValStr = macroProps[macroAttrName];
let macroPropsValue = "";
if (REGS.g.test(macroPropsValStr)) {
let cpp = "";
macroPropsValStr.split(REGS.d).forEach((val, index) => {
if ((index + 1) % 2 === 0) {
if (val !== "") {
cpp += `${val}+`;
}
} else {
if (val !== "") {
cpp += `'${val}'+`;
} else {
cpp += val;
}
}
});
let npp = cpp.length > 0 ? cpp.substring(0, cpp.length - 1) : "''";
macroPropsValue = npp.substring(1, npp.length - 1);
} else {
macroPropsValue = `"${macroPropsValStr}"`;
}
if (macroAttrName.startsWith("on")) {
let _eventtype = macroAttrName.substring(2);
_event.push({ name: _eventtype, value: macroPropsValue });
} else if (macroAttrName.startsWith("@")) {
_props.push(`"${macroAttrName.substring(1)}":${macroPropsValue}`);
if (macroAttrName.substring(1).trim() === "parameter") {
let t = macroPropsValue.match(REGS.prop);
if (t) {
t.forEach(p => {
_useprops.push(p);
let e = p.split(".");
e.pop();
_usepropsmap.push(`"${p}":${p}`);
_usepropsmap.push(`"${e.join(".")}":${e.join(".")}`);
});
}
}
} else {
_attrs.push(`"${macroAttrName}":${macroPropsValue}`);
}
});
let _event_ = _event.map(info => {
let _val = info.value.split(/\(|\)/);
let method = _val.shift();
_val.pop();
let paranames = [];
let parameters = _val.map(a => {
if (a.indexOf(" as ") !== -1) {
let _b = a.split("as");
paranames.push(`${_b[1].trim()}`);
return _b[0].trim();
} else {
paranames.push(`${a.split(".").pop()}`);
return a;
}
});
return `"${info.name}":{method:"${method}",parameters:[${parameters.join(",")}],paranames:"${paranames.join(",")}"}`;
});
vt += `${str.substring(startin, item.from)}<%var $$MACRORESULT=this._macro({tag:"${item.tagname}",props:{${_props.join(",")}},attrs:{${_attrs.join(",")}},events:{${_event_.join(",")}},bodyStr:"${item.bodystr}",uses:[${_useprops.map(a => "'" + a + "'").join(",")}],usesmap:{${_usepropsmap.join(",")}}});if({{node}}){if($$MACRORESULT&&$$MACRORESULT.length){for(var $$INDEX=0;$$INDEX<$$MACRORESULT.length;$$INDEX++){{{node}}.children.push($$MACRORESULT[$$INDEX]);}}else{{{node}}.children.push({content:$$MACRORESULT||''});}}else{if($$MACRORESULT&&$$MACRORESULT.length){$$RESULT=$$RESULT.concat($$MACRORESULT);}else{$$RESULT.push({content:$$MACRORESULT||''});}}%>`;
startin = item.to;
});
vt += str.substring(startin, str.length);
return vt;
} else {
return str;
}
},
parseNode(str = "") {
if (str && str !== "") {
let stacks = [],
nodes = [],
current = null;
let tagname = "",
tagendname = "",
propname = "",
value = "",
text = "";
let tagnamestart = false,
propstart = false,
valuestart = false,
tagendstart = false,
element = false;
for (let i = 0, len = str.length; i < len; i++) {
let a = str[i];
if (a !== "\r" && a !== "\n") {
if (a === "<") {
element = true;
if (text.trim() !== "") {
current = {
content: text.trim() || "",
parent: stacks[stacks.length - 1] || null,
isTextNode: true
};
if (stacks[stacks.length - 1]) {
stacks[stacks.length - 1].children.push(current);
} else {
nodes.push(current);
}
text = "";
}
if (str[i + 1] && str[i + 1] === "/") {
tagendstart = true;
} else {
current = {
tag: "",
props: {},
children: [],
parent: null,
hasProp: false,
isTextNode: false
};
stacks.push(current);
if (stacks.length - 2 >= 0) {
stacks[stacks.length - 2].children.push(current);
current.parent = stacks[stacks.length - 2];
}
tagnamestart = true;
}
continue;
} else if (a === " ") {
if (element) {
if (tagnamestart) {
tagnamestart = false;
current.tag = tagname.trim();
tagname = "";
}
if (!propstart && !valuestart) {
propstart = true;
continue;
}
}
} else if (a === "=") {
element && (propstart = false);
} else if (a === "'" || a === "\"") {
if (!valuestart && element) {
valuestart = a;
continue;
} else {
if (valuestart === a) {
valuestart = false, current.hasProp = true;
current.props[propname.trim()] = value.trim();
propname = "", value = "";
}
}
} else if (a === ">") {
element = false, propstart = false, valuestart = false, tagnamestart = false;
if (tagendstart) {
tagendstart = false, tagendname = "";
stacks.length === 1 && nodes.push(stacks[0]);
stacks.pop();
}
if (!current.hasProp) {
current.tag === "" && (current.tag = tagname.trim());
tagname = "";
}
!element && text && (text += a);
continue;
} else if (a === "/") {
if (str[i + 1] && str[i + 1] === ">") {
element = false, valuestart = false, propstart = false, tagendstart = false, tagnamestart = false, tagendname = "";
if (stacks.length === 1) {
nodes.push(stacks[0]);
}
if (!current.hasProp) {
current.tag === "" && (current.tag = tagname.trim());
tagname = "";
}
stacks.pop();
} else {
if (!element) {
text += a;
} else {
valuestart && (value += a);
}
}
continue;
}
tagnamestart && (tagname += a);
propstart && (propname += a);
valuestart && (value += a);
tagendstart && (tagendname += a);
!element && (text += a);
}
}
if (text) {
nodes.push({ content: text || "", parent: null, isTextNode: true });
}
return nodes;
} else {
return [];
}
},
reparseNode(nodes) {
nodes.forEach(node => {
if (node.isTextNode) {
if (node.parent && REGS.assigncodex.test(node.content)) {
if (!REGS.assigncodeonly.test(node.content)) {
let t = [], r = [];
node.content.replace(REGS.assigncode, str => {
t.push({
content: str,
isTextNode: true,
parent: node.parent
});
return str;
}).split(REGS.assigncodex).forEach(txt => {
if (txt.trim()) {
r.push({
content: txt,
isTextNode: true,
parent: node.parent
});
}
let m = t.shift();
if (m) {
r.push(m);
}
});
let _index = node.parent.children.indexOf(node);
if (_index !== -1) {
node.parent.children.splice(_index, 1, ...r);
}
}
}
} else {
this.reparseNode(node.children);
}
});
return nodes;
},
code(temp = "") {
let fn = "", outp = "", cc = [], ee = [];
let t = `"use strict";\nvar $$RESULT=[],$$CURRENT=null;\n`;
temp.split(REGS.d).forEach((e, index) => {
if (index % 2 !== 0) {
if (REGS.e.test(e)) {
fn += outp + `[[${cc.length}]]`;
cc.push(e);
} else {
fn += `(([${ee.length}]))`;
ee.push(e);
}
} else {
fn += outp + e;
}
});
Parser.reparseNode(Parser.parseNode(fn)).forEach((item, i) => {
let pt = i + "_0";
let ct = Parser.nodeParser.code(item, pt);
t += ct + "\r\n";
if (ct.indexOf("$$NODE" + pt) !== -1 && ct.indexOf("$$RESULT.push($$NODE" + pt + ")") === -1) {
t += "$$RESULT.push($$NODE" + pt + ");$$CURRENT=null;\r\n";
}
});
t = t.replace(REGS.assigncode, (a, b, c) => {
return ee[a.substring(3, a.length - 3)];
}).replace(REGS.propcode, (a, b, c) => {
let aa = cc[a.substring(3, a.length - 3)];
if (aa && aa[0] === "=") {
return aa.substring(1, aa.length - 1);
} else {
return aa;
}
}).replace(REGS.eventcode, (a, b, c) => {
let aa = cc[a.substring(3, a.length - 3)];
if (aa && aa[0] === "=") {
let qt = aa.substring(1, aa.length - 1), qtt = qt.split(/\(|\)/);
let paranames = [], _paranames = [];
if (qtt[1]) {
paranames = qtt[1].split(",").map(name => {
if (name.indexOf(" as ") !== -1) {
return name.split("as").pop().trim();
} else {
return name.indexOf(".") !== -1 ? name.split(".").pop() : name;
}
});
_paranames = qtt[1].split(",").map(name => {
if (name.indexOf(" as ") !== -1) {
return name.split("as").shift().trim();
} else {
return name;
}
});
}
return `{method:"${qtt[0].trim()}",parameters:[${_paranames.join(",") || ''}],paranames:"${paranames.join(",")}"}`;
} else {
return aa;
}
}).replace(REGS.expresscode, (a, b, c) => {
let aa = cc[a.substring(2, a.length - 2)];
if (aa && aa[0] === "=") {
let et = aa.substring(1, aa.length - 1);
if (et.indexOf("assign(") === 0) {
et = et.substring(7, et.length - 1);
let _et = et.split(",");
let _type = _et.shift(), _code = _et.join(",");
return `"+((${_code})!==undefined?(${_code}):''),"type":"${_type}`;
} else {
return `"+((${et})!==undefined?(${et}):'')+"`;
}
} else {
return aa;
}
}).replace(REGS.nodecode, (a, b, c) => "$$CURRENT");
t += "return $$RESULT;";
return t;
},
parse(context, temp = "", tags = {}, directives) {
let coverageCount = 0, code = "";
temp.replace(REGS.syntaxs, (str) => {
let a = str.substring(2, str.length - 2), b = a.split(" "), c = b.shift();
if (REGS.threeIf.test(b.join(" "))) {
coverageCount++;
}
if (['if', 'else', 'elseif'].indexOf(c) !== -1) {
coverageCount++;
}
});
code = Parser.code(Parser.parseMacro(Parser.beautySyntax.parse(Parser.preparse(context, temp, tags), directives)));
return { coverageCount, code };
}
};
let Differ = {
childrenEvents(children = []) {
let bubbleEvents = new Set();
let unBubbleEvents = {};
function walk(node) {
if (node.events) {
Reflect.ownKeys(node.events).forEach(type => {
if (eventHelper.canBubbleUp(type)) {
bubbleEvents.add(type);
} else {
let name = node.attrs["data-bind"];
if (!unBubbleEvents[name]) {
unBubbleEvents[name] = [];
}
if (unBubbleEvents[name].indexOf(type) === -1) {
unBubbleEvents[name].push(type);
}
}
});
if (!node.attrs || !node.attrs["data-module"]) {
node.children && node.children.forEach(child => walk(child));
}
}
}
children.forEach(child => walk(child));
return {
bubbleEvents: Array.from(bubbleEvents),
unBubbleEvents
};
},
diffNode(a, b, current, r) {
if (a && b) {
let lent = a.length;
if (a.length === 0) {
if (b.length !== 0) {
r.removeAll.push({
path: current.join(",")
});
}
} else {
if (a[0].attrs && a[0].attrs.uid && b[0] && b[0].attrs && b[0].attrs.uid) {
let removeInfos = [], addInfos = [], sortInfos = [], aIds = [], bIds = [];
aIds = a.map(node => node.attrs.uid);
b = b.filter((node, index) => {
bIds.push(node.attrs.uid);
if (aIds.indexOf(node.attrs.uid) === -1) {
removeInfos.push(index);
} else {
return true;
}
});
a.forEach((node, index) => {
let id = node.attrs.uid;
if (bIds.indexOf(id) === -1) {
addInfos.push({ node, index });
b.push(node);
}
});
let result = [];
b.forEach((node, i) => {
let id = node.attrs.uid, to = aIds.indexOf(id);
if (to !== -1 && to !== i) {
sortInfos.push({ node, from: i, to });
result[to] = node;
} else {
result[i] = node;
}
});
b = result;
removeInfos.forEach((removeIndex) => {
r.bremove.push({
path: current.join(",") + (current.length > 0 ? "," : '') + removeIndex
});
});
addInfos.forEach(info => {
r.badd.push({
path: current.join(","),
node: info.node
});
});
sortInfos.map(info => {
r.sort.push({
path: current.join(",") + (current.length > 0 ? "," : '') + info.from,
to: info.to,
from: info.from
});
let from = b[info.from];
b[info.from] = b[info.to];
b[info.to] = from;
});
} else {
if (a.length < b.length) {
lent = b.length;
}
}
if (b.length > a.length) {
lent = b.length;
}
for (let i = 0; i < lent; i++) {
current.push(i);
if (a[i]) {
if (b[i]) {
if (!(a[i].attrs && a[i].attrs["data-module"] !== undefined)) {
let ctp = Differ.checkNode(a[i], b[i]);
if (b[i].attrs && b[i].attrs["data-module"] !== undefined) {
r.replace.push({
path: current.join(","),
node: a[i]
});
} else {
if (ctp === true) {
Differ.diffNode(a[i].children, b[i].children, current, r);
} else if (ctp === "replace") {
r.replace.push({
path: current.join(","),
node: a[i]
});
} else {
r.edit.push({
path: current.join(","),
attrs: ctp
});
Differ.diffNode(a[i].children, b[i].children, current, r);
}
}
} else if (a[i].attrs["data-module"] !== undefined) {
let ctp = Differ.checkNode(a[i], b[i]);
if (ctp === "replace") {
r.replace.push({
path: current.join(","),
node: a[i]
});
} else if (ctp !== true) {
r.edit.push({
path: current.join(","),
attrs: ctp
});
}
}
} else {
r.add.push({
path: current.join(","),
node: a[i]
});
}
} else {
r.remove.push({
path: current.join(","),
node: b[i]
});
}
current.pop();
}
}
}
},
checkNode(a, b) {
let r = true;
if (a.content !== undefined) {
if (a.content === b.content) {
r = true;
} else {
r = "replace";
}
} else {
if (a.tag === b.tag) {
let left = a.attrs && a.attrs["data-module"],
right = b.attrs && b.attrs["data-module"];
if (left !== right) {
return "replace";
} else {
return Differ.checkProps(a.attrs, b.attrs);
}
} else {
r = "replace";
}
}
return r;
},
checkProps(a, b) {
let ap = Object.keys(a),
bp = Object.keys(b),
r = { final: a },
t = ap.length,
isedit = false;
if (ap.length < bp.length) {
t = bp.length;
}
for (let i = 0; i < t; i++) {
let key = ap[i];
if (key) {
if (b[key] === undefined) {
isedit = true;
break;
} else {
if (a[key] !== b[key]) {
isedit = true;
break;
}
}
} else {
isedit = true;
break;
}
}
if (isedit) {
return r;
} else {
return true;
}
},
diff(newnode, oldnode) {
let r = { add: [], replace: [], remove: [], edit: [], removeAll: [], bremove: [], badd: [], sort: [] },
current = [];
let a = Differ.diffNode(newnode, oldnode, current, r);
oldnode = [];
return r;
}
};
let Effecter = {
element(context, data, issvg, directives) {
if (data.tag === "svg") {
!issvg ? (issvg = true) : "";
}
if (data.content !== undefined) {
if (issvg) {
return context.document.createElementNS("http://www.w3.org/2000/svg", "text");
} else {
if (data.type === undefined) {
return context.document.createTextNode(data.content);
} else {
let _adtype = data.type, _code = data.content;
let _directives = Object.assign({}, context.ddm.defaultAssignDirectives, directives);
if (_directives[_adtype]) {
_code = _directives[_adtype](data.content);
}
let rtn = context.document.createElement("div");
rtn.setAttribute("class", "ada-assign-directive");
rtn.setAttribute("data-assign-directive", _adtype);
rtn.innerHTML = _code || "";
return rtn;
}
}
} else {
let t = null;
if (!issvg) {
t = context.document.createElement(data.tag);
} else {
t = context.document.createElementNS("http://www.w3.org/2000/svg", data.tag);
}
for (let i in data.attrs) {
if (!issvg) {
t.setAttribute(i, data.attrs[i]);
} else {
let a = i.split(":");
if (a.length > 1) {
t.setAttributeNS("http://www.w3.org/1999/" + a[0], a[1], data.attrs[i]);
} else {
t.setAttributeNS(null, a[0], data.attrs[i]);
}
}
}
for (let i = 0; i < data.children.length; i++) {
t.appendChild(Effecter.element(context, data.children[i], issvg));
}
return t;
}
},
effect(context, dom, actions, directives) {
context.logger.log(`> DIFF[${actions.badd.length + actions.add.length + actions.replace.length + actions.remove.length + actions.edit.length + actions.removeAll.length + actions.bremove.length + actions.sort.length}] : Badd:${actions.badd.length} Add:${actions.add.length} Replace:${actions.replace.length} Remove:${actions.remove.length} Edit:${actions.edit.length} RemoveAll:${actions.removeAll.length} Bremove:${actions.bremove.length} Sort:${actions.sort.length}`);
let adds = {};
actions.badd.forEach(action => {
let t = dom;
if (action.path.trim()) {
action.path.split(",").forEach(path => {
t = t.childNodes[path / 1];
});
}
t.appendChild(Effecter.element(context, action.node, false, directives));
});
actions.bremove.map(remove => {
let t = dom;
if (remove.path.trim()) {
remove.path.split(",").forEach(path => {
t = t.childNodes[path / 1];
});
}
return t;
}).forEach(node => node.parentNode.removeChild(node));
if (actions.sort.length > 0) {
let sorts = actions.sort.map(info => {
let t = dom;
if (info.path.trim()) {
info.path.split(",").forEach(path => {
t = t.childNodes[path / 1];
});
}
return {
node: t,
to: info.to,
from: info.from
};
});
let mt = [...sorts[0].node.parentNode.childNodes];
sorts.forEach(sort => {
let a = mt[sort.to];
mt[sort.to] = sort.node;
});
let parent = mt[0].parentNode;
let fragment = context.document.createDocumentFragment();
for (let i = 0; i < mt.length; i++) {
fragment.appendChild(mt[i]);
}
parent.appendChild(fragment);
}
actions.replace.forEach(action => {
let t = dom, has = false;
if (action.path.trim()) {
action.path.split(",").forEach(path => {
let q = t.childNodes[path / 1];
if (q) {
t = q;
has = true;
} else {
has = false;
t.appendChild(Effecter.element(context, action.node, false, directives));
}
});
}
if (has) {
t.parentNode.replaceChild(Effecter.element(context, action.node, false, directives), t);
}
});
actions.add.forEach(action => {
let t = dom;
let paths = action.path.split(",");
paths.pop();
let pname = paths.join("");
paths.forEach(path => {
t = t.childNodes[path / 1];
});
if (!adds[pname]) {
adds[pname] = [{
p: t,
n: action.node
}];
} else {
adds[pname].push({
p: t,
n: action.node
});
}
});
actions.edit.forEach(action => {
let t = dom;
if (action.path.trim()) {
action.path.split(",").forEach(path => t = t.childNodes[path / 1]);
}
let attrs = action.attrs, attributes = [];
if (t.attributes.getNamedItem) {
attributes = [...t.attributes].map(attribute => attribute.nodeName);
} else {
for (let nt in t.attributes) {
attributes.push(nt);
}
}
Reflect.ownKeys(attrs.final).forEach(propName => {
if (propName.indexOf(":") === -1) {
if (t.getAttribute(propName) !== attrs.final[propName]) {
t.setAttribute(propName, attrs.final[propName]);
try {
t[propName] = attrs.final[propName];
} catch (e) {
}
}
let etm = attributes.indexOf(propName);
if (etm !== -1) {
attributes.splice(etm, 1);
}
} else {
let to = propName.split(":"), top = to[1], stop = to[0];
if (t.getAttributeNS("http://www.w3.org/1999/" + stop, top) !== attrs.final[propName]) {
t.setAttributeNS("http://www.w3.org/1999/" + stop, top, attrs.final[propName]);
}
let etm = attributes.indexOf(top);
if (etm !== -1) {
attributes.splice(etm, 1);
}
etm = attributes.indexOf(propName);
if (etm !== -1) {
attributes.splice(etm, 1);
}
}
});
attributes.forEach(attribute => {
if (attribute.indexOf(":") === -1) {
t.removeAttribute(attribute);
} else {
let to = attribute.split(":"), top =