mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] qweb: use native Node and Element instead of custom Dom types
This commit is contained in:
committed by
Aaron Bohy
parent
e746574a1d
commit
efa147fdab
+28
-13
@@ -1,5 +1,4 @@
|
|||||||
import { BDom } from "../blockdom";
|
import { BDom } from "../blockdom";
|
||||||
import { Dom, DomNode, domToString, DomType } from "./dom_helpers";
|
|
||||||
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
|
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
|
||||||
import {
|
import {
|
||||||
AST,
|
AST,
|
||||||
@@ -33,6 +32,10 @@ export function compileTemplate(template: string, name?: string): TemplateFuncti
|
|||||||
return compiler.compile();
|
return compiler.compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
||||||
|
// of HTML (as we will parse it as xml later)
|
||||||
|
const xmlDoc = document.implementation.createDocument(null, null, null);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// BlockDescription
|
// BlockDescription
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -47,8 +50,8 @@ class BlockDescription {
|
|||||||
hasDynamicChildren: boolean = false;
|
hasDynamicChildren: boolean = false;
|
||||||
children: BlockDescription[] = [];
|
children: BlockDescription[] = [];
|
||||||
data: string[] = [];
|
data: string[] = [];
|
||||||
dom?: Dom;
|
dom?: Node;
|
||||||
currentDom?: DomNode;
|
currentDom?: Element;
|
||||||
childNumber: number = 0;
|
childNumber: number = 0;
|
||||||
target: CodeTarget;
|
target: CodeTarget;
|
||||||
type: BlockType;
|
type: BlockType;
|
||||||
@@ -69,9 +72,9 @@ class BlockDescription {
|
|||||||
return this.data.push(id) - 1;
|
return this.data.push(id) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(dom: Dom) {
|
insert(dom: Node) {
|
||||||
if (this.currentDom) {
|
if (this.currentDom) {
|
||||||
this.currentDom.content.push(dom);
|
this.currentDom.appendChild(dom);
|
||||||
} else {
|
} else {
|
||||||
this.dom = dom;
|
this.dom = dom;
|
||||||
}
|
}
|
||||||
@@ -90,6 +93,14 @@ class BlockDescription {
|
|||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asXmlString() {
|
||||||
|
// Can't use outerHTML on text/comment nodes
|
||||||
|
// append dom to any element and use innerHTML instead
|
||||||
|
const t = xmlDoc.createElement('t');
|
||||||
|
t.appendChild(this.dom!);
|
||||||
|
return t.innerHTML;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -186,7 +197,7 @@ export class QWebCompiler {
|
|||||||
|
|
||||||
insertAnchor(block: BlockDescription) {
|
insertAnchor(block: BlockDescription) {
|
||||||
const tag = `block-child-${block.children.length}`;
|
const tag = `block-child-${block.children.length}`;
|
||||||
const anchor: Dom = { type: DomType.Node, tag, attrs: {}, content: [] };
|
const anchor = xmlDoc.createElement(tag);
|
||||||
block.insert(anchor);
|
block.insert(anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +258,7 @@ export class QWebCompiler {
|
|||||||
this.addLine(``);
|
this.addLine(``);
|
||||||
for (let block of this.blocks) {
|
for (let block of this.blocks) {
|
||||||
if (block.dom) {
|
if (block.dom) {
|
||||||
this.addLine(`let ${block.blockName} = createBlock(\`${domToString(block.dom)}\`);`);
|
this.addLine(`let ${block.blockName} = createBlock(\`${block.asXmlString()}\`);`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,7 +406,7 @@ export class QWebCompiler {
|
|||||||
block = this.createBlock(block, "block", ctx);
|
block = this.createBlock(block, "block", ctx);
|
||||||
this.blocks.push(block);
|
this.blocks.push(block);
|
||||||
}
|
}
|
||||||
const text: Dom = { type: DomType.Comment, value: ast.value };
|
const text = xmlDoc.createComment(ast.value);
|
||||||
block!.insert(text);
|
block!.insert(text);
|
||||||
if (isNewBlock) {
|
if (isNewBlock) {
|
||||||
this.insertBlock("", block!, ctx);
|
this.insertBlock("", block!, ctx);
|
||||||
@@ -411,9 +422,8 @@ export class QWebCompiler {
|
|||||||
forceNewBlock: forceNewBlock && !block,
|
forceNewBlock: forceNewBlock && !block,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const type = ast.type === ASTType.Text ? DomType.Text : DomType.Comment;
|
const createFn = ast.type === ASTType.Text ? xmlDoc.createTextNode : xmlDoc.createComment;
|
||||||
const text: Dom = { type, value: ast.value };
|
block.insert(createFn.call(xmlDoc, ast.value));
|
||||||
block.insert(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +509,12 @@ export class QWebCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dom: Dom = { type: DomType.Node, tag: ast.tag, attrs: attrs, content: [] };
|
const dom = xmlDoc.createElement(ast.tag);
|
||||||
|
for (const [attr, val] of Object.entries(attrs)) {
|
||||||
|
if (!(attr === "class" && val === "")) {
|
||||||
|
dom.setAttribute(attr, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
block!.insert(dom);
|
block!.insert(dom);
|
||||||
if (ast.content.length) {
|
if (ast.content.length) {
|
||||||
const initialDom = block!.currentDom;
|
const initialDom = block!.currentDom;
|
||||||
@@ -553,7 +568,7 @@ export class QWebCompiler {
|
|||||||
this.insertBlock(`text(${expr})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block });
|
this.insertBlock(`text(${expr})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block });
|
||||||
} else {
|
} else {
|
||||||
const idx = block.insertData(expr);
|
const idx = block.insertData(expr);
|
||||||
const text: Dom = { type: DomType.Node, tag: `block-text-${idx}`, attrs: {}, content: [] };
|
const text = xmlDoc.createElement(`block-text-${idx}`);
|
||||||
block.insert(text);
|
block.insert(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
export const enum DomType {
|
|
||||||
Text,
|
|
||||||
Comment,
|
|
||||||
Node,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DomText {
|
|
||||||
type: DomType.Text;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DomComment {
|
|
||||||
type: DomType.Comment;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
export interface DomNode {
|
|
||||||
type: DomType.Node;
|
|
||||||
tag: string;
|
|
||||||
attrs: { [key: string]: string };
|
|
||||||
content: Dom[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Dom = DomText | DomComment | DomNode;
|
|
||||||
|
|
||||||
function escape(str: string): string {
|
|
||||||
const p = document.createElement("p");
|
|
||||||
p.textContent = str;
|
|
||||||
return p.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function domToString(dom: Dom): string {
|
|
||||||
switch (dom.type) {
|
|
||||||
case DomType.Text:
|
|
||||||
return escape(dom.value);
|
|
||||||
case DomType.Comment:
|
|
||||||
return `<!--${dom.value}-->`;
|
|
||||||
case DomType.Node:
|
|
||||||
const content = dom.content.map(domToString).join("");
|
|
||||||
const attrs: string[] = [];
|
|
||||||
for (let [key, value] of Object.entries(dom.attrs)) {
|
|
||||||
if (!(key === "class" && value === "")) {
|
|
||||||
attrs.push(`${key}="${escape(value)}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (content) {
|
|
||||||
return `<${dom.tag}${attrs.length ? " " + attrs.join(" ") : ""}>${content}</${dom.tag}>`;
|
|
||||||
} else {
|
|
||||||
return `<${dom.tag}${attrs.length ? " " + attrs.join(" ") : ""}/>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isProp(tag: string, key: string): boolean {
|
|
||||||
switch (tag) {
|
|
||||||
case "input":
|
|
||||||
return (
|
|
||||||
key === "checked" ||
|
|
||||||
key === "indeterminate" ||
|
|
||||||
key === "value" ||
|
|
||||||
key === "readonly" ||
|
|
||||||
key === "disabled"
|
|
||||||
);
|
|
||||||
case "option":
|
|
||||||
return key === "selected" || key === "disabled";
|
|
||||||
case "textarea":
|
|
||||||
return key === "readonly" || key === "disabled";
|
|
||||||
break;
|
|
||||||
case "button":
|
|
||||||
case "select":
|
|
||||||
case "optgroup":
|
|
||||||
return key === "disabled";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user