mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
232 lines
6.1 KiB
TypeScript
232 lines
6.1 KiB
TypeScript
import QWeb from "./qweb";
|
|
import { Context } from "./qweb";
|
|
|
|
interface CompilationInfo {
|
|
nodeID?: string;
|
|
node: Element;
|
|
qweb: QWeb;
|
|
ctx: Context;
|
|
fullName: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface Directive {
|
|
name: string;
|
|
priority: number;
|
|
// if return true, then directive is fully applied and there is no need to
|
|
// keep processing node. Otherwise, we keep going.
|
|
atNodeEncounter?(info: CompilationInfo): boolean;
|
|
atNodeCreation?(info: CompilationInfo);
|
|
finalize?(info: CompilationInfo);
|
|
}
|
|
|
|
const forEachDirective: Directive = {
|
|
name: "foreach",
|
|
priority: 10,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
const elems = node.getAttribute("t-foreach")!;
|
|
const name = node.getAttribute("t-as")!;
|
|
let arrayID = ctx.generateID();
|
|
ctx.addLine(`let ${arrayID} = ${qweb._formatExpression(elems)}`);
|
|
ctx.addLine(
|
|
`if (typeof ${arrayID} === 'number') { ${arrayID} = Array.from(Array(${arrayID}).keys())}`
|
|
);
|
|
let keysID = ctx.generateID();
|
|
ctx.addLine(
|
|
`let ${keysID} = ${arrayID} instanceof Array ? ${arrayID} : Object.keys(${arrayID})`
|
|
);
|
|
let valuesID = ctx.generateID();
|
|
ctx.addLine(
|
|
`let ${valuesID} = ${arrayID} instanceof Array ? ${arrayID} : Object.values(${arrayID})`
|
|
);
|
|
ctx.addLine(`for (let i = 0; i < ${keysID}.length; i++) {`);
|
|
ctx.indent();
|
|
ctx.addLine(`context.${name}_first = i === 0`);
|
|
ctx.addLine(`context.${name}_last = i === ${keysID}.length - 1`);
|
|
ctx.addLine(`context.${name}_parity = i % 2 === 0 ? 'even' : 'odd'`);
|
|
ctx.addLine(`context.${name}_index = i`);
|
|
ctx.addLine(`context.${name} = ${keysID}[i]`);
|
|
ctx.addLine(`context.${name}_value = ${valuesID}[i]`);
|
|
const nodes = Array.from(node.childNodes);
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
qweb._compileNode(nodes[i], ctx);
|
|
}
|
|
ctx.dedent();
|
|
ctx.addLine("}");
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const ifDirective: Directive = {
|
|
name: "if",
|
|
priority: 20,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
let cond = qweb._getValue(node.getAttribute("t-if")!, ctx);
|
|
ctx.addLine(`if (${qweb._formatExpression(cond)}) {`);
|
|
ctx.indent();
|
|
return false;
|
|
},
|
|
finalize({ ctx }) {
|
|
ctx.dedent();
|
|
ctx.addLine(`}`);
|
|
}
|
|
};
|
|
|
|
const elifDirective: Directive = {
|
|
name: "elif",
|
|
priority: 30,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
let cond = qweb._getValue(node.getAttribute("t-elif")!, ctx);
|
|
ctx.addLine(`else if (${qweb._formatExpression(cond)}) {`);
|
|
ctx.indent();
|
|
return false;
|
|
},
|
|
finalize({ ctx }) {
|
|
ctx.dedent();
|
|
ctx.addLine(`}`);
|
|
}
|
|
};
|
|
|
|
const elseDirective: Directive = {
|
|
name: "else",
|
|
priority: 40,
|
|
atNodeEncounter({ ctx }): boolean {
|
|
ctx.addLine(`else {`);
|
|
ctx.indent();
|
|
return false;
|
|
},
|
|
finalize({ ctx }) {
|
|
ctx.dedent();
|
|
ctx.addLine(`}`);
|
|
}
|
|
};
|
|
|
|
const callDirective: Directive = {
|
|
name: "call",
|
|
priority: 50,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
const subTemplate = node.getAttribute("t-call")!;
|
|
const nodeTemplate = qweb.nodeTemplates[subTemplate];
|
|
const nodeCopy = <Element>node.cloneNode(true);
|
|
nodeCopy.removeAttribute("t-call");
|
|
|
|
// extract variables from nodecopy
|
|
const tempCtx = new Context();
|
|
qweb._compileNode(nodeCopy, tempCtx);
|
|
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
|
const subCtx = ctx.withCaller(nodeCopy).withVariables(vars);
|
|
|
|
qweb._compileNode(nodeTemplate.firstChild!, subCtx);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const setDirective: Directive = {
|
|
name: "set",
|
|
priority: 60,
|
|
atNodeEncounter({ node, ctx }): boolean {
|
|
const variable = node.getAttribute("t-set")!;
|
|
let value = node.getAttribute("t-value")!;
|
|
if (value) {
|
|
ctx.variables[variable] = value;
|
|
} else {
|
|
ctx.variables[variable] = node.childNodes;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Context) {
|
|
if (value === "0" && ctx.caller) {
|
|
qweb._compileNode(ctx.caller, ctx);
|
|
return;
|
|
}
|
|
|
|
if (typeof value === "string") {
|
|
const exprID = ctx.generateID();
|
|
ctx.addLine(`let ${exprID} = ${qweb._formatExpression(value)}`);
|
|
ctx.addLine(`if (${exprID} || ${exprID} === 0) {`);
|
|
ctx.indent();
|
|
let text = exprID;
|
|
if (ctx.escaping) {
|
|
text = `this.escape(${text})`;
|
|
}
|
|
|
|
const nodeID = ctx.generateID();
|
|
ctx.addLine(`let ${nodeID} = document.createTextNode(${text})`);
|
|
ctx.addNode(nodeID);
|
|
ctx.dedent();
|
|
if (node.childNodes.length) {
|
|
ctx.addLine("} else {");
|
|
ctx.indent();
|
|
qweb._compileChildren(node, ctx);
|
|
ctx.dedent();
|
|
}
|
|
ctx.addLine("}");
|
|
return;
|
|
}
|
|
if (value instanceof NodeList) {
|
|
for (let node of Array.from(value)) {
|
|
qweb._compileNode(<ChildNode>node, ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
const escDirective: Directive = {
|
|
name: "esc",
|
|
priority: 70,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
if (node.nodeName !== "t") {
|
|
let nodeID = qweb._compileGenericNode(node, ctx);
|
|
ctx = ctx.withParent(nodeID);
|
|
}
|
|
let value = qweb._getValue(node.getAttribute("t-esc")!, ctx);
|
|
compileValueNode(value, node, qweb, ctx.withEscaping());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const rawDirective: Directive = {
|
|
name: "raw",
|
|
priority: 80,
|
|
atNodeEncounter({ node, qweb, ctx }): boolean {
|
|
if (node.nodeName !== "t") {
|
|
let nodeID = qweb._compileGenericNode(node, ctx);
|
|
ctx = ctx.withParent(nodeID);
|
|
}
|
|
let value = qweb._getValue(node.getAttribute("t-raw")!, ctx);
|
|
compileValueNode(value, node, qweb, ctx);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const onDirective: Directive = {
|
|
name: "on",
|
|
priority: 90,
|
|
atNodeCreation({ ctx, fullName, value, nodeID }) {
|
|
const eventName = fullName.slice(5);
|
|
let extraArgs;
|
|
let handler = value.replace(/\(.*\)/, function(args) {
|
|
extraArgs = args.slice(1, -1);
|
|
return "";
|
|
});
|
|
ctx.addLine(
|
|
`${nodeID}.addEventListener('${eventName}', context['${handler}'].bind(context${
|
|
extraArgs ? ", " + extraArgs : ""
|
|
}))`
|
|
);
|
|
}
|
|
};
|
|
|
|
export default [
|
|
forEachDirective,
|
|
ifDirective,
|
|
elifDirective,
|
|
elseDirective,
|
|
callDirective,
|
|
setDirective,
|
|
escDirective,
|
|
rawDirective,
|
|
onDirective
|
|
];
|