mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 02:32:05 +02:00
Initial commit: Core packages
This commit is contained in:
commit
12c29a983b
9512 changed files with 8379910 additions and 0 deletions
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script type="text/javascript" src="qweb.js"></script>
|
||||
<script type="text/javascript" src="qweb2.js"></script>
|
||||
<script type="text/javascript">
|
||||
(function (c) {
|
||||
if (c.time) { return; }
|
||||
var d = {};
|
||||
c.time = function (key) {
|
||||
d[key] = Date.now();
|
||||
};
|
||||
c.timeEnd = function (key) {
|
||||
var end = Date.now(),
|
||||
origin = d[key];
|
||||
delete d[key];
|
||||
if (!origin) { return; }
|
||||
console.log(key + ': ' + (end - origin) + 'ms');
|
||||
};
|
||||
})(window.console);
|
||||
var dict = {
|
||||
session : true,
|
||||
testing : 'yes',
|
||||
name : 'AGR'
|
||||
};
|
||||
console.time("Load template with QWeb");
|
||||
QWeb.add_template("qweb-benchmark.xml");
|
||||
console.timeEnd("Load template with QWeb");
|
||||
|
||||
console.time("Load template with QWeb2");
|
||||
var engine = new QWeb2.Engine("qweb-benchmark.xml")
|
||||
engine.debug = true;
|
||||
console.timeEnd("Load template with QWeb2")
|
||||
|
||||
var iter = 1000;
|
||||
console.log("Rendering...");
|
||||
console.time("Render " + iter + " templates with QWeb");
|
||||
for (var i = 0; i < iter; i++) {
|
||||
var qweb = QWeb.render('benchmark', dict);
|
||||
}
|
||||
console.timeEnd("Render " + iter + " templates with QWeb");
|
||||
|
||||
console.time("Render " + iter + " templates with QWeb2");
|
||||
for (var i = 0; i < iter; i++) {
|
||||
var qweb2 = engine.render('benchmark', dict);
|
||||
}
|
||||
console.timeEnd("Render " + iter + " templates with QWeb2");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
Please, check your console for results
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template">
|
||||
<t t-name="benchmark"><div id="oe_notification" class="oe_notification">
|
||||
<div id="oe_notification_default">
|
||||
<a class="ui-notify-cross ui-notify-close" href="#">x</a>
|
||||
<h1>title</h1>
|
||||
<p>text</p>
|
||||
</div>
|
||||
<div id="oe_notification_alert" class="ui-state-error">
|
||||
<a class="ui-notify-cross ui-notify-close" href="#">x</a>
|
||||
<span style="float:left; margin:2px 5px 0 0;" class="ui-icon ui-icon-alert"></span>
|
||||
<h1>title</h1>
|
||||
<p>text</p>
|
||||
</div>
|
||||
</div>
|
||||
<t t-js="d">
|
||||
d.iter = 'one,two,three,four,five'.split(',')
|
||||
</t>
|
||||
<t t-foreach="iter" t-as="i">
|
||||
<t t-call="benchmark_call">
|
||||
+ <t t-esc="i"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-set="enplus">1</t>
|
||||
<t t-set="novar">true</t>
|
||||
<div t-attf-class="id_#{enplus}"/>
|
||||
<div t-if="testing || true" t-att-class="novar || 'yes'" style="display: none">
|
||||
<t t-set="novar"></t>
|
||||
<t t-set="style">height: 200px; border: 1px solid red;</t>
|
||||
<div t-att="{ 'style' : style, 'disabled' : 'false', 'readonly' : novar or undefined }"/>
|
||||
<t t-foreach="{'my': 'first', 'my2': 'second' }" t-as="v">
|
||||
* <t t-esc="v"/> : <t t-esc="v_value"/>
|
||||
</t>
|
||||
Ok this is good <t t-esc="name"/>!
|
||||
<t t-set="myvar">Hi there !</t>
|
||||
[<t t-raw="myvar"/>]
|
||||
<t t-set="myvar2" t-value="'a,b,c,d,e'.split(',')"/>
|
||||
<t t-foreach="myvar2" t-as="i">
|
||||
(<t t-esc="i"/>)
|
||||
</t>
|
||||
</div>
|
||||
<div id="oe_notification" class="oe_notification">
|
||||
<div id="oe_notification_default">
|
||||
<a class="ui-notify-cross ui-notify-close" href="#">x</a>
|
||||
<h1>title</h1>
|
||||
<p>text</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="benchmark_call">
|
||||
<div id="oe_notification_alert" class="ui-state-error">
|
||||
<a class="ui-notify-cross ui-notify-close" href="#">x</a>
|
||||
<span style="float:left; margin:2px 5px 0 0;" class="ui-icon ui-icon-alert"></span>
|
||||
<h1>Here's your value : (<t t-esc="0"/>) !!</h1>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<templates>
|
||||
<t t-name="static">
|
||||
<div foo="a" bar="b" baz="c"/>
|
||||
</t>
|
||||
<result id="static"><![CDATA[<div foo="a" bar="b" baz="c"></div>]]></result>
|
||||
|
||||
<t t-name="static-void">
|
||||
<img src="/test.jpg" alt="Test" loading="lazy"/>
|
||||
</t>
|
||||
<result id="static-void"><![CDATA[<img src="/test.jpg" alt="Test" loading="lazy"/>]]></result>
|
||||
|
||||
<t t-name="fixed-literal">
|
||||
<div t-att-foo="'bar'"/>
|
||||
</t>
|
||||
<result id="fixed-literal"><![CDATA[<div foo="bar"></div>]]></result>
|
||||
|
||||
<t t-name="fixed-variable">
|
||||
<div t-att-foo="value"/>
|
||||
</t>
|
||||
<params id="fixed-variable">{"value": "ok"}</params>
|
||||
<result id="fixed-variable"><![CDATA[<div foo="ok"></div>]]></result>
|
||||
|
||||
<t t-name="tuple-literal">
|
||||
<div t-att="['foo', 'bar']"/>
|
||||
</t>
|
||||
<result id="tuple-literal"><![CDATA[<div foo="bar"></div>]]></result>
|
||||
|
||||
<t t-name="tuple-variable">
|
||||
<div t-att="value"/>
|
||||
</t>
|
||||
<params id="tuple-variable">{"value": ["foo", "bar"]}</params>
|
||||
<result id="tuple-variable"><![CDATA[<div foo="bar"></div>]]></result>
|
||||
|
||||
<t t-name="object">
|
||||
<div t-att="value"/>
|
||||
</t>
|
||||
<params id="object">{"value": {"a": 1, "b": 2, "c": 3}}</params>
|
||||
<result id="object"><![CDATA[<div a="1" b="2" c="3"></div>]]></result>
|
||||
|
||||
<t t-name="format-literal">
|
||||
<div t-attf-foo="bar"/>
|
||||
</t>
|
||||
<result id="format-literal"><![CDATA[<div foo="bar"></div>]]></result>
|
||||
|
||||
<t t-name="format-value">
|
||||
<div t-attf-foo="b{{value}}r"/>
|
||||
</t>
|
||||
<params id="format-value">{"value": "a"}</params>
|
||||
<result id="format-value"><![CDATA[<div foo="bar"></div>]]></result>
|
||||
|
||||
<t t-name="format-expression">
|
||||
<div t-attf-foo="{{value + 37}}"/>
|
||||
</t>
|
||||
<params id="format-expression">{"value": 5}</params>
|
||||
<result id="format-expression"><![CDATA[<div foo="42"></div>]]></result>
|
||||
|
||||
<t t-name="format-multiple">
|
||||
<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>
|
||||
</t>
|
||||
<params id="format-multiple">{
|
||||
"value1": 0,
|
||||
"value2": 1,
|
||||
"value3": 2
|
||||
}</params>
|
||||
<result id="format-multiple"><![CDATA[
|
||||
<div foo="a 0 is 1 of 2 ]"></div>
|
||||
]]></result>
|
||||
|
||||
<t t-name="various-escapes">
|
||||
<div foo="<foo"
|
||||
t-att-bar="bar"
|
||||
t-attf-baz="<{{baz}}>"
|
||||
t-att="qux"/>
|
||||
</t>
|
||||
<params id="various-escapes"><![CDATA[{
|
||||
"bar": "<bar>",
|
||||
"baz": "\"<baz>\"",
|
||||
"qux": {"qux": "<>"}
|
||||
}]]></params>
|
||||
<result id="various-escapes"><![CDATA[
|
||||
<div foo="<foo" bar="<bar>" baz="<"<baz>">" qux="<>"></div>
|
||||
]]></result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<templates>
|
||||
<t t-name="_basic-callee">ok</t>
|
||||
<t t-name="_callee-printsbody"><t t-esc="0"/></t>
|
||||
<t t-name="_callee-uses-foo"><t t-esc="foo"/></t>
|
||||
|
||||
<t t-name="basic-caller">
|
||||
<t t-call="_basic-callee"/>
|
||||
</t>
|
||||
<result id="basic-caller">ok</result>
|
||||
|
||||
<t t-name="with-unused-body">
|
||||
<t t-call="_basic-callee">WHEEE</t>
|
||||
</t>
|
||||
<result id="with-unused-body">ok</result>
|
||||
|
||||
<t t-name="with-unused-setbody">
|
||||
<t t-call="_basic-callee">
|
||||
<t t-set="qux" t-value="3"/>
|
||||
</t>
|
||||
</t>
|
||||
<result id="with-unused-setbody">ok</result>
|
||||
|
||||
<t t-name="with-used-body">
|
||||
<t t-call="_callee-printsbody">ok</t>
|
||||
</t>
|
||||
<result id="with-used-body">ok</result>
|
||||
|
||||
<t t-name="with-used-setbody">
|
||||
<t t-call="_callee-uses-foo">
|
||||
<t t-set="foo" t-value="'ok'"/>
|
||||
</t>
|
||||
</t>
|
||||
<result id="with-used-setbody">ok</result>
|
||||
|
||||
<t t-name="_call-with-body-arch-lookup">
|
||||
<section t-raw="0"/>
|
||||
</t>
|
||||
|
||||
<t t-name="call-without-body-arch-lookup">
|
||||
<t t-call="_call-with-body-arch-lookup"/>
|
||||
</t>
|
||||
<result id="call-without-body-arch-lookup"><![CDATA[<section></section>]]></result>
|
||||
|
||||
<t t-name="call-with-body-arch-lookup">
|
||||
<t t-call="_call-with-body-arch-lookup">
|
||||
<div><span class="toto" t-esc="value"/></div>
|
||||
</t>
|
||||
</t>
|
||||
<params id="call-with-body-arch-lookup">
|
||||
{"value": "ok"}
|
||||
</params>
|
||||
<result id="call-with-body-arch-lookup"><![CDATA[<section>
|
||||
<div><span class="toto">ok</span></div>
|
||||
</section>]]></result>
|
||||
|
||||
<!--
|
||||
postfix to call removed because Python impl appends all whitespace
|
||||
following called template's root to template result (+= element.tail)
|
||||
-> ends up with bunch of extra whitespace in the middle of the
|
||||
generated content. Could normalize, not sure current impl can be
|
||||
fixed as-is
|
||||
-->
|
||||
<t t-name="inherit-context">
|
||||
<t t-set="foo" t-value="1"/>
|
||||
<t t-call="_callee-uses-foo"/><!-- - <t t-esc="foo"/> -->
|
||||
</t>
|
||||
<result id="inherit-context">1<!-- - 1 --></result>
|
||||
|
||||
<t t-name="scoped-parameter">
|
||||
<t t-call="_basic-callee">
|
||||
<t t-set="foo" t-value="42"/>
|
||||
</t>
|
||||
<!-- should not print anything -->
|
||||
<t t-esc="foo"/>
|
||||
</t>
|
||||
<result id="scoped-parameter">
|
||||
ok
|
||||
</result>
|
||||
|
||||
<t t-name="expression-caller">
|
||||
<t t-call="{{True and '_basic-callee' or 'other'}}"/>
|
||||
</t>
|
||||
<result id="expression-caller">ok</result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<templates>
|
||||
<t t-name="boolean-value-condition">
|
||||
<t t-if="condition">ok</t>
|
||||
</t>
|
||||
<params id="boolean-value-condition">{"condition": true}</params>
|
||||
<result id="boolean-value-condition">ok</result>
|
||||
|
||||
<t t-name="boolean-value-condition-false">
|
||||
<t t-if="condition">fail</t>
|
||||
</t>
|
||||
<params id="boolean-value-condition-false">{"condition": false}</params>
|
||||
<result id="boolean-value-condition-false"/>
|
||||
|
||||
<t t-name="boolean-value-condition-missing">
|
||||
<t t-if="condition">fail</t>
|
||||
</t>
|
||||
<result id="boolean-value-condition-missing"/>
|
||||
|
||||
<t t-name="boolean-value-condition-elif">
|
||||
<t t-if="color == 'black'">black pearl</t>
|
||||
<t t-elif="color == 'yellow'">yellow submarine</t>
|
||||
<t t-elif="color == 'red'">red is dead</t>
|
||||
<t t-else="">beer</t>
|
||||
</t>
|
||||
<params id="boolean-value-condition-elif">{"color": "red"}</params>
|
||||
<result id="boolean-value-condition-elif">red is dead</result>
|
||||
|
||||
<t t-name="boolean-value-condition-else">
|
||||
<div><span>begin</span><t t-if="condition">ok</t>
|
||||
<t t-else="">ok-else</t><span>end</span></div>
|
||||
</t>
|
||||
<params id="boolean-value-condition-else">{"condition": true}</params>
|
||||
<result id="boolean-value-condition-else"><![CDATA[<div><span>begin</span>ok<span>end</span></div>]]></result>
|
||||
|
||||
<t t-name="boolean-value-condition-false-else">
|
||||
<div><span>begin</span><t t-if="condition">fail</t>
|
||||
<t t-else="">fail-else</t><span>end</span></div>
|
||||
</t>
|
||||
<params id="boolean-value-condition-false-else">{"condition": false}</params>
|
||||
<result id="boolean-value-condition-false-else"><![CDATA[<div><span>begin</span>fail-else<span>end</span></div>]]></result>
|
||||
|
||||
<t t-name="comment-branching">
|
||||
<t t-if="condition == 'if'">if</t>
|
||||
<t t-elif="condition == 'elif1'">elif1</t>
|
||||
<!-- Comment ignored PART OF THE TEST !!! -->
|
||||
<t t-elif="condition == 'elif2'">elif2</t>
|
||||
<t t-else="">else</t>
|
||||
</t>
|
||||
<params id="comment-branching">{"condition": "elif1"}</params>
|
||||
<result id="comment-branching"><![CDATA[elif1]]></result>
|
||||
|
||||
<t t-name="comment-branching-1">
|
||||
<t t-if="condition == 'if'">if</t>
|
||||
<t t-elif="condition == 'elif1'">elif1</t>
|
||||
<!-- Comment ignored PART OF THE TEST !!! -->
|
||||
<t t-elif="condition == 'elif2'">elif2</t>
|
||||
<t t-else="">else</t>
|
||||
</t>
|
||||
<params id="comment-branching-1">{"condition": "elif2"}</params>
|
||||
<result id="comment-branching-1"><![CDATA[elif2]]></result>
|
||||
|
||||
<t t-name="comment-branching-2">
|
||||
<div t-if="condition == 'if'">if</div><!-- Comment ignored PART OF THE TEST !!! --><div>sometext</div>
|
||||
</t>
|
||||
<params id="comment-branching-2">{"condition": "if"}</params>
|
||||
<result id="comment-branching-2"><![CDATA[<div>if</div><div>sometext</div>]]></result>
|
||||
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<templates>
|
||||
<!-- js-only -->
|
||||
<t t-name="jquery-extend">
|
||||
<ul><li>one</li></ul>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul" t-operation="append"><li>3</li></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul li:first-child" t-operation="replace"><li>2</li></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul" t-operation="prepend"><li>1</li></t>
|
||||
<t t-jquery="ul" t-operation="before"><hr/></t>
|
||||
<t t-jquery="ul" t-operation="after"><hr/></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul">this.attr('class', 'main');</t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul" t-operation="attributes"><attribute name="title" value="Main Title" /></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="ul" t-operation="attributes"><attribute name="name">main-ul</attribute></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="hr:eq(1)" t-operation="replace"><footer></footer></t>
|
||||
</t>
|
||||
<t t-extend="jquery-extend">
|
||||
<t t-jquery="footer" t-operation="inner"><b>[[end]]</b></t>
|
||||
</t>
|
||||
<result id="jquery-extend"><![CDATA[
|
||||
<hr/><ul class="main" title="Main Title" name="main-ul"><li>1</li><li>2</li><li>3</li></ul><footer><b>[[end]]</b></footer>
|
||||
]]></result>
|
||||
|
||||
<t t-name="jquery-extend-clone" t-extend="jquery-extend">
|
||||
<t t-jquery="ul" t-operation="append"><li>[[cloned template]]</li></t>
|
||||
</t>
|
||||
<result id="jquery-extend-clone"><![CDATA[
|
||||
<hr/><ul class="main" title="Main Title" name="main-ul"><li>1</li><li>2</li><li>3</li><li>[[cloned template]]</li></ul><footer><b>[[end]]</b></footer>
|
||||
]]></result>
|
||||
|
||||
|
||||
<t t-name="a">
|
||||
<div><span>Hi</span></div>
|
||||
</t>
|
||||
<t t-name="b" t-extend="a">
|
||||
<t t-jquery="span" t-operation="after"><i>World</i></t>
|
||||
</t>
|
||||
<t t-name="c" t-extend="b">
|
||||
<t t-jquery="span" t-operation="replace"><span>Hello</span></t>
|
||||
</t>
|
||||
<result id="a"><![CDATA[
|
||||
<div><span>Hi</span></div>
|
||||
]]></result>
|
||||
<result id="b"><![CDATA[
|
||||
<div><span>Hi</span><i>World</i></div>
|
||||
]]></result>
|
||||
<result id="c"><![CDATA[
|
||||
<div><span>Hello</span><i>World</i></div>
|
||||
]]></result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<templates xml:space="preserve">
|
||||
<t t-name="iter-items">
|
||||
<t t-foreach="[3, 2, 1]" t-as="item">
|
||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]</t>
|
||||
</t>
|
||||
<result id="iter-items">
|
||||
[0: 3 3]
|
||||
[1: 2 2]
|
||||
[2: 1 1]
|
||||
</result>
|
||||
|
||||
<t t-name="iter-position">
|
||||
<t t-foreach="5" t-as="item">
|
||||
-<t t-if="item_first"> first</t><t t-if="item_last"> last</t> (<t t-esc="item_parity"/>)</t>
|
||||
</t>
|
||||
<result id="iter-position">
|
||||
- first (even)
|
||||
- (odd)
|
||||
- (even)
|
||||
- (odd)
|
||||
- last (even)
|
||||
</result>
|
||||
|
||||
<!-- test integer param -->
|
||||
<t t-name="iter-int">
|
||||
<t t-foreach="3" t-as="item">
|
||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]</t>
|
||||
</t>
|
||||
<result id="iter-int">
|
||||
[0: 0 0]
|
||||
[1: 1 1]
|
||||
[2: 2 2]
|
||||
</result>
|
||||
|
||||
<!-- test dict param -->
|
||||
<t t-name="iter-dict">
|
||||
<t t-foreach="value" t-as="item">
|
||||
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/> - <t t-esc="item_parity"/>]</t>
|
||||
</t>
|
||||
<params id="iter-dict">{"value": {"a": 1, "b": 2, "c": 3}}</params>
|
||||
<result id="iter-dict">
|
||||
[0: a 1 - even]
|
||||
[1: b 2 - odd]
|
||||
[2: c 3 - even]
|
||||
</result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<templates>
|
||||
<t t-name="_callee-asc"><Año t-att-falló="'agüero'" t-raw="0"/></t>
|
||||
<t t-name="_callee-uses-foo"><span t-esc="foo">foo default</span></t>
|
||||
<t t-name="_callee-asc-toto"><div t-raw="toto">toto default</div></t>
|
||||
|
||||
<t t-name="caller">
|
||||
<t t-foreach="[4,5,6]" t-as="value">
|
||||
<span t-esc="value"/>
|
||||
<t t-call="_callee-asc">
|
||||
<t t-call="_callee-uses-foo">
|
||||
<t t-set="foo" t-value="'aaa'"/>
|
||||
</t>
|
||||
<t t-call="_callee-uses-foo"/>
|
||||
<t t-set="foo" t-value="'bbb'"/>
|
||||
<t t-call="_callee-uses-foo"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-call="_callee-asc-toto"/>
|
||||
<t t-set="toto"><t t-set="truc" t-value="'bbb'"/><i t-att-notruc="not truc or None" t-att-truc="bool(truc)">i</i></t>
|
||||
<t t-call="_callee-asc-toto"/>
|
||||
</t>
|
||||
|
||||
<result id="caller"><![CDATA[
|
||||
<span>4</span>
|
||||
<Año falló="agüero">
|
||||
<span>aaa</span>
|
||||
<span>foo default</span>
|
||||
|
||||
<span>bbb</span>
|
||||
</Año>
|
||||
|
||||
<span>5</span>
|
||||
<Año falló="agüero">
|
||||
<span>aaa</span>
|
||||
<span>foo default</span>
|
||||
|
||||
<span>bbb</span>
|
||||
</Año>
|
||||
|
||||
<span>6</span>
|
||||
<Año falló="agüero">
|
||||
<span>aaa</span>
|
||||
<span>foo default</span>
|
||||
|
||||
<span>bbb</span>
|
||||
</Año>
|
||||
|
||||
<div>toto default</div>
|
||||
|
||||
<div><i truc="True">i</i></div>
|
||||
]]></result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<templates>
|
||||
<!-- esc, evaluates and returns @t-esc after having xml-escaped it -->
|
||||
<t t-name="esc-literal">
|
||||
<t t-esc="'ok'"/>
|
||||
</t>
|
||||
<result id="esc-literal">ok</result>
|
||||
|
||||
<t t-name="esc-variable">
|
||||
<t t-esc="var"/>
|
||||
</t>
|
||||
<params id="esc-variable">{"var": "ok"}</params>
|
||||
<result id="esc-variable">ok</result>
|
||||
|
||||
<t t-name="esc-toescape">
|
||||
<t t-esc="var"/>
|
||||
</t>
|
||||
<params id="esc-toescape"><![CDATA[{"var": "<ok>"}]]></params>
|
||||
<result id="esc-toescape"><![CDATA[<ok>]]></result>
|
||||
<t t-name="esc-node">
|
||||
<span t-esc="'ok'"/>
|
||||
</t>
|
||||
<result id="esc-node"><![CDATA[<span>ok</span>]]></result>
|
||||
|
||||
|
||||
<!-- raw, evaluates and returns @t-raw directly (no escaping) -->
|
||||
<t t-name="raw-literal">
|
||||
<t t-raw="'ok'"/>
|
||||
</t>
|
||||
<result id="raw-literal">ok</result>
|
||||
|
||||
<t t-name="raw-number">
|
||||
<t t-raw="1"/>
|
||||
</t>
|
||||
<result id="raw-number">1</result>
|
||||
|
||||
<t t-name="raw-variable">
|
||||
<t t-raw="var"/>
|
||||
</t>
|
||||
<params id="raw-variable">{"var": "ok"}</params>
|
||||
<result id="raw-variable">ok</result>
|
||||
|
||||
<t t-name="raw-notescaped">
|
||||
<t t-raw="var"/>
|
||||
</t>
|
||||
<params id="raw-notescaped"><![CDATA[{"var": "<ok>"}]]></params>
|
||||
<result id="raw-notescaped"><![CDATA[<ok>]]></result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<templates>
|
||||
<t t-name="set-from-attribute-literal">
|
||||
<t t-set="value" t-value="'ok'"/>
|
||||
<t t-esc="value"/>
|
||||
</t>
|
||||
<result id="set-from-attribute-literal">
|
||||
ok
|
||||
</result>
|
||||
|
||||
<t t-name="set-from-body-literal">
|
||||
<t t-set="value">ok</t>
|
||||
<t t-esc="value"/>
|
||||
</t>
|
||||
<result id="set-from-body-literal">
|
||||
ok
|
||||
</result>
|
||||
|
||||
<t t-name="set-from-attribute-lookup">
|
||||
<t t-set="stuff" t-value="value"/>
|
||||
<t t-esc="stuff"/>
|
||||
</t>
|
||||
<params id="set-from-attribute-lookup">
|
||||
{"value": "ok"}
|
||||
</params>
|
||||
<result id="set-from-attribute-lookup">
|
||||
ok
|
||||
</result>
|
||||
|
||||
<t t-name="set-from-body-lookup">
|
||||
<t t-set="stuff">
|
||||
<t t-esc="value"/>
|
||||
</t>
|
||||
<t t-esc="stuff"/>
|
||||
</t>
|
||||
<params id="set-from-body-lookup">
|
||||
{"value": "ok"}
|
||||
</params>
|
||||
<result id="set-from-body-lookup">
|
||||
ok
|
||||
</result>
|
||||
|
||||
<t t-name="set-from-body-arch-lookup">
|
||||
<t t-set="stuff"><div><span t-esc="value"/></div></t>
|
||||
<t t-raw="stuff"/>
|
||||
</t>
|
||||
<params id="set-from-body-arch-lookup">
|
||||
{"value": "ok"}
|
||||
</params>
|
||||
<result id="set-from-body-arch-lookup"><![CDATA[<div><span>ok</span></div>]]></result>
|
||||
|
||||
<t t-name="set-empty-body">
|
||||
<t t-set="stuff"/>
|
||||
<t t-esc="stuff"/>
|
||||
</t>
|
||||
<result id="set-empty-body"/>
|
||||
|
||||
<t t-name="t-value-priority">
|
||||
<t t-set="value" t-value="1">2</t>
|
||||
<t t-esc="value"/>
|
||||
</t>
|
||||
<result id="t-value-priority">1</result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<templates>
|
||||
<t t-name="fixed-literal">
|
||||
<Año t-att-falló="'agüero'"/>
|
||||
</t>
|
||||
<result id="fixed-literal"><![CDATA[<Año falló="agüero"></Año>]]></result>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<templates>
|
||||
<t t-name="date-simple"><t t-esc='value' /></t>
|
||||
<params id="date-simple">{"value": "1988-09-16"}</params>
|
||||
<result id="date-simple">1988-09-16</result>
|
||||
|
||||
<t t-name="datetime-simple"><t t-esc='value' /></t>
|
||||
<params id="datetime-simple">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-simple">1988-09-16 14:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-datetime"><t t-esc='value' t-options="{'widget': 'datetime'}" /></t>
|
||||
<params id="datetime-widget-datetime">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-datetime">09/16/1988 16:00:00</result>
|
||||
|
||||
<t t-name="datetime-widget-date"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date">{"value": "1988-09-16 14:00:00"}</params>
|
||||
<result id="datetime-widget-date">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz2"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz2">{"value": "1988-09-16 01:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz2">09/16/1988</result>
|
||||
|
||||
<t t-name="datetime-widget-date-tz"><t t-esc='value' t-options="{'widget': 'date'}" /></t>
|
||||
<params id="datetime-widget-date-tz">{"value": "1988-09-16 23:00:00"}</params>
|
||||
<result id="datetime-widget-date-tz">09/17/1988</result>
|
||||
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="/web/static/lib/jquery/jquery.js"></script>
|
||||
<link rel="stylesheet" href="/web/static/lib/qunit/qunit.css" type="text/css" media="screen"/>
|
||||
<script type="text/javascript" src="/web/static/lib/qunit/qunit.js"></script>
|
||||
|
||||
<script type="text/javascript" src="qweb2.js"></script>
|
||||
|
||||
<script>
|
||||
QWeb = new QWeb2.Engine();
|
||||
function trim(s) {
|
||||
return s.replace(/(^\s+|\s+$)/g, '');
|
||||
}
|
||||
function render(template, context) {
|
||||
return trim(QWeb.render(template, context)).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the template file, and executes all the test template in a
|
||||
* qunit module $title
|
||||
*/
|
||||
function test(title, template) {
|
||||
QUnit.module(title, {
|
||||
setup: function () {
|
||||
var self = this;
|
||||
this.qweb = new QWeb2.Engine();
|
||||
QUnit.stop();
|
||||
this.qweb.add_template(template, function (_, doc) {
|
||||
self.doc = doc;
|
||||
QUnit.start();
|
||||
})
|
||||
}
|
||||
});
|
||||
QUnit.test('autotest', function (assert) {
|
||||
var templates = this.qweb.templates;
|
||||
for (var template in templates) {
|
||||
if (!templates.hasOwnProperty(template)) { continue; }
|
||||
// ignore templates whose name starts with _, they're
|
||||
// helpers/internal
|
||||
if (/^_/.test(template)) { continue; }
|
||||
|
||||
var params = this.doc.querySelector('params#' + template);
|
||||
var args = params ? JSON.parse(params.textContent) : {};
|
||||
|
||||
var results = this.doc.querySelector('result#' + template);
|
||||
assert.equal(
|
||||
trim(this.qweb.render(template, args)),
|
||||
trim(results.textContent),
|
||||
template);
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(function() {
|
||||
test("Output", 'qweb-test-output.xml');
|
||||
test("Context-setting", 'qweb-test-set.xml');
|
||||
test("Conditionals", 'qweb-test-conditionals.xml');
|
||||
test("Attributes manipulation", 'qweb-test-attributes.xml');
|
||||
test("Template calling (to the faraway pages)",
|
||||
'qweb-test-call.xml');
|
||||
test("Foreach", 'qweb-test-foreach.xml');
|
||||
test("Global", 'qweb-test-global.xml');
|
||||
|
||||
test('Template inheritance', 'qweb-test-extend.xml');
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
</html>
|
||||
435
odoo-bringout-oca-ocb-web/web/static/lib/qweb/qweb.js
Normal file
435
odoo-bringout-oca-ocb-web/web/static/lib/qweb/qweb.js
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
Copyright (c) 2013, Fabien Meghazi
|
||||
|
||||
Released under the MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------
|
||||
// QWeb javascript
|
||||
//---------------------------------------------------------
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
String parsing
|
||||
if (window.DOMParser) {
|
||||
parser=new DOMParser();
|
||||
xmlDoc=parser.parseFromString(text,"text/xml");
|
||||
} else {
|
||||
xmlDoc=new ActiveXObject("Msxml2.DOMDocument.4.0");
|
||||
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
|
||||
Which versions to try, it's confusing...
|
||||
xmlDoc.async="false";
|
||||
xmlDoc.async=false;
|
||||
xmlDoc.preserveWhiteSpace=true;
|
||||
xmlDoc.load("f.xml");
|
||||
xmlDoc.loadXML(text); ?
|
||||
}
|
||||
|
||||
Support space in IE by reparsing the responseText
|
||||
xmlhttp.responseXML.loadXML(xmlhttp.responseText); ?
|
||||
|
||||
Preprocess: (nice optimization)
|
||||
preprocess by flattening all non t- element to a TEXT_NODE.
|
||||
count the number of "\n" in text nodes to give an aproximate LINE NUMBER on elements for error reporting
|
||||
if from IE HTMLDOM use if(a[i].specified) to avoid 88 empty attributes per element during the preprocess,
|
||||
|
||||
implement t-trim 'left' 'right' 'both', is it needed ? inner=render_trim(l_inner.join(), t_att)
|
||||
|
||||
Ruby/python: to backport from javascript to python/ruby render_node to use regexp, factorize foreach %var, t-att test for tuple(attname,value)
|
||||
|
||||
DONE
|
||||
we reintroduced t-att-id, no more t-esc-id because of the new convention t-att="["id","val"]"
|
||||
*/
|
||||
|
||||
var QWeb = {
|
||||
templates:{},
|
||||
prefix:"t",
|
||||
reg:new RegExp(),
|
||||
tag:{},
|
||||
att:{},
|
||||
ValueException: function (value, message) {
|
||||
this.value = value;
|
||||
this.message = message;
|
||||
},
|
||||
eval_object:function(e, v) {
|
||||
// TODO: Currently this will also replace and, or, ... in strings. Try
|
||||
// 'hi boys and girls' != '' and 1 == 1 -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
|
||||
// try to find a solution without tokenizing
|
||||
e = '(' + e + ')';
|
||||
e = e.replace(/\band\b/g, " && ");
|
||||
e = e.replace(/\bor\b/g, " || ");
|
||||
e = e.replace(/\bgt\b/g, " > ");
|
||||
e = e.replace(/\bgte\b/g, " >= ");
|
||||
e = e.replace(/\blt\b/g, " < ");
|
||||
e = e.replace(/\blte\b/g, " <= ");
|
||||
if (v[e] != undefined) {
|
||||
return v[e];
|
||||
} else {
|
||||
with (v) return eval(e);
|
||||
}
|
||||
},
|
||||
eval_str:function(e, v) {
|
||||
var r = this.eval_object(e, v);
|
||||
r = (typeof(r) == "undefined" || r == null) ? "" : r.toString();
|
||||
return e == "0" ? v["0"] : r;
|
||||
},
|
||||
eval_format:function(e, v) {
|
||||
var m, src = e.split(/#/), r = src[0];
|
||||
for (var i = 1; i < src.length; i++) {
|
||||
if (m = src[i].match(/^{(.*)}(.*)/)) {
|
||||
r += this.eval_str(m[1], v) + m[2];
|
||||
} else {
|
||||
r += "#" + src[i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
},
|
||||
eval_bool:function(e, v) {
|
||||
return !!this.eval_object(e, v);
|
||||
},
|
||||
trim : function(v, mode) {
|
||||
if (!v || !mode) return v;
|
||||
switch (mode) {
|
||||
case 'both':
|
||||
return v.replace(/^\s*|\s*$/g, "");
|
||||
case "left":
|
||||
return v.replace(/^\s*/, "");
|
||||
case "right":
|
||||
return v.replace(/\s*$/, "");
|
||||
}
|
||||
throw new QWeb.ValueException(
|
||||
mode, "unknown trimming mode, trim mode must follow the pattern '[inner] (left|right|both)'");
|
||||
},
|
||||
escape_text:function(s) {
|
||||
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
},
|
||||
escape_att:function(s) {
|
||||
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||
},
|
||||
render_node : function(e, v, inner_trim) {
|
||||
if (e.nodeType == 3) {
|
||||
return inner_trim ? this.trim(e.data, inner_trim) : e.data;
|
||||
}
|
||||
if (e.nodeType == 1) {
|
||||
var g_att = {};
|
||||
var t_att = {};
|
||||
var t_render = null;
|
||||
var a = e.attributes;
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
var an = a[i].name,av = a[i].value;
|
||||
var m;
|
||||
if (m = an.match(this.reg)) {
|
||||
var n = m[1];
|
||||
if (n == "eval") {
|
||||
n = m[2].substring(1);
|
||||
av = this.eval_str(av, v);
|
||||
}
|
||||
var f;
|
||||
if (f = this.att[n]) {
|
||||
this[f](e, t_att, g_att, v, m[2], av);
|
||||
} else if (f = this.tag[n]) {
|
||||
t_render = f;
|
||||
}
|
||||
t_att[n] = av;
|
||||
} else {
|
||||
g_att[an] = av;
|
||||
}
|
||||
}
|
||||
if (inner_trim && !t_att["trim"]) {
|
||||
t_att["trim"] = "inner " + inner_trim;
|
||||
}
|
||||
if (t_render) {
|
||||
return this[t_render](e, t_att, g_att, v);
|
||||
}
|
||||
return this.render_element(e, t_att, g_att, v);
|
||||
}
|
||||
return "";
|
||||
},
|
||||
render_element:function(e, t_att, g_att, v) {
|
||||
var inner = "", ec = e.childNodes, trim = t_att["trim"], inner_trim;
|
||||
if (trim) {
|
||||
if (/\binner\b/.test(trim)) {
|
||||
inner_trim = true;
|
||||
if (trim == 'inner') {
|
||||
trim = "both";
|
||||
}
|
||||
}
|
||||
var tm = /\b(both|left|right)\b/.exec(trim);
|
||||
if (tm) trim = tm[1];
|
||||
}
|
||||
for (var i = 0; i < ec.length; i++) {
|
||||
inner += inner_trim ? this.trim(this.render_node(ec[i], v, inner_trim ? trim : null), trim) : this.render_node(ec[i], v, inner_trim ? trim : null);
|
||||
}
|
||||
if (trim && !inner_trim) {
|
||||
inner = this.trim(inner, trim);
|
||||
}
|
||||
if (e.tagName == this.prefix) {
|
||||
return inner;
|
||||
}
|
||||
var att = "";
|
||||
for (var an in g_att) {
|
||||
att += " " + an + '="' + this.escape_att(g_att[an]) + '"';
|
||||
}
|
||||
// Some IE versions have problems with closed tags
|
||||
var opentag = !!t_att['opentag'] && this.eval_bool(t_att["opentag"], v);
|
||||
return inner.length || opentag ? "<" + e.tagName + att + ">" + inner + "</" + e.tagName + ">" : "<" + e.tagName + att + "/>";
|
||||
},
|
||||
render_att_att:function(e, t_att, g_att, v, ext, av) {
|
||||
if (ext) {
|
||||
var attv = this.eval_object(av, v);
|
||||
if (attv != null) {
|
||||
g_att[ext.substring(1)] = attv.toString();
|
||||
}
|
||||
} else {
|
||||
var o = this.eval_object(av, v);
|
||||
if (o != null) {
|
||||
// TODO: http://bonsaiden.github.com/JavaScript-Garden/#types.typeof
|
||||
if (o.constructor == Array && o.length > 1 && o[1] != null) {
|
||||
g_att[o[0]] = new String(o[1]);
|
||||
} else if (o.constructor == Object) {
|
||||
for (var i in o) {
|
||||
if(o[i]!=null) {
|
||||
g_att[i] = new String(o[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
render_att_attf:function(e, t_att, g_att, v, ext, av) {
|
||||
g_att[ext.substring(1)] = this.eval_format(av, v);
|
||||
},
|
||||
render_tag_raw:function(e, t_att, g_att, v) {
|
||||
return this.eval_str(t_att["raw"], v);
|
||||
},
|
||||
render_tag_rawf:function(e, t_att, g_att, v) {
|
||||
return this.eval_format(t_att["rawf"], v);
|
||||
},
|
||||
/*
|
||||
* Idea: if the name of the tag != t render the tag around the value <a name="a" t-esc="label"/>
|
||||
*/
|
||||
render_tag_esc:function(e, t_att, g_att, v) {
|
||||
return this.escape_text(this.eval_str(t_att["esc"], v));
|
||||
},
|
||||
render_tag_escf:function(e, t_att, g_att, v) {
|
||||
return this.escape_text(this.eval_format(t_att["escf"], v));
|
||||
},
|
||||
render_tag_if:function(e, t_att, g_att, v) {
|
||||
return this.eval_bool(t_att["if"], v) ? this.render_element(e, t_att, g_att, v) : "";
|
||||
},
|
||||
render_tag_set:function(e, t_att, g_att, v) {
|
||||
var ev = t_att["value"];
|
||||
if (ev && ev.constructor != Function) {
|
||||
v[t_att["set"]] = this.eval_object(ev, v);
|
||||
} else {
|
||||
v[t_att["set"]] = this.render_element(e, t_att, g_att, v);
|
||||
}
|
||||
return "";
|
||||
},
|
||||
render_tag_call:function(e, t_att, g_att, v) {
|
||||
var d = v;
|
||||
if (!t_att["import"]) {
|
||||
d = {};
|
||||
for (var i in v) {
|
||||
d[i] = v[i];
|
||||
}
|
||||
}
|
||||
d["0"] = this.render_element(e, t_att, g_att, d);
|
||||
return this.render(t_att["call"], d);
|
||||
},
|
||||
render_tag_js:function(e, t_att, g_att, v) {
|
||||
var dict_name = t_att["js"] || "dict";
|
||||
v[dict_name] = v;
|
||||
var r = this.eval_str(this.render_element(e, t_att, g_att, v), v);
|
||||
delete(v[dict_name]);
|
||||
return r || '';
|
||||
},
|
||||
/**
|
||||
* Renders a foreach loop (@t-foreach).
|
||||
*
|
||||
* Adds the following elements to its context, where <code>${name}</code>
|
||||
* is specified via <code>@t-as</code>:
|
||||
* * <code>${name}</code> The current element itself
|
||||
* * <code>${name}_value</code> Same as <code>${name}</code>
|
||||
* * <code>${name}_index</code> The 0-based index of the current element
|
||||
* * <code>${name}_first</code> Whether the current element is the first one
|
||||
* * <code>${name}_parity</code> odd|even (as strings)
|
||||
* * <code>${name}_all</code> The iterated collection itself
|
||||
*
|
||||
* If the collection being iterated is an array, also adds:
|
||||
* * <code>${name}_last</code> Whether the current element is the last one
|
||||
* * All members of the current object
|
||||
*
|
||||
* If the collection being iterated is an object, the value is actually the object's key
|
||||
*
|
||||
* @param e ?
|
||||
* @param t_att attributes of the element being <code>t-foreach</code>'d
|
||||
* @param g_att ?
|
||||
* @param old_context the context in which the foreach is evaluated
|
||||
*/
|
||||
render_tag_foreach:function(e, t_att, g_att, old_context) {
|
||||
var expr = t_att["foreach"];
|
||||
var enu = this.eval_object(expr, old_context);
|
||||
var ru = [];
|
||||
if (enu) {
|
||||
var val = t_att['as'] || expr.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
var context = {};
|
||||
for (var i in old_context) {
|
||||
context[i] = old_context[i];
|
||||
}
|
||||
context[val + "_all"] = enu;
|
||||
var val_value = val + "_value",
|
||||
val_index = val + "_index",
|
||||
val_first = val + "_first",
|
||||
val_last = val + "_last",
|
||||
val_parity = val + "_parity";
|
||||
var size = enu.length;
|
||||
if (size) {
|
||||
context[val + "_size"] = size;
|
||||
for (var j = 0; j < size; j++) {
|
||||
var cur = enu[j];
|
||||
context[val_value] = cur;
|
||||
context[val_index] = j;
|
||||
context[val_first] = j == 0;
|
||||
context[val_last] = j + 1 == size;
|
||||
context[val_parity] = (j % 2 == 1 ? 'odd' : 'even');
|
||||
if (cur.constructor == Object) {
|
||||
for (var k in cur) {
|
||||
context[k] = cur[k];
|
||||
}
|
||||
}
|
||||
context[val] = cur;
|
||||
var r = this.render_element(e, t_att, g_att, context);
|
||||
ru.push(r);
|
||||
}
|
||||
} else {
|
||||
var index = 0;
|
||||
for (cur in enu) {
|
||||
context[val_value] = cur;
|
||||
context[val_index] = index;
|
||||
context[val_first] = index == 0;
|
||||
context[val_parity] = (index % 2 == 1 ? 'odd' : 'even');
|
||||
context[val] = cur;
|
||||
ru.push(this.render_element(e, t_att, g_att, context));
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return ru.join("");
|
||||
} else {
|
||||
return "qweb: foreach " + expr + " not found.";
|
||||
}
|
||||
},
|
||||
hash:function() {
|
||||
var l = [], m;
|
||||
for (var i in this) {
|
||||
if (m = i.match(/render_tag_(.*)/)) {
|
||||
this.tag[m[1]] = i;
|
||||
l.push(m[1]);
|
||||
} else if (m = i.match(/render_att_(.*)/)) {
|
||||
this.att[m[1]] = i;
|
||||
l.push(m[1]);
|
||||
}
|
||||
}
|
||||
l.sort(function(a, b) {
|
||||
return a.length > b.length ? -1 : 1;
|
||||
});
|
||||
var s = "^" + this.prefix + "-(eval|" + l.join("|") + "|.*)(.*)$";
|
||||
this.reg = new RegExp(s);
|
||||
},
|
||||
/**
|
||||
* returns the correct XMLHttpRequest instance for the browser, or null if
|
||||
* it was not able to build any XHR instance.
|
||||
*
|
||||
* @returns XMLHttpRequest|MSXML2.XMLHTTP.3.0|null
|
||||
*/
|
||||
get_xhr:function () {
|
||||
if (window.XMLHttpRequest) {
|
||||
return new window.XMLHttpRequest();
|
||||
}
|
||||
try {
|
||||
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
|
||||
} catch(e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
load_xml:function(s) {
|
||||
var xml;
|
||||
if (s[0] == "<") {
|
||||
/*
|
||||
manque ca pour sarrisa
|
||||
if(window.DOMParser){
|
||||
mozilla
|
||||
if(!window.DOMParser){
|
||||
var doc = Sarissa.getDomDocument();
|
||||
doc.loadXML(sXml);
|
||||
return doc;
|
||||
};
|
||||
};
|
||||
*/
|
||||
} else {
|
||||
var req = this.get_xhr();
|
||||
if (req) {
|
||||
req.open("GET", s, false);
|
||||
req.send(null);
|
||||
//if ie r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
|
||||
xml = req.responseXML;
|
||||
/*
|
||||
TODO
|
||||
if intsernetexploror
|
||||
getdomimplmentation() for try catch
|
||||
responseXML.getImplet
|
||||
d=domimple()
|
||||
d.preserverWhitespace=1
|
||||
d.loadXML()
|
||||
|
||||
xml.preserverWhitespace=1
|
||||
xml.loadXML(r.reponseText)
|
||||
*/
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
},
|
||||
add_template:function(e) {
|
||||
// TODO: keep sources so we can implement reload()
|
||||
this.hash();
|
||||
if (e.constructor == String) {
|
||||
e = this.load_xml(e);
|
||||
}
|
||||
|
||||
var ec = e.documentElement ? e.documentElement.childNodes : ( e.childNodes ? e.childNodes : [] );
|
||||
|
||||
for (var i = 0; i < ec.length; i++) {
|
||||
var n = ec[i];
|
||||
if (n.nodeType == 1) {
|
||||
var name = n.getAttribute(this.prefix + "-name");
|
||||
this.templates[name] = n;
|
||||
}
|
||||
}
|
||||
},
|
||||
render:function(name, v) {
|
||||
var e;
|
||||
if (e = this.templates[name]) {
|
||||
return this.render_node(e, v);
|
||||
}
|
||||
return "template " + name + " not found";
|
||||
}
|
||||
};
|
||||
|
||||
874
odoo-bringout-oca-ocb-web/web/static/lib/qweb/qweb2.js
Normal file
874
odoo-bringout-oca-ocb-web/web/static/lib/qweb/qweb2.js
Normal file
|
|
@ -0,0 +1,874 @@
|
|||
/*
|
||||
Copyright (c) 2013, Fabien Meghazi
|
||||
|
||||
Released under the MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// TODO: trim support
|
||||
// TODO: line number -> https://bugzilla.mozilla.org/show_bug.cgi?id=618650
|
||||
// TODO: templates orverwritten could be called by t-call="__super__" ?
|
||||
// TODO: t-set + t-value + children node == scoped variable ?
|
||||
var QWeb2 = {
|
||||
expressions_cache: { },
|
||||
RESERVED_WORDS: 'true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date'.split(','),
|
||||
ACTIONS_PRECEDENCE: 'foreach,if,elif,else,call,set,tag,out,esc,raw,js,debug,log'.split(','),
|
||||
WORD_REPLACEMENT: {
|
||||
'and': '&&',
|
||||
'or': '||',
|
||||
'gt': '>',
|
||||
'gte': '>=',
|
||||
'lt': '<',
|
||||
'lte': '<='
|
||||
},
|
||||
VOID_ELEMENTS: 'area,base,br,col,embed,hr,img,input,keygen,link,menuitem,meta,param,source,track,wbr'.split(','),
|
||||
tools: {
|
||||
exception: function(message, context) {
|
||||
context = context || {};
|
||||
var prefix = 'QWeb2';
|
||||
if (context.template) {
|
||||
prefix += " - template['" + context.template + "']";
|
||||
}
|
||||
throw new Error(prefix + ": " + message);
|
||||
},
|
||||
warning : function(message) {
|
||||
if (typeof(window) !== 'undefined' && window.console) {
|
||||
window.console.warn(message);
|
||||
}
|
||||
},
|
||||
trim: function(s, mode) {
|
||||
switch (mode) {
|
||||
case "left":
|
||||
return s.replace(/^\s*/, "");
|
||||
case "right":
|
||||
return s.replace(/\s*$/, "");
|
||||
default:
|
||||
return s.replace(/^\s*|\s*$/g, "");
|
||||
}
|
||||
},
|
||||
js_escape: function(s, noquotes) {
|
||||
return (noquotes ? '' : "'") + s.replace(/\r?\n/g, "\\n").replace(/'/g, "\\'") + (noquotes ? '' : "'");
|
||||
},
|
||||
html_escape: function(s) {
|
||||
if (s == null) {
|
||||
return '';
|
||||
}
|
||||
return _.escape(s);
|
||||
},
|
||||
markup(s) {
|
||||
return new _Markup(s);
|
||||
},
|
||||
gen_attribute: function(o) {
|
||||
if (o !== null && o !== undefined) {
|
||||
if (o.constructor === Array) {
|
||||
if (o[1] !== null && o[1] !== undefined) {
|
||||
return this.format_attribute(o[0], o[1]);
|
||||
}
|
||||
} else if (typeof o === 'object') {
|
||||
var r = '';
|
||||
for (var k in o) {
|
||||
if (o.hasOwnProperty(k)) {
|
||||
r += this.gen_attribute([k, o[k]]);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
format_attribute: function(name, value) {
|
||||
// we want to ensure `value` is *not* a `Markup`, because markup-safe
|
||||
// strings are not necessarily attributes-safe.
|
||||
const attrvalue = value == null ? '' : this.html_escape(String(value));
|
||||
return ` ${name}="${attrvalue}"`;
|
||||
},
|
||||
extend: function(dst, src, exclude) {
|
||||
for (var p in src) {
|
||||
if (src.hasOwnProperty(p) && !(exclude && this.arrayIndexOf(exclude, p) !== -1)) {
|
||||
dst[p] = src[p];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
},
|
||||
arrayIndexOf : function(array, item) {
|
||||
for (var i = 0, ilen = array.length; i < ilen; i++) {
|
||||
if (array[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
get_element_sibling: function(node, dom_attr) {
|
||||
// This helper keeps support for IE8 which does not
|
||||
// implement DOMNode.(previous|next)ElementSibling
|
||||
var sibling = node[dom_attr];
|
||||
while (sibling && sibling.nodeType !== 1) {
|
||||
sibling = sibling[dom_attr];
|
||||
}
|
||||
return sibling;
|
||||
},
|
||||
xml_node_to_string : function(node, childs_only) {
|
||||
if (childs_only) {
|
||||
var childs = node.childNodes, r = [];
|
||||
for (var i = 0, ilen = childs.length; i < ilen; i++) {
|
||||
r.push(this.xml_node_to_string(childs[i]));
|
||||
}
|
||||
return r.join('');
|
||||
} else {
|
||||
// avoid XMLSerializer with text node for IE
|
||||
if (node.nodeType == 3) {
|
||||
return node.data;
|
||||
}
|
||||
if (typeof XMLSerializer !== 'undefined') {
|
||||
return (new XMLSerializer()).serializeToString(node);
|
||||
} else {
|
||||
switch(node.nodeType) {
|
||||
case 1: return node.outerHTML;
|
||||
case 4: return '<![CDATA[' + node.data + ']]>';
|
||||
case 8: return '<!-- ' + node.data + '-->';
|
||||
}
|
||||
throw new Error('Unknown node type ' + node.nodeType);
|
||||
}
|
||||
}
|
||||
},
|
||||
call: function(context, template, old_dict, _import, callback) {
|
||||
var new_dict = this.extend({}, old_dict);
|
||||
new_dict['__caller__'] = old_dict['__template__'];
|
||||
if (callback) {
|
||||
new_dict[0] = this.markup(callback(context, new_dict));
|
||||
}
|
||||
return this.markup(context.engine._render(template, new_dict));
|
||||
},
|
||||
foreach: function(context, enu, as, old_dict, callback) {
|
||||
if (enu != null) {
|
||||
var index, jlen, cur;
|
||||
var new_dict = this.extend({}, old_dict);
|
||||
new_dict[as + "_all"] = enu;
|
||||
var as_value = as + "_value",
|
||||
as_index = as + "_index",
|
||||
as_first = as + "_first",
|
||||
as_last = as + "_last",
|
||||
as_parity = as + "_parity";
|
||||
if (enu instanceof Array) {
|
||||
var size = enu.length;
|
||||
new_dict[as + "_size"] = size;
|
||||
for (index = 0, jlen = enu.length; index < jlen; index++) {
|
||||
cur = enu[index];
|
||||
new_dict[as_value] = cur;
|
||||
new_dict[as_index] = index;
|
||||
new_dict[as_first] = index === 0;
|
||||
new_dict[as_last] = index + 1 === size;
|
||||
new_dict[as_parity] = (index % 2 == 1 ? 'odd' : 'even');
|
||||
if (cur && cur.constructor === Object) {
|
||||
this.extend(new_dict, cur);
|
||||
}
|
||||
new_dict[as] = cur;
|
||||
callback(context, new_dict);
|
||||
}
|
||||
} else if (enu.constructor == Number) {
|
||||
var _enu = [];
|
||||
for (var i = 0; i < enu; i++) {
|
||||
_enu.push(i);
|
||||
}
|
||||
this.foreach(context, _enu, as, old_dict, callback);
|
||||
} else {
|
||||
index = 0;
|
||||
for (var k in enu) {
|
||||
if (enu.hasOwnProperty(k)) {
|
||||
cur = enu[k];
|
||||
new_dict[as_value] = cur;
|
||||
new_dict[as_index] = index;
|
||||
new_dict[as_first] = index === 0;
|
||||
new_dict[as_parity] = (index % 2 == 1 ? 'odd' : 'even');
|
||||
new_dict[as] = k;
|
||||
callback(context, new_dict);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_.each(Object.keys(old_dict), function(z) {
|
||||
old_dict[z] = new_dict[z];
|
||||
});
|
||||
} else {
|
||||
this.exception("No enumerator given to foreach", context);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QWeb2.Engine = (function() {
|
||||
function Engine() {
|
||||
// TODO: handle prefix at template level : t-prefix="x", don't forget to lowercase it
|
||||
this.prefix = 't';
|
||||
this.debug = false;
|
||||
this.templates_resources = []; // TODO: implement this.reload()
|
||||
this.templates = {};
|
||||
this.compiled_templates = {};
|
||||
this.extend_templates = {};
|
||||
this.default_dict = {};
|
||||
this.tools = QWeb2.tools;
|
||||
this.jQuery = window.jQuery;
|
||||
this.reserved_words = QWeb2.RESERVED_WORDS.slice(0);
|
||||
this.actions_precedence = QWeb2.ACTIONS_PRECEDENCE.slice(0);
|
||||
this.void_elements = QWeb2.VOID_ELEMENTS.slice(0);
|
||||
this.word_replacement = QWeb2.tools.extend({}, QWeb2.WORD_REPLACEMENT);
|
||||
this.preprocess_node = null;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this.add_template(arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
QWeb2.tools.extend(Engine.prototype, {
|
||||
/**
|
||||
* Add a template to the engine
|
||||
*
|
||||
* @param {String|Document} template Template as string or url or DOM Document
|
||||
* @param {Function} [callback] Called when the template is loaded, force async request
|
||||
*/
|
||||
add_template : function(template, callback) {
|
||||
var self = this;
|
||||
this.templates_resources.push(template);
|
||||
if (template.constructor === String) {
|
||||
return this.load_xml(template, function (err, xDoc) {
|
||||
if (err) {
|
||||
if (callback) {
|
||||
return callback(err);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
self.add_template(xDoc, callback);
|
||||
});
|
||||
}
|
||||
template = this.preprocess(template);
|
||||
var ec = (template.documentElement && template.documentElement.childNodes) || template.childNodes || [];
|
||||
for (var i = 0; i < ec.length; i++) {
|
||||
var node = ec[i];
|
||||
if (node.nodeType === 1) {
|
||||
var name = node.getAttribute(this.prefix + '-name');
|
||||
var extend = node.getAttribute(this.prefix + '-extend');
|
||||
if (name && extend) {
|
||||
// Clone template and extend it
|
||||
if (!this.templates[extend]) {
|
||||
return this.tools.exception("Can't clone undefined template '" + extend + "' to create '" + name + "'");
|
||||
}
|
||||
this.templates[name] = this.templates[extend].cloneNode(true);
|
||||
this.extend_templates[name] = (this.extend_templates[extend] || []).slice();
|
||||
extend = name;
|
||||
name = undefined;
|
||||
}
|
||||
if (name) {
|
||||
this.templates[name] = node;
|
||||
this.compiled_templates[name] = null;
|
||||
} else if (extend) {
|
||||
delete(this.compiled_templates[extend]);
|
||||
if (this.extend_templates[extend]) {
|
||||
this.extend_templates[extend].push(node);
|
||||
} else {
|
||||
this.extend_templates[extend] = [node];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
callback(null, template);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
preprocess: function(doc) {
|
||||
/**
|
||||
* Preprocess a template's document at load time.
|
||||
* This method is mostly used for template sanitization but could
|
||||
* also be overloaded for extended features such as translations, ...
|
||||
* Throws an exception if a template is invalid.
|
||||
*
|
||||
* @param {Document} doc Document containg the loaded templates
|
||||
* @return {Document} Returns the pre-processed/sanitized template
|
||||
*/
|
||||
var self = this;
|
||||
var childs = (doc.documentElement && doc.documentElement.childNodes) || doc.childNodes || [];
|
||||
|
||||
// Check for load errors
|
||||
for (var i = 0; i < childs.length; i++) {
|
||||
var node = childs[i];
|
||||
if (node.nodeType === 1 && node.nodeName == 'parsererror') {
|
||||
return this.tools.exception(node.innerText);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize t-elif and t-else directives
|
||||
var tbranch = doc.querySelectorAll('[t-elif], [t-else]');
|
||||
for (var i = 0, ilen = tbranch.length; i < ilen; i++) {
|
||||
var node = tbranch[i];
|
||||
var prev_elem = self.tools.get_element_sibling(node, 'previousSibling');
|
||||
var pattr = function(name) { return prev_elem.getAttribute(name); }
|
||||
var nattr = function(name) { return +!!node.getAttribute(name); }
|
||||
if (prev_elem && (pattr('t-if') || pattr('t-elif'))) {
|
||||
if (pattr('t-foreach')) {
|
||||
return self.tools.exception("Error: t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
|
||||
}
|
||||
if (['t-if', 't-elif', 't-else'].map(nattr).reduce(function(a, b) { return a + b; }) > 1) {
|
||||
return self.tools.exception("Error: only one conditional branching directive is allowed per node");
|
||||
}
|
||||
// All text nodes between branch nodes are removed
|
||||
var text_node;
|
||||
while ((text_node = node.previousSibling) !== prev_elem) {
|
||||
if (text_node.nodeType !== 8 && self.tools.trim(text_node.nodeValue)) {
|
||||
return self.tools.exception("Error: text is not allowed between branching directives");
|
||||
}
|
||||
// IE <= 11.0 doesn't support ChildNode.remove
|
||||
text_node.parentNode.removeChild(text_node);
|
||||
}
|
||||
} else {
|
||||
return self.tools.exception("Error: t-elif and t-else directives must be preceded by a t-if or t-elif directive");
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
},
|
||||
load_xml : function(s, callback) {
|
||||
var self = this;
|
||||
var async = !!callback;
|
||||
s = this.tools.trim(s);
|
||||
if (s.charAt(0) === '<') {
|
||||
var tpl = this.load_xml_string(s);
|
||||
if (callback) {
|
||||
callback(null, tpl);
|
||||
}
|
||||
return tpl;
|
||||
} else {
|
||||
var req = this.get_xhr();
|
||||
if (this.debug) {
|
||||
s += '?debug=' + (new Date()).getTime(); // TODO fme: do it properly in case there's already url parameters
|
||||
}
|
||||
req.open('GET', s, async);
|
||||
if (async) {
|
||||
req.addEventListener("load", function() {
|
||||
// 0, not being a valid HTTP status code, is used by browsers
|
||||
// to indicate success for a non-http xhr response
|
||||
// (for example, using the file:// protocol)
|
||||
// https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=331610
|
||||
if (req.status == 200 || req.status == 0) {
|
||||
callback(null, self._parse_from_request(req));
|
||||
} else {
|
||||
callback(new Error("Can't load template " + s + ", http status " + req.status));
|
||||
}
|
||||
});
|
||||
}
|
||||
req.send(null);
|
||||
if (!async) {
|
||||
return this._parse_from_request(req);
|
||||
}
|
||||
}
|
||||
},
|
||||
_parse_from_request: function(req) {
|
||||
var xDoc = req.responseXML;
|
||||
if (xDoc) {
|
||||
if (!xDoc.documentElement) {
|
||||
throw new Error("QWeb2: This xml document has no root document : " + xDoc.responseText);
|
||||
}
|
||||
if (xDoc.documentElement.nodeName == "parsererror") {
|
||||
throw new Error("QWeb2: Could not parse document :" + xDoc.documentElement.childNodes[0].nodeValue);
|
||||
}
|
||||
return xDoc;
|
||||
} else {
|
||||
return this.load_xml_string(req.responseText);
|
||||
}
|
||||
},
|
||||
load_xml_string : function(s) {
|
||||
if (window.DOMParser) {
|
||||
var dp = new DOMParser();
|
||||
var r = dp.parseFromString(s, "text/xml");
|
||||
if (r.body && r.body.firstChild && r.body.firstChild.nodeName == 'parsererror') {
|
||||
throw new Error("QWeb2: Could not parse document :" + r.body.innerText);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
var xDoc;
|
||||
try {
|
||||
xDoc = new ActiveXObject("MSXML2.DOMDocument");
|
||||
} catch (e) {
|
||||
throw new Error("Could not find a DOM Parser: " + e.message);
|
||||
}
|
||||
xDoc.async = false;
|
||||
xDoc.preserveWhiteSpace = true;
|
||||
xDoc.loadXML(s);
|
||||
return xDoc;
|
||||
},
|
||||
has_template : function(template) {
|
||||
return !!this.templates[template];
|
||||
},
|
||||
get_xhr : function() {
|
||||
if (window.XMLHttpRequest) {
|
||||
return new window.XMLHttpRequest();
|
||||
}
|
||||
try {
|
||||
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
|
||||
} catch (e) {
|
||||
throw new Error("Could not get XHR");
|
||||
}
|
||||
},
|
||||
compile : function(node) {
|
||||
var e = new QWeb2.Element(this, node);
|
||||
var template = node.getAttribute(this.prefix + '-name');
|
||||
return " /* 'this' refers to Qweb2.Engine instance */\n" +
|
||||
" var context = { engine : this, template : " + (this.tools.js_escape(template)) + " };\n" +
|
||||
" dict = dict || {};\n" +
|
||||
" dict['__template__'] = '" + template + "';\n" +
|
||||
" var r = [];\n" +
|
||||
" /* START TEMPLATE */" +
|
||||
(this.debug ? "" : " try {\n") +
|
||||
(e.compile()) + "\n" +
|
||||
" /* END OF TEMPLATE */" +
|
||||
(this.debug ? "" : " } catch(error) {\n" +
|
||||
" if (console && console.exception) console.exception(error);\n" +
|
||||
" context.engine.tools.exception('Runtime Error: ' + error, context);\n") +
|
||||
(this.debug ? "" : " }\n") +
|
||||
" return r.join('');";
|
||||
},
|
||||
render : function(template, dict) {
|
||||
dict = dict || {};
|
||||
QWeb2.tools.extend(dict, this.default_dict);
|
||||
/*if (this.debug && window['console'] !== undefined) {
|
||||
console.time("QWeb render template " + template);
|
||||
}*/
|
||||
var r = this._render(template, dict);
|
||||
/*if (this.debug && window['console'] !== undefined) {
|
||||
console.timeEnd("QWeb render template " + template);
|
||||
}*/
|
||||
return r;
|
||||
},
|
||||
_render : function(template, dict) {
|
||||
if (this.compiled_templates[template]) {
|
||||
return this.compiled_templates[template].apply(this, [dict || {}]);
|
||||
} else if (this.templates[template]) {
|
||||
var ext;
|
||||
if (ext = this.extend_templates[template]) {
|
||||
var extend_node;
|
||||
while (extend_node = ext.shift()) {
|
||||
this.extend(template, extend_node);
|
||||
}
|
||||
}
|
||||
var code = this.compile(this.templates[template]), tcompiled;
|
||||
try {
|
||||
tcompiled = new Function(['dict'], code);
|
||||
} catch (error) {
|
||||
if (this.debug && window.console) {
|
||||
console.log(code);
|
||||
}
|
||||
this.tools.exception("Error evaluating template: " + error, { template: template });
|
||||
}
|
||||
if (!tcompiled) {
|
||||
this.tools.exception("Error evaluating template: (IE?)" + error, { template: template });
|
||||
}
|
||||
this.compiled_templates[template] = tcompiled;
|
||||
return this.render(template, dict);
|
||||
} else {
|
||||
return this.tools.exception("Template '" + template + "' not found");
|
||||
}
|
||||
},
|
||||
extend : function(template, extend_node) {
|
||||
var jQuery = this.jQuery;
|
||||
if (!jQuery) {
|
||||
return this.tools.exception("Can't extend template " + template + " without jQuery");
|
||||
}
|
||||
var template_dest = this.templates[template];
|
||||
for (var i = 0, ilen = extend_node.childNodes.length; i < ilen; i++) {
|
||||
var child = extend_node.childNodes[i];
|
||||
if (child.nodeType === 1) {
|
||||
var jquery = child.getAttribute(this.prefix + '-jquery'),
|
||||
operation = child.getAttribute(this.prefix + '-operation'),
|
||||
target,
|
||||
error_msg = "Error while extending template '" + template;
|
||||
if (jquery) {
|
||||
target = jQuery(jquery, template_dest);
|
||||
if (!target.length && window.console) {
|
||||
console.error("Can't find '" + jquery + "' when extending template " + template);
|
||||
}
|
||||
} else {
|
||||
this.tools.exception(error_msg + "No expression given");
|
||||
}
|
||||
error_msg += "' (expression='" + jquery + "') : ";
|
||||
if (operation) {
|
||||
var allowed_operations = "append,prepend,before,after,replace,inner,attributes".split(',');
|
||||
if (this.tools.arrayIndexOf(allowed_operations, operation) == -1) {
|
||||
this.tools.exception(error_msg + "Invalid operation : '" + operation + "'");
|
||||
}
|
||||
operation = {'replace' : 'replaceWith', 'inner' : 'html'}[operation] || operation;
|
||||
if (operation === 'attributes') {
|
||||
jQuery('attribute', child).each(function () {
|
||||
var attrib = jQuery(this);
|
||||
target.attr(attrib.attr('name'), attrib.text() || attrib.attr('value'));
|
||||
});
|
||||
} else {
|
||||
target[operation](child.cloneNode(true).childNodes);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
var f = new Function(['$', 'document'], this.tools.xml_node_to_string(child, true));
|
||||
} catch(error) {
|
||||
return this.tools.exception("Parse " + error_msg + error);
|
||||
}
|
||||
try {
|
||||
f.apply(target, [jQuery, template_dest.ownerDocument]);
|
||||
} catch(error) {
|
||||
return this.tools.exception("Runtime " + error_msg + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Engine;
|
||||
})();
|
||||
|
||||
QWeb2.Element = (function() {
|
||||
function Element(engine, node) {
|
||||
this.engine = engine;
|
||||
this.node = node;
|
||||
this.tag = node.tagName;
|
||||
this.actions = {tag: this.tag};
|
||||
this.actions_done = [];
|
||||
this.attributes = {};
|
||||
this.children = [];
|
||||
this._top = [];
|
||||
this._bottom = [];
|
||||
this._indent = 1;
|
||||
this.process_children = true;
|
||||
this.is_void_element = ~QWeb2.tools.arrayIndexOf(this.engine.void_elements, this.tag);
|
||||
var childs = this.node.childNodes;
|
||||
if (childs) {
|
||||
for (var i = 0, ilen = childs.length; i < ilen; i++) {
|
||||
this.children.push(new QWeb2.Element(this.engine, childs[i]));
|
||||
}
|
||||
}
|
||||
var attrs = this.node.attributes;
|
||||
if (attrs) {
|
||||
for (var j = 0, jlen = attrs.length; j < jlen; j++) {
|
||||
var attr = attrs[j];
|
||||
var name = attr.name;
|
||||
var m = name.match(new RegExp("^" + this.engine.prefix + "-(.+)"));
|
||||
if (m) {
|
||||
name = m[1];
|
||||
if (name === 'name') {
|
||||
continue;
|
||||
}
|
||||
if (name.match(/^attf?(-.*)?/)) {
|
||||
this.attributes[m[0]] = attr.value;
|
||||
} else {
|
||||
this.actions[name] = attr.value;
|
||||
}
|
||||
} else {
|
||||
this.attributes[name] = attr.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.engine.preprocess_node) {
|
||||
this.engine.preprocess_node.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
QWeb2.tools.extend(Element.prototype, {
|
||||
compile : function() {
|
||||
var r = [],
|
||||
instring = false,
|
||||
lines = this._compile().split('\n');
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
var m, line = lines[i];
|
||||
if (m = line.match(/^(\s*)\/\/@string=(.*)/)) {
|
||||
if (instring) {
|
||||
if (this.engine.debug) {
|
||||
// Split string lines in indented r.push arguments
|
||||
r.push((m[2].indexOf("\\n") != -1 ? "',\n\t" + m[1] + "'" : '') + m[2]);
|
||||
} else {
|
||||
r.push(m[2]);
|
||||
}
|
||||
} else {
|
||||
r.push(m[1] + "r.push('" + m[2]);
|
||||
instring = true;
|
||||
}
|
||||
} else {
|
||||
if (instring) {
|
||||
r.push("');\n");
|
||||
}
|
||||
instring = false;
|
||||
r.push(line + '\n');
|
||||
}
|
||||
}
|
||||
return r.join('');
|
||||
},
|
||||
_compile : function() {
|
||||
switch (this.node.nodeType) {
|
||||
case 3:
|
||||
case 4:
|
||||
this.top_string(this.node.data);
|
||||
break;
|
||||
case 1:
|
||||
this.compile_element();
|
||||
}
|
||||
var r = this._top.join('');
|
||||
if (this.process_children) {
|
||||
for (var i = 0, ilen = this.children.length; i < ilen; i++) {
|
||||
var child = this.children[i];
|
||||
child._indent = this._indent;
|
||||
r += child._compile();
|
||||
}
|
||||
}
|
||||
r += this._bottom.join('');
|
||||
return r;
|
||||
},
|
||||
format_expression : function(e) {
|
||||
/* Naive format expression builder. Replace reserved words and variables to dict[variable]
|
||||
* Does not handle spaces before dot yet, and causes problems for anonymous functions. Use t-js="" for that */
|
||||
if (QWeb2.expressions_cache[e]) {
|
||||
return QWeb2.expressions_cache[e];
|
||||
}
|
||||
var chars = e.split(''),
|
||||
instring = '',
|
||||
invar = '',
|
||||
invar_pos = 0,
|
||||
r = '';
|
||||
chars.push(' ');
|
||||
for (var i = 0, ilen = chars.length; i < ilen; i++) {
|
||||
var c = chars[i];
|
||||
if (instring.length) {
|
||||
if (c === instring && chars[i - 1] !== "\\") {
|
||||
instring = '';
|
||||
}
|
||||
} else if (c === '"' || c === "'") {
|
||||
instring = c;
|
||||
} else if (c.match(/[a-zA-Z_\$]/) && !invar.length) {
|
||||
invar = c;
|
||||
invar_pos = i;
|
||||
continue;
|
||||
} else if (c.match(/\W/) && invar.length) {
|
||||
// TODO: Should check for possible spaces before dot
|
||||
if (chars[invar_pos - 1] !== '.' && QWeb2.tools.arrayIndexOf(this.engine.reserved_words, invar) < 0) {
|
||||
invar = this.engine.word_replacement[invar] || ("dict['" + invar + "']");
|
||||
}
|
||||
r += invar;
|
||||
invar = '';
|
||||
} else if (invar.length) {
|
||||
invar += c;
|
||||
continue;
|
||||
}
|
||||
r += c;
|
||||
}
|
||||
r = r.slice(0, -1);
|
||||
QWeb2.expressions_cache[e] = r;
|
||||
return r;
|
||||
},
|
||||
format_str: function (e) {
|
||||
if (e == '0') {
|
||||
return 'dict[0]';
|
||||
}
|
||||
return this.format_expression(e);
|
||||
},
|
||||
string_interpolation : function(s) {
|
||||
var _this = this;
|
||||
if (!s) {
|
||||
return "''";
|
||||
}
|
||||
function append_literal(s) {
|
||||
s && r.push(_this.engine.tools.js_escape(s));
|
||||
}
|
||||
|
||||
var re = /#{(.+?)}|{{(.+?)}}/g, start = 0, r = [], m;
|
||||
while (m = re.exec(s)) {
|
||||
// extract literal string between previous and current match
|
||||
append_literal(s.slice(start, re.lastIndex - m[0].length));
|
||||
// extract matched expression
|
||||
r.push('(' + this.format_str(m[2] || m[1]) + ')');
|
||||
// update position of new matching
|
||||
start = re.lastIndex;
|
||||
}
|
||||
// remaining text after last expression
|
||||
append_literal(s.slice(start));
|
||||
|
||||
return r.join(' + ');
|
||||
},
|
||||
indent : function() {
|
||||
return this._indent++;
|
||||
},
|
||||
dedent : function() {
|
||||
if (this._indent !== 0) {
|
||||
return this._indent--;
|
||||
}
|
||||
},
|
||||
get_indent : function() {
|
||||
return new Array(this._indent + 1).join("\t");
|
||||
},
|
||||
top : function(s) {
|
||||
return this._top.push(this.get_indent() + s + '\n');
|
||||
},
|
||||
top_string : function(s) {
|
||||
return this._top.push(this.get_indent() + "//@string=" + this.engine.tools.js_escape(s, true) + '\n');
|
||||
},
|
||||
bottom : function(s) {
|
||||
return this._bottom.unshift(this.get_indent() + s + '\n');
|
||||
},
|
||||
bottom_string : function(s) {
|
||||
return this._bottom.unshift(this.get_indent() + "//@string=" + this.engine.tools.js_escape(s, true) + '\n');
|
||||
},
|
||||
compile_element : function() {
|
||||
for (var i = 0, ilen = this.engine.actions_precedence.length; i < ilen; i++) {
|
||||
var a = this.engine.actions_precedence[i];
|
||||
if (a in this.actions) {
|
||||
var value = this.actions[a];
|
||||
var key = 'compile_action_' + a;
|
||||
if (this[key]) {
|
||||
this[key](value);
|
||||
} else if (this.engine[key]) {
|
||||
this.engine[key].call(this, value);
|
||||
} else {
|
||||
this.engine.tools.exception("No handler method for action '" + a + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
compile_action_tag : function() {
|
||||
if (this.tag.toLowerCase() !== this.engine.prefix) {
|
||||
this.top_string("<" + this.tag);
|
||||
for (var a in this.attributes) {
|
||||
var v = this.attributes[a];
|
||||
var d = a.split('-');
|
||||
if (d[0] === this.engine.prefix && d.length > 1) {
|
||||
if (d.length === 2) {
|
||||
this.top("r.push(context.engine.tools.gen_attribute(" + (this.format_expression(v)) + "));");
|
||||
} else {
|
||||
this.top("r.push(context.engine.tools.gen_attribute(['" + d.slice(2).join('-') + "', (" +
|
||||
(d[1] === 'att' ? this.format_expression(v) : this.string_interpolation(v)) + ")]));");
|
||||
}
|
||||
} else {
|
||||
this.top_string(this.engine.tools.gen_attribute([a, v]));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.actions.opentag === 'true' || (!this.children.length && this.is_void_element)) {
|
||||
// We do not enforce empty content on void elements
|
||||
// because QWeb rendering is not necessarily html.
|
||||
this.top_string("/>");
|
||||
} else {
|
||||
this.top_string(">");
|
||||
this.bottom_string("</" + this.tag + ">");
|
||||
}
|
||||
}
|
||||
},
|
||||
compile_action_if : function(value) {
|
||||
this.top("if (" + (this.format_expression(value)) + ") {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_elif : function(value) {
|
||||
this.top("else if (" + (this.format_expression(value)) + ") {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_else : function(value) {
|
||||
this.top("else {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_foreach : function(value) {
|
||||
var as = this.actions['as'] || value.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
//TODO: exception if t-as not valid
|
||||
this.top("context.engine.tools.foreach(context, " + (this.format_expression(value)) + ", " + (this.engine.tools.js_escape(as)) + ", dict, function(context, dict) {");
|
||||
this.bottom("});");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_call : function(value) {
|
||||
if (this.children.length === 0) {
|
||||
return this.top("r.push(context.engine.tools.call(context, " + (this.string_interpolation(value)) + ", dict));");
|
||||
} else {
|
||||
this.top("r.push(context.engine.tools.call(context, " + (this.string_interpolation(value)) + ", dict, null, function(context, dict) {");
|
||||
this.bottom("}));");
|
||||
this.indent();
|
||||
this.top("var r = [];");
|
||||
return this.bottom("return context.engine.tools.markup(r.join(''));");
|
||||
}
|
||||
},
|
||||
compile_action_set : function(value) {
|
||||
var variable = this.format_expression(value);
|
||||
if (this.actions['value']) {
|
||||
if (this.children.length) {
|
||||
this.engine.tools.warning("@set with @value plus node chidren found. Children are ignored.");
|
||||
}
|
||||
this.top(variable + " = (" + (this.format_expression(this.actions['value'])) + ");");
|
||||
this.process_children = false;
|
||||
} else {
|
||||
if (this.children.length === 0) {
|
||||
this.top(variable + " = '';");
|
||||
} else if (this.children.length === 1 && this.children[0].node.nodeType === 3) {
|
||||
this.top(variable + " = " + (this.engine.tools.js_escape(this.children[0].node.data)) + ";");
|
||||
this.process_children = false;
|
||||
} else {
|
||||
this.top(variable + " = (function(dict) {");
|
||||
this.bottom("})(dict);");
|
||||
this.indent();
|
||||
this.top("var r = [];");
|
||||
this.bottom("return context.engine.tools.markup(r.join(''));");
|
||||
}
|
||||
}
|
||||
},
|
||||
compile_action_esc : function(value) {
|
||||
return this.compile_action_out(value);
|
||||
},
|
||||
compile_action_out(value) {
|
||||
this.top("var t = " + this.format_str(value) + ";");
|
||||
this.top("if (t != null) r.push(context.engine.tools.html_escape(t));");
|
||||
this.top("else {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_raw : function(value) {
|
||||
let e = this.node;
|
||||
while (e.parentElement && !e.getAttribute('t-name')) {
|
||||
e = e.parentElement;
|
||||
}
|
||||
console.warn(
|
||||
"Found deprecated directive '@t-raw=\"%s\"' in "
|
||||
+ "template '%s'. Replace by @t-out, and explicitely wrap content in "
|
||||
+ "utils.Markup if necessary.", value, e.getAttribute('t-name'));
|
||||
this.top("var t = " + this.format_str(value) + ";");
|
||||
this.top("if (t != null) r.push(t);");
|
||||
this.top("else {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_js : function(value) {
|
||||
this.top("(function(" + value + ") {");
|
||||
this.bottom("})(dict);");
|
||||
this.indent();
|
||||
var lines = this.engine.tools.xml_node_to_string(this.node, true).split(/\r?\n/);
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
this.top(lines[i]);
|
||||
}
|
||||
this.process_children = false;
|
||||
},
|
||||
compile_action_debug : function(value) {
|
||||
this.top("debugger;");
|
||||
},
|
||||
compile_action_log : function(value) {
|
||||
this.top("console.log(" + this.format_expression(value) + ");");
|
||||
}
|
||||
});
|
||||
return Element;
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue