adajs
Version:
Integrated Web Framework
649 lines (643 loc) • 27.1 kB
JavaScript
let util = require("../base/util");
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,
expresscode: /\[\[[0-9]+\]\]/g,
nodecode: /\{\{node\}\}/g,
prop: /[0-9a-z]+((\.)([0-9a-z_]+))+/g
};
const SINGLETAG = ["br", "hr", "img", "input", "param", "link", "meta", "area", "base", "basefont", "param", "col", "frame", "embed", "keygen", "source"];
const TEMPLATECACHE = new Map();
const IDNAME = "$$ID";
let Parser = {
beautySyntax: {
syntaxs: {
defaults(str) {
return `<%=${str.substring(2, str.length - 2)};%>`;
},
log(str) {
return `<%console.log(${str.join(" ")});%>`;
},
html(str) {
return `<%=assign(html,${str});%>`;
},
map(a) {
let dataname = a.shift();
a.shift();
let keyname = a.shift() || "$value",
indexname = a.shift() || "$key",
iname = "_" + util.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 = "_" + util.randomid(8),
lenname = "_" + util.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) {
return `<%if(${str.join(" ")}){%>`;
},
"elseif"(str) {
return `<%}else if(${str.join(" ")}){%>`;
},
"else"() {
return "<%}else{%>";
},
"/if"() {
return "<%}%>";
},
break() {
return "<%break;%>";
},
set(str) {
return `<%var ${str.join(" ")};%>`;
}
},
parse(strs = "") {
return strs.replace(/\{\{[\s\S]+?\}\}/g, (str) => {
let a = str.substring(2, str.length - 2),
b = a.split(" "),
c = b.shift();
try {
if (Parser.beautySyntax.syntaxs[c]) {
return Parser.beautySyntax.syntaxs[c](b);
} else {
return Parser.beautySyntax.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;
t = `${tempstr};$$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);
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(nodes) {
let str = "";
function html(node) {
let result = "", issingletag = SINGLETAG.indexOf(node.tag) !== -1;
if (node.content !== undefined) {
if (node.type === undefined) {
result = util.encodeHTML(node.content) || "";
} else {
let _adtype = node.type;
let _code = node.content;
if (DefaultAssignDirectives[_adtype]) {
_code = DefaultAssignDirectives[_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(str = "") {
str = str.trim()
.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, "");
SINGLETAG.forEach((tag) => {
let reg = new RegExp(`<${tag} .*?>`, "g");
str = str.replace(reg, function (a) {
return a.substring(0, a.length - 1) + "/>";
});
});
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 = [];
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 => {
let e = p.split(".");
e.pop();
_useprops.push(`"${p}":${p}`);
_useprops.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.join(",")}}});if($$MACRORESULT&&$$MACRORESULT.length){for(var $$INDEX=0;$$INDEX<$$MACRORESULT.length;$$INDEX++){{{node}}.children.push($$MACRORESULT[$$INDEX]);}}else{{{node}}.children.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 = "";
}
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 [];
}
},
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.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 + ");\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(temp = "") {
let result = "";
if (!TEMPLATECACHE.has(temp)) {
result = Parser.code(Parser.parseMacro(Parser.beautySyntax.parse(Parser.preparse(temp))));
TEMPLATECACHE.set(temp, result);
} else {
result = TEMPLATECACHE.get(temp);
}
return result;
}
};
module.exports = Parser;