mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Dropped support for namespaces and updated docs and tests
This commit is contained in:
parent
bd8f760afa
commit
da274538ab
384
README.md
384
README.md
@ -1,7 +1,7 @@
|
||||
raptor-templates
|
||||
================
|
||||
|
||||
Raptor Templates is a streaming, asynchronous, [high performance](https://github.com/raptorjs3/templating-benchmarks), _HTML-based_ templating language that can be used in Node.js or in the browser. Because the Raptor Templates compiler understands the structure of the HTML document, the directives in template files are less obtrusive and more powerful .
|
||||
Raptor Templates is an extensible, streaming, asynchronous, [high performance](https://github.com/raptorjs3/templating-benchmarks), _HTML-based_ templating language that can be used in Node.js or in the browser. Raptor Templates was founded on the philosophy that an HTML-based templating language is more natural and intuitive for generating HTML. Because the Raptor Templates compiler understands the structure of the HTML document, the directives in template files are less obtrusive and more powerful. In addition, Raptor Templates allows you to introduce your own custom tags and custom attributes to extend the HTML grammar (much like [Web Components](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/)--only you can use it now).
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
@ -50,12 +50,12 @@ Raptor Templates is a streaming, asynchronous, [high performance](https://github
|
||||
- [Tag Renderer](#tag-renderer)
|
||||
- [raptor-taglib.json](#raptor-taglibjson)
|
||||
- [Sample Taglib](#sample-taglib)
|
||||
- [Taglib Namespace](#taglib-namespace)
|
||||
- [Defining Tags](#defining-tags)
|
||||
- [Defining Attributes](#defining-attributes)
|
||||
- [Scanning for Tags](#scanning-for-tags)
|
||||
- [Nested Tags](#nested-tags)
|
||||
- [Taglib Discovery](#taglib-discovery)
|
||||
- [FAQ](#faq)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
@ -65,12 +65,12 @@ A basic template with text replacement, looping and conditionals is shown below:
|
||||
```html
|
||||
Hello ${data.name}!
|
||||
|
||||
<ul c:if="notEmpty(data.colors)">
|
||||
<li style="color: $color" c:for="color in data.colors">
|
||||
<ul c-if="notEmpty(data.colors)">
|
||||
<li style="color: $color" c-for="color in data.colors">
|
||||
$color
|
||||
</li>
|
||||
</ul>
|
||||
<div c:else>
|
||||
<div c-else>
|
||||
No colors!
|
||||
</div>
|
||||
```
|
||||
@ -122,17 +122,17 @@ Raptor Templates also supports custom tags so you can easily extend the HTML gra
|
||||
```html
|
||||
Welcome to Raptor Templates!
|
||||
|
||||
<ui:tabs>
|
||||
<ui:tab label="Home">
|
||||
<ui-tabs>
|
||||
<ui-tab label="Home">
|
||||
Content for Home
|
||||
</ui:tab>
|
||||
<ui:tab label="Profile">
|
||||
</ui-tab>
|
||||
<ui-tab label="Profile">
|
||||
Content for Profile
|
||||
</ui:tab>
|
||||
<ui:tab label="Messages">
|
||||
</ui-tab>
|
||||
<ui-tab label="Messages">
|
||||
Content for Messages
|
||||
</ui:tab>
|
||||
</ui:tabs>
|
||||
</ui-tab>
|
||||
</ui-tabs>
|
||||
```
|
||||
|
||||
The above template is a very simple way to generate the much more complicated HTML output shown below:
|
||||
@ -396,14 +396,14 @@ Almost all of the Raptor templating directives can be used as either an attribut
|
||||
_Applying directives using attributes:_
|
||||
```html
|
||||
<!-- Colors available -->
|
||||
<ul c:if="notEmpty(colors)">
|
||||
<li c:for="color in colors">
|
||||
<ul c-if="notEmpty(colors)">
|
||||
<li c-for="color in colors">
|
||||
$color
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- No colors available -->
|
||||
<div c:if="empty(colors)">
|
||||
<div c-if="empty(colors)">
|
||||
No colors!
|
||||
</div>
|
||||
```
|
||||
@ -411,25 +411,25 @@ _Applying directives using attributes:_
|
||||
_Applying directives using elements:_
|
||||
```html
|
||||
<!-- Colors available -->
|
||||
<c:if test="notEmpty(colors)">
|
||||
<c-if test="notEmpty(colors)">
|
||||
<ul>
|
||||
<c:for each="color in colors">
|
||||
<c-for each="color in colors">
|
||||
<li>
|
||||
$color
|
||||
</li>
|
||||
</c:for>
|
||||
</c-for>
|
||||
</ul>
|
||||
</c:if>
|
||||
</c-if>
|
||||
|
||||
<!-- No colors available -->
|
||||
<c:if test="empty(colors)">
|
||||
<c-if test="empty(colors)">
|
||||
<div>
|
||||
No colors!
|
||||
</div>
|
||||
</c:if>
|
||||
</c-if>
|
||||
```
|
||||
|
||||
The disadvantage of using elements to control structural logic is that they change the nesting of the elements which can impact readability. For this reason it is often more suitable to use attributes.
|
||||
The disadvantage of using elements to control structural logic is that they change the nesting of the elements which can impact readability. For this reason it is often more suitable to apply directives as attributes.
|
||||
|
||||
## Text Replacement
|
||||
|
||||
@ -472,12 +472,26 @@ JavaScript Operator | Raptor Equivalent
|
||||
`<=` | `le`
|
||||
`>=` | `ge`
|
||||
|
||||
## Includes
|
||||
|
||||
Other Raptor Template files can be included using the `<c:include>` tag and a relative path. For example:
|
||||
For example, both of the following are valid and equivalent:
|
||||
|
||||
```html
|
||||
<c:include template="./greeting" name="Frank" count="30"/>
|
||||
<div c-if="searchResults.length > 100">
|
||||
Show More
|
||||
</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<div c-if="searchResults.length gt 100">
|
||||
Show More
|
||||
</div>
|
||||
```
|
||||
|
||||
## Includes
|
||||
|
||||
Other Raptor Template files can be included using the `<c-include>` tag and a relative path. For example:
|
||||
|
||||
```html
|
||||
<c-include template="./greeting.rhtml" name="Frank" count="30"/>
|
||||
```
|
||||
|
||||
## Variables
|
||||
@ -485,102 +499,102 @@ Other Raptor Template files can be included using the `<c:include>` tag and a re
|
||||
Input data passed to a template is made available using a special `data` variable. It's possible to declare your own variables as shown in the following sample code:
|
||||
|
||||
```html
|
||||
<c:var name="name" value="data.name.toUpperCase()" />
|
||||
<c-var name="name" value="data.name.toUpperCase()" />
|
||||
```
|
||||
|
||||
## Conditionals
|
||||
|
||||
### if...else-if...else
|
||||
|
||||
Any element or fragment of HTML can be made conditional using the c:if, c:else-if or c:else directive.
|
||||
Any element or fragment of HTML can be made conditional using the `c-if`, `c-else-if` or `c-else` directive.
|
||||
|
||||
_Applied as an attribute:_
|
||||
_Applied as attributes:_
|
||||
```html
|
||||
<!--Simple if-->
|
||||
<div c:if="someCondition">
|
||||
<div c-if="someCondition">
|
||||
Hello World
|
||||
</div>
|
||||
|
||||
<!--Complex if-->
|
||||
<div c:if="test === 'a'">
|
||||
<div c-if="test === 'a'">
|
||||
A
|
||||
</div>
|
||||
<div c:else-if="test === 'b'">
|
||||
<div c-else-if="test === 'b'">
|
||||
B
|
||||
</div>
|
||||
<div c:else-if="test === 'c'">
|
||||
<div c-else-if="test === 'c'">
|
||||
C
|
||||
</div>
|
||||
<div c:else>
|
||||
<div c-else>
|
||||
Something else
|
||||
</div>
|
||||
```
|
||||
|
||||
_Applied as an element:_
|
||||
_Applied as elements:_
|
||||
```html
|
||||
<!-- Colors available -->
|
||||
<!--Simple if-->
|
||||
<c:if test="someCondition">
|
||||
<c-if test="someCondition">
|
||||
<div>
|
||||
Hello World
|
||||
</div>
|
||||
</c:if>
|
||||
</c-if>
|
||||
|
||||
<!--Complex if-->
|
||||
<c:if test="test === 'a'">
|
||||
<c-if test="test === 'a'">
|
||||
<div>
|
||||
A
|
||||
</div>
|
||||
</c:if>
|
||||
<c:else-if test="test === 'b'">
|
||||
</c-if>
|
||||
<c-else-if test="test === 'b'">
|
||||
<div>
|
||||
B
|
||||
</div>
|
||||
</c:else-if>
|
||||
<c:else-if test="test === 'c'">
|
||||
</c-else-if>
|
||||
<c-else-if test="test === 'c'">
|
||||
<div>
|
||||
C
|
||||
</div>
|
||||
</c:else-if>
|
||||
<c:else>
|
||||
</c-else-if>
|
||||
<c-else>
|
||||
<div>
|
||||
Something else
|
||||
</div>
|
||||
</c:else>
|
||||
</c-else>
|
||||
```
|
||||
|
||||
### choose…when…otherwise
|
||||
|
||||
The `c:choose` directive, in combination with the directives `c:when` and `c:otherwise` provides advanced conditional processing for rendering one of several alternatives. The first matching `c:when` branch is rendered, or, if no `c:when` branch matches, the `c:otherwise` branch is rendered.
|
||||
The `c-choose` directive, in combination with the directives `c-when` and `c-otherwise` provides advanced conditional processing for rendering one of several alternatives. The first matching `c-when` branch is rendered, or, if no `c-when` branch matches, the `c-otherwise` branch is rendered.
|
||||
|
||||
_Applied as an attribute:_
|
||||
```html
|
||||
<c:choose>
|
||||
<c:when test="myVar === 'A'">
|
||||
<c-choose>
|
||||
<c-when test="myVar === 'A'">
|
||||
<div>A</div>
|
||||
</c:when>
|
||||
<c:when test="myVar === 'B'">
|
||||
</c-when>
|
||||
<c-when test="myVar === 'B'">
|
||||
<div>B</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
</c-when>
|
||||
<c-otherwise>
|
||||
<div>Something else</div>
|
||||
</c:otherwise>
|
||||
<c:choose>
|
||||
</c-otherwise>
|
||||
<c-choose>
|
||||
```
|
||||
|
||||
_Applied as an element:_
|
||||
```html
|
||||
<c:choose>
|
||||
<div c:when="myVar === 'A'">
|
||||
<c-choose>
|
||||
<div c-when="myVar === 'A'">
|
||||
A
|
||||
</div>
|
||||
<div c:when="myVar === 'B'">
|
||||
<div c-when="myVar === 'B'">
|
||||
B
|
||||
</div>
|
||||
<div c:otherwise="">
|
||||
<div c-otherwise="">
|
||||
Something else
|
||||
</div>
|
||||
<c:choose>
|
||||
<c-choose>
|
||||
```
|
||||
|
||||
### Shorthand conditionals
|
||||
@ -623,21 +637,21 @@ With a value of `false` for `active`, the output would be the following:
|
||||
|
||||
### for
|
||||
|
||||
Any element can be repeated for every item in an array using the `c:for` directive. The directive can be applied as an element or as an attribute.
|
||||
Any element can be repeated for every item in an array using the `c-for` directive. The directive can be applied as an element or as an attribute.
|
||||
|
||||
_Applied as an attribute:_
|
||||
```html
|
||||
<ul>
|
||||
<li c:for="item in items">${item}</li>
|
||||
<li c-for="item in items">${item}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
_Applied as an element:_
|
||||
```html
|
||||
<ul>
|
||||
<c:for each="item in items">
|
||||
<c-for each="item in items">
|
||||
<li>${item}</li>
|
||||
</c:for>
|
||||
</c-for>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -658,14 +672,14 @@ The output would be the following:
|
||||
|
||||
#### Loop Status Variable
|
||||
|
||||
The `c:for` directive also supports a loop status variable in case you need to know the current loop index. For example:
|
||||
The `c-for` directive also supports a loop status variable in case you need to know the current loop index. For example:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li c:for="color in colors; status-var=loop">
|
||||
<li c-for="color in colors; status-var=loop">
|
||||
${loop.getIndex()+1}) $color
|
||||
<c:if test="loop.isFirst()"> - FIRST</c:if>
|
||||
<c:if test="loop.isLast()"> - LAST</c:if>
|
||||
<c-if test="loop.isFirst()"> - FIRST</c-if>
|
||||
<c-if test="loop.isLast()"> - LAST</c-if>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
@ -673,10 +687,10 @@ The `c:for` directive also supports a loop status variable in case you need to k
|
||||
#### Loop Separator
|
||||
|
||||
```html
|
||||
<c:for each="color in colors" separator=", ">$color</c:for>
|
||||
<c-for each="color in colors" separator=", ">$color</c-for>
|
||||
|
||||
<div>
|
||||
<span c:for="color in colors; separator=', '" style="color: $color">$color</span>
|
||||
<span c-for="color in colors; separator=', '" style="color: $color">$color</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
@ -684,7 +698,7 @@ The `c:for` directive also supports a loop status variable in case you need to k
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li c:for="(name,value) in settings">
|
||||
<li c-for="(name,value) in settings">
|
||||
<b>$name</b>:
|
||||
$value
|
||||
</li>
|
||||
@ -693,24 +707,24 @@ The `c:for` directive also supports a loop status variable in case you need to k
|
||||
|
||||
## Macros
|
||||
|
||||
Parameterized macros allow for reusable fragments within an HTML template. A macro can be defined using the `<c:def>` directive.
|
||||
Parameterized macros allow for reusable fragments within an HTML template. A macro can be defined using the `<c-def>` directive.
|
||||
|
||||
### def
|
||||
|
||||
The `<c:def>` directive can be used to define a reusable function within a template.
|
||||
The `<c-def>` directive can be used to define a reusable function within a template.
|
||||
|
||||
```html
|
||||
<c:def function="greeting(name, count)">
|
||||
<c-def function="greeting(name, count)">
|
||||
Hello $name! You have $count new messages.
|
||||
</c:def>
|
||||
</c-def>
|
||||
```
|
||||
|
||||
The above macro can then be invoked as part of any expression. Alternatively, the [`<c:invoke>`](#invoke) directive can be used invoke a macro function using named attributes. The following sample template shows how to use macro functions inside expressions:
|
||||
The above macro can then be invoked as part of any expression. Alternatively, the [`<c-invoke>`](#invoke) directive can be used invoke a macro function using named attributes. The following sample template shows how to use macro functions inside expressions:
|
||||
|
||||
```html
|
||||
<c:def function="greeting(name, count)">
|
||||
<c-def function="greeting(name, count)">
|
||||
Hello $name! You have $count new messages.
|
||||
</c:def>
|
||||
</c-def>
|
||||
|
||||
<p>
|
||||
${greeting("John", 10)}
|
||||
@ -722,15 +736,15 @@ The above macro can then be invoked as part of any expression. Alternatively, th
|
||||
|
||||
### invoke
|
||||
|
||||
The `<c:invoke>` directive can be used to invoke a function defined using the `<c:def>` directive or a function that is part of the input to a template. The `<c:invoke>` directive allows arguments to be passed using element attributes, but that format is only supported for functions that were previously defined using the `<c:def>` directive.
|
||||
The `<c-invoke>` directive can be used to invoke a function defined using the `<c-def>` directive or a function that is part of the input to a template. The `<c-invoke>` directive allows arguments to be passed using element attributes, but that format is only supported for functions that were previously defined using the `<c-def>` directive.
|
||||
|
||||
```html
|
||||
<c:def function="greeting(name, count)">
|
||||
<c-def function="greeting(name, count)">
|
||||
Hello ${name}! You have ${count} new messages.
|
||||
</c:def>
|
||||
</c-def>
|
||||
|
||||
<c:invoke function="greeting" name="John" count="${10}"/>
|
||||
<c:invoke function="greeting('Frank', 20)"/>
|
||||
<c-invoke function="greeting" name="John" count="${10}"/>
|
||||
<c-invoke function="greeting('Frank', 20)"/>
|
||||
```
|
||||
|
||||
The output for the above template would be the following:
|
||||
@ -744,7 +758,7 @@ The output for the above template would be the following:
|
||||
</p>
|
||||
```
|
||||
|
||||
_NOTE:_ By default, the arguments will be of type "string" when using `<c:invoke>.` However, argument attributes support JavaScript expressions which allow for other types of arguments. Example:
|
||||
_NOTE:_ By default, the arguments will be of type "string" when using `<c-invoke>.` However, argument attributes support JavaScript expressions which allow for other types of arguments. Example:
|
||||
```html
|
||||
count="10" <!-- string argument -->
|
||||
count="${10}" <!-- number argument -->
|
||||
@ -755,10 +769,10 @@ count="${10}" <!-- number argument -->
|
||||
|
||||
### attrs
|
||||
|
||||
The `c:attrs` attribute allows attributes to be dynamically added to an element at runtime. The value of the c:attrs attribute should be an expression that resolves to an object with properties that correspond to the dynamic attributes. For example:
|
||||
The `c-attrs` attribute allows attributes to be dynamically added to an element at runtime. The value of the c-attrs attribute should be an expression that resolves to an object with properties that correspond to the dynamic attributes. For example:
|
||||
|
||||
```html
|
||||
<div c:attrs="myAttrs">
|
||||
<div c-attrs="myAttrs">
|
||||
Hello World!
|
||||
</div>
|
||||
```
|
||||
@ -783,7 +797,7 @@ This directive replaces any nested content with the result of evaluating the exp
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li c:content="myExpr">Hello</li>
|
||||
<li c-content="myExpr">Hello</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -801,7 +815,7 @@ This directive replaces the element itself with the result of evaluating the exp
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span c:replace="myExpr">Hello</span>
|
||||
<span c-replace="myExpr">Hello</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
@ -819,7 +833,7 @@ This directive conditionally strips the top-level element from the output. If th
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span c:strip="true"><b>Hello</b></span>
|
||||
<span c-strip="true"><b>Hello</b></span>
|
||||
</div>
|
||||
```
|
||||
|
||||
@ -842,6 +856,19 @@ Example comments:
|
||||
<h1>Hello</h1>
|
||||
```
|
||||
|
||||
If you would like for your HTML comment to show up in the final output then you can use the custom `html-comment` tag:
|
||||
```html
|
||||
<html-comment>This is a comment that *will* be rendered</html-comment>
|
||||
<h1>Hello</h1>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<!--This is a comment that *will* be rendered-->
|
||||
<h1>Hello</h1>
|
||||
```
|
||||
|
||||
## Helpers
|
||||
|
||||
Since Raptor Template files compile into CommonJS modules, any Node.js module can be "imported" into a template for use as a helper module. For example, given the following helper module:
|
||||
@ -861,26 +888,26 @@ The above module can then be imported into a template as shown in the following
|
||||
|
||||
_src/template.rhtml_:
|
||||
```html
|
||||
<c:require module="./util" var="util" />
|
||||
<c-require module="./util" var="util" />
|
||||
|
||||
<div>${util.reverse('reverse test')}</div>
|
||||
```
|
||||
|
||||
The `c:require` directive is just short-hand for the following:
|
||||
The `c-require` directive is just short-hand for the following:
|
||||
```html
|
||||
<c:var name="util" value="require('./util')" />
|
||||
<c-var name="util" value="require('./util')" />
|
||||
<div>${util.reverse('reverse test')}</div>
|
||||
```
|
||||
|
||||
## Custom Tags and Attributes
|
||||
|
||||
Raptor Templates supports extending the language with custom tags and attributes. A custom tag or a custom attribute should be prefixed with a namespace that matches a module name that exports the custom tag or custom attribute. Alternatively, the namespace can be a shorter alias defined by the taglib.
|
||||
Raptor Templates supports extending the language with custom tags and attributes. A custom tag or a custom attribute __must have at least one dash__ to indicate that is not part of the standard HTML grammar.
|
||||
|
||||
Below illustrates how to use a simple custom tag:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<app:hello name="World"/>
|
||||
<my-hello name="World"/>
|
||||
</div>
|
||||
```
|
||||
|
||||
@ -905,19 +932,19 @@ _default-layout.rhtml:_
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><layout:placeholder name="title"/></title>
|
||||
<title><layout-placeholder name="title"/></title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 c:if="data.showHeader !== false">
|
||||
<layout:placeholder name="title"/>
|
||||
<h1 c-if="data.showHeader !== false">
|
||||
<layout-placeholder name="title"/>
|
||||
</h1>
|
||||
<p>
|
||||
<layout:placeholder name="body"/>
|
||||
<layout-placeholder name="body"/>
|
||||
</p>
|
||||
<div>
|
||||
<layout:placeholder name="footer">
|
||||
<layout-placeholder name="footer">
|
||||
Default Footer
|
||||
</layout:placeholder>
|
||||
</layout-placeholder>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -926,10 +953,10 @@ _default-layout.rhtml:_
|
||||
_Usage of `default-layout.rhtml`:_
|
||||
|
||||
```html
|
||||
<layout:use template="./default-layout.rhtml" show-header="$true">
|
||||
<layout:put into="title">My Page</layout:put>
|
||||
<layout:put into="body">BODY CONTENT</layout:put>
|
||||
</layout:use>
|
||||
<layout-use template="./default-layout.rhtml" show-header="$true">
|
||||
<layout-put into="title">My Page</layout-put>
|
||||
<layout-put into="body">BODY CONTENT</layout-put>
|
||||
</layout-use>
|
||||
```
|
||||
|
||||
# Custom Taglibs
|
||||
@ -965,9 +992,8 @@ A tag renderer should be mapped to a custom tag by creating a `raptor-taglib.jso
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "my-taglib",
|
||||
"tags": {
|
||||
"hello": {
|
||||
"my-hello": {
|
||||
"renderer": "./hello-renderer.js",
|
||||
"attributes": {
|
||||
"name": "string"
|
||||
@ -977,41 +1003,20 @@ A tag renderer should be mapped to a custom tag by creating a `raptor-taglib.jso
|
||||
}
|
||||
```
|
||||
|
||||
### Taglib Namespace
|
||||
|
||||
Every taglib should be associated with one or more namespaces as shown below:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "my-taglib",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Multiple aliases can be offered:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": ["my-taglib", "my"],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Defining Tags
|
||||
|
||||
Tags can be defined by adding a `"tags"` property to your `raptor-taglib.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "my-taglib",
|
||||
"tags": {
|
||||
"hello": {
|
||||
"my-hello": {
|
||||
"renderer": "./hello-renderer.js",
|
||||
"attributes": {
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
"foo": {
|
||||
"my-foo": {
|
||||
"renderer": "./foo-renderer.js",
|
||||
"attributes": {
|
||||
"*": "string"
|
||||
@ -1049,7 +1054,6 @@ With this approach, `raptor-taglib.json` will be much simpler:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "my-taglib",
|
||||
"tags-dir": "./components"
|
||||
}
|
||||
```
|
||||
@ -1057,20 +1061,20 @@ With this approach, `raptor-taglib.json` will be much simpler:
|
||||
Given the following directory structure:
|
||||
|
||||
* __components/__
|
||||
* __hello/__
|
||||
* __my-hello/__
|
||||
* renderer.js
|
||||
* __foo/__
|
||||
* __my-foo/__
|
||||
* renderer.js
|
||||
* __bar/__
|
||||
* __my-bar/__
|
||||
* renderer.js
|
||||
* raptor-tag.json
|
||||
* raptor-taglib.json
|
||||
|
||||
The following three tags will be exported as part of the "my-taglib" namespace:
|
||||
The following three tags will be exported:
|
||||
|
||||
* `<my-taglib:hello>`
|
||||
* `<my-taglib:foo>`
|
||||
* `<my-taglib:bar>`
|
||||
* `<my-hello>`
|
||||
* `<my-foo>`
|
||||
* `<my-bar>`
|
||||
|
||||
Directory scanning only supports one tag per directory and it will only look at directories one level deep. The tag definition can be embedded into the `renderer.js` file or it can be put into a separate `raptor-tag.json`. For example:
|
||||
|
||||
@ -1101,24 +1105,23 @@ _NOTE: It is not necessary to declare the `renderer` since the scanner will auto
|
||||
It is often necessary for tags to have a parent/child or ancestor/descendent relationship. For example:
|
||||
|
||||
```html
|
||||
<ui:tabs>
|
||||
<ui:tab label="Overview"></ui:tab>
|
||||
<ui:tab label="Language Guide"></ui:tab>
|
||||
<ui:tab label="JavaScript API"></ui:tab>
|
||||
</ui:tabs>
|
||||
<ui-tabs>
|
||||
<ui-tab label="Overview"></ui-tab>
|
||||
<ui-tab label="Language Guide"></ui-tab>
|
||||
<ui-tab label="JavaScript API"></ui-tab>
|
||||
</ui-tabs>
|
||||
```
|
||||
|
||||
Raptor Templates supports this by leveraging JavaScript closures in the compiled output. A tag can introduce scoped variables that are available to nested tags. This is shown in the sample `raptor-taglib.json` below:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "ui",
|
||||
"tags": {
|
||||
"tabs": {
|
||||
"ui-tabs": {
|
||||
"renderer": "./tabs-tag.js",
|
||||
"var": "tabs"
|
||||
},
|
||||
"tab": {
|
||||
"ui-tab": {
|
||||
"renderer": "./tab-tag.js",
|
||||
"import-var": {
|
||||
"tabs": "tabs"
|
||||
@ -1131,7 +1134,7 @@ Raptor Templates supports this by leveraging JavaScript closures in the compiled
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the `<ui:tabs>` tag will introduce a scoped variable named `tabs` that is then automatically imported by the nested `<ui:tab>` tags. When the nested `<ui:tab>` tags render they can use the scoped variable to communicate with the renderer for the `<ui:tabs>` tag.
|
||||
In the above example, the `<ui-tabs>` tag will introduce a scoped variable named `tabs` that is then automatically imported by the nested `<ui-tab>` tags. When the nested `<ui-tab>` tags render they can use the scoped variable to communicate with the renderer for the `<ui-tabs>` tag.
|
||||
|
||||
The complete code for this example is shown below:
|
||||
|
||||
@ -1143,7 +1146,7 @@ var raptorTemplates = require('raptor-templates');
|
||||
module.exports = function render(input, context) {
|
||||
var nestedTabs = [];
|
||||
|
||||
// Invoke the body function to discover nested <ui:tab> tags
|
||||
// Invoke the body function to discover nested <ui-tab> tags
|
||||
input.invokeBody({ // Invoke the body with the scoped "tabs" variable
|
||||
addTab: function(tab) {
|
||||
tab.id = tab.id || ("tab" + tabs.length);
|
||||
@ -1173,15 +1176,15 @@ _components/tabs/template.rhtml:_
|
||||
```html
|
||||
<div class="tabs">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="tab" c:for="tab in data.tabs">
|
||||
<li class="tab" c-for="tab in data.tabs">
|
||||
<a href="#${tab.id}" data-toggle="tab">
|
||||
${tab.title}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="${tab.id}" class="tab-pane" c:for="tab in data.tabs">
|
||||
<c:invoke function="tab.invokeBody()"/>
|
||||
<div id="${tab.id}" class="tab-pane" c-for="tab in data.tabs">
|
||||
<c-invoke function="tab.invokeBody()"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1203,3 +1206,84 @@ As an example, given a template at path `/my-project/src/pages/login/template.rh
|
||||
7. `/my-project/raptor-taglib.json`
|
||||
8. `/my-project/node_modules/*/raptor-taglib.json`
|
||||
|
||||
# FAQ
|
||||
|
||||
__Question:__ _Is Raptor Templates ready for production use?_
|
||||
__Answer__: Yes, Raptor Templates has been battle-tested at [eBay](http://www.ebay.com/) and other companies for well over a year and has been designed with high performance, scalability, security and stability in mind. However, the latest version of Raptor Templates is still being marked as beta as we nail down the final feature set as part of the [RaptorJS 3 initiative](https://github.com/raptorjs/raptorjs/wiki/RaptorJS-3-Plan).
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _Can templates be compiled on the client?_
|
||||
__Answer__: Possibly, but it is not recommended and it will likely not work in older browsers. The compiler is optimized to produce small, high performance compiled templates, but the compiler itself is not small and it comes bundled with some heavyweight modules such as a [JavaScript HTML parser](https://github.com/fb55/htmlparser2). In short, always compile your templates on the server. The [RaptorJS Optimizer](https://github.com/raptorjs3/raptor-optimizer) is recommended for including compiled templates as part of a web page.
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _Which web browsers are supported?_
|
||||
__Answer__: The runtime for template rendering is supported in all web browsers. If you find an issue please report a bug.
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _How can Raptor Templates be used with Express?_
|
||||
__Answer__: The recommended way to use Raptor Templates with Express is to bypass the Express view engine and instead directly pipe the rendering output stream to the response stream as shown in the following code:
|
||||
|
||||
```javascript
|
||||
var raptorTemplates = require('raptor-templates');
|
||||
var templatePath = require.resolve('./template.rhtml');
|
||||
|
||||
app.get('/profile', function(req, res) {
|
||||
raptorTemplates
|
||||
.stream(templatePath, {
|
||||
name: 'Frank'
|
||||
})
|
||||
.pipe(res);
|
||||
});
|
||||
```
|
||||
|
||||
With this approach, you can benefit from streaming and there is no middleman (less complexity).
|
||||
|
||||
Alternatively, you can use the independent [view-engine](https://github.com/patrick-steele-idem/view-engine) module to provide an abstraction that allows pluggable view engines and provides full support for streaming. This is shown in the following sample code:
|
||||
|
||||
```javascript
|
||||
var template = require('view-engine').load(require.resolve('./template.rhtml'));
|
||||
|
||||
app.get('/profile', function(req, res) {
|
||||
template.stream({
|
||||
name: 'Frank'
|
||||
})
|
||||
.pipe(res);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _I heard Raptor Templates is XML-based. What is that about?_
|
||||
__Answer__: Raptor Templates started out using an XML parser. This required that templates be well-formed XML (a major source of problems). This is no longer the case, as the compiler has been updated to use the awesome [htmlparser2](https://github.com/fb55/htmlparser2) module by [Felix Boehm](https://github.com/fb55). Also, XML namespaces are no longer used and all taglibs are now defined using simple JSON. If you are coming from the old XML-based version of Raptor Templates, please see the [Migration Guide](migration.md).
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _What is the recommended directory structure for templates and "partials"_
|
||||
__Answer__: Your templates should be organized just like all other JavaScript modules. You should put your templates right next to the code that refers to them. That is, do not create a separate "templates" directory. For a sample Express app that uses Raptor Templates, please see [raptorjs-express-app](https://github.com/raptorjs3/samples/tree/master/raptorjs-express-app).
|
||||
|
||||
<hr>
|
||||
|
||||
__Question:__ _How is Raptor Templates related to [RaptorJS](http://raptorjs.org)?_
|
||||
__Answer__: Raptor Templates is one of the modules that is part of the RaptorJS toolkit. It used to be a submodule, but now it has been split out into its own top-level Node.js module (for history, please see the [RaptorJS 3 Plan](https://github.com/raptorjs/raptorjs/wiki/RaptorJS-3-Plan) page).
|
||||
|
||||
# Discuss
|
||||
|
||||
Please post questions or comments on the [RaptorJS Google Groups Discussion Forum](http://groups.google.com/group/raptorjs).
|
||||
|
||||
# Contributors
|
||||
|
||||
* [Patrick Steele-Idem](https://github.com/patrick-steele-idem) (Twitter: [@psteeleidem](http://twitter.com/psteeleidem))
|
||||
* [Phillip Gates-Idem](https://github.com/philidem/) (Twitter: [@philidem](https://twitter.com/philidem))
|
||||
* Ramesh Mahadevan
|
||||
|
||||
# Contribute
|
||||
|
||||
Pull Requests welcome. Please submit Github issues for any feature enhancements, bugs or documentation problems.
|
||||
|
||||
# License
|
||||
|
||||
Apache License v2.0
|
||||
@ -31,6 +31,12 @@ function ElementNode(localName, namespace, prefix) {
|
||||
this.attributesByNS = {};
|
||||
this.prefix = prefix;
|
||||
this.localName = this.tagName = localName;
|
||||
if (this.prefix) {
|
||||
this.qName = this.prefix + ':' + localName;
|
||||
} else {
|
||||
this.qName = localName;
|
||||
}
|
||||
|
||||
this.namespace = namespace;
|
||||
this.allowSelfClosing = false;
|
||||
this.startTagOnly = false;
|
||||
|
||||
@ -19,6 +19,8 @@ var Expression = require('./Expression');
|
||||
var strings = require('raptor-strings');
|
||||
var stringify = require('raptor-json/stringify');
|
||||
var regexp = require('raptor-regexp');
|
||||
var ok = require('assert').ok;
|
||||
|
||||
var endingTokens = {
|
||||
'${': '}',
|
||||
'$!{': '}',
|
||||
@ -247,6 +249,9 @@ function ExpressionParser() {
|
||||
* @param thisObj
|
||||
*/
|
||||
ExpressionParser.parse = function (str, callback, thisObj, options) {
|
||||
|
||||
ok(str != null, '"str" is required');
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
'use strict';
|
||||
var createError = require('raptor-util').createError;
|
||||
var forEachEntry = require('raptor-util').forEachEntry;
|
||||
var extend = require('raptor-util').extend;
|
||||
|
||||
var isArray = Array.isArray;
|
||||
var isEmpty = require('raptor-objects').isEmpty;
|
||||
function Node(nodeType) {
|
||||
@ -69,95 +71,31 @@ Node.prototype = {
|
||||
compiler.addError(error + ' (' + this.toString() + ')', pos);
|
||||
},
|
||||
resolveNamespace: function(namespace) {
|
||||
if (namespace == null) {
|
||||
namespace = '';
|
||||
}
|
||||
|
||||
return this.compiler ?
|
||||
this.compiler.taglibs.resolveNamespace(namespace) || namespace :
|
||||
namespace;
|
||||
return namespace || '';
|
||||
},
|
||||
setProperty: function (name, value) {
|
||||
this.setPropertyNS(null, name, value);
|
||||
},
|
||||
setPropertyNS: function (namespace, name, value) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var namespacedProps = this.properties[namespace];
|
||||
if (!namespacedProps) {
|
||||
namespacedProps = this.properties[namespace] = {};
|
||||
}
|
||||
namespacedProps[name] = value;
|
||||
this.properties[name] = value;
|
||||
},
|
||||
setProperties: function (props) {
|
||||
this.setPropertiesNS(null, props);
|
||||
},
|
||||
setPropertiesNS: function (namespace, props) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
forEachEntry(props, function (name, value) {
|
||||
this.setPropertyNS(namespace, name, value);
|
||||
}, this);
|
||||
},
|
||||
getPropertyNamespaces: function () {
|
||||
return Object.keys(this.properties);
|
||||
if (!props) {
|
||||
return;
|
||||
}
|
||||
extend(this.properties, props);
|
||||
},
|
||||
getProperties: function () {
|
||||
return this.getPropertiesNS(null);
|
||||
},
|
||||
hasProperty: function (name) {
|
||||
return this.hasPropertyNS('', name);
|
||||
},
|
||||
hasPropertyNS: function (namespace, name) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var namespaceProps = this.properties[namespace];
|
||||
return namespaceProps.hasOwnProperty(name);
|
||||
},
|
||||
getPropertiesByNS: function () {
|
||||
return this.properties;
|
||||
},
|
||||
getPropertiesNS: function (namespace) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
return this.properties[namespace];
|
||||
hasProperty: function (name) {
|
||||
return this.properties.hasOwnProperty(name);
|
||||
},
|
||||
forEachProperty: function (callback, thisObj) {
|
||||
forEachEntry(this.properties, function (namespace, properties) {
|
||||
forEachEntry(properties, function (name, value) {
|
||||
callback.call(thisObj, namespace, name, value);
|
||||
}, this);
|
||||
}, this);
|
||||
forEachEntry(this.properties, callback, this);
|
||||
},
|
||||
getProperty: function (name) {
|
||||
return this.getPropertyNS(null, name);
|
||||
},
|
||||
getPropertyNS: function (namespace, name) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var namespaceProps = this.properties[namespace];
|
||||
return namespaceProps ? namespaceProps[name] : undefined;
|
||||
return this.properties[name];
|
||||
},
|
||||
removeProperty: function (name) {
|
||||
this.removePropertyNS('', name);
|
||||
},
|
||||
removePropertyNS: function (namespace, name) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var namespaceProps = this.properties[namespace];
|
||||
if (namespaceProps) {
|
||||
delete namespaceProps[name];
|
||||
}
|
||||
if (isEmpty(namespaceProps)) {
|
||||
delete this.properties[namespace];
|
||||
}
|
||||
},
|
||||
removePropertiesNS: function (namespace) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
delete this.properties[namespace];
|
||||
},
|
||||
forEachPropertyNS: function (namespace, callback, thisObj) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var props = this.properties[namespace];
|
||||
if (props) {
|
||||
forEachEntry(props, function (name, value) {
|
||||
callback.call(thisObj, name, value);
|
||||
}, this);
|
||||
}
|
||||
delete this.properties[name];
|
||||
},
|
||||
forEachChild: function (callback, thisObj) {
|
||||
if (!this.firstChild) {
|
||||
@ -443,7 +381,7 @@ Node.prototype = {
|
||||
} else {
|
||||
//There is a strip expression
|
||||
if (!this.generateBeforeCode || !this.generateAfterCode) {
|
||||
this.addError('The c:strip directive is not supported for node ' + this);
|
||||
this.addError('The c-strip directive is not supported for node ' + this);
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,8 +110,7 @@ ParseTreeBuilder.prototype = {
|
||||
function getNS(node) {
|
||||
if (node.namespace) {
|
||||
return node.namespace;
|
||||
}
|
||||
else if (node.prefix) {
|
||||
} else if (node.prefix) {
|
||||
if (node.prefix === 'xml') {
|
||||
return 'http://www.w3.org/XML/1998/namespace';
|
||||
}
|
||||
@ -121,11 +120,8 @@ ParseTreeBuilder.prototype = {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
var taglibs = this.taglibs;
|
||||
|
||||
var elNS = getNS(el);
|
||||
elNS = taglibs.resolveNamespace(elNS) || elNS;
|
||||
|
||||
var elementNode = new ElementNode(
|
||||
el.localName,
|
||||
@ -140,20 +136,15 @@ ParseTreeBuilder.prototype = {
|
||||
|
||||
elementNode.setRoot(true);
|
||||
|
||||
if (!el.namespace && el.localName === 'template') {
|
||||
elementNode.namespace = 'core';
|
||||
if (!elNS && el.localName === 'template') {
|
||||
elementNode.localName = 'c-template';
|
||||
}
|
||||
|
||||
this.rootNode = elementNode;
|
||||
}
|
||||
|
||||
attributes.forEach(function (attr) {
|
||||
if (attr.prefix === 'xmlns') {
|
||||
return; // Skip xmlns attributes
|
||||
}
|
||||
var attrNS = getNS(attr);
|
||||
attrNS = taglibs.resolveNamespace(attrNS) || attrNS;
|
||||
|
||||
var attrLocalName = attr.localName;
|
||||
var attrPrefix = attr.prefix;
|
||||
elementNode.setAttributeNS(attrNS, attrLocalName, attr.value, attrPrefix);
|
||||
|
||||
@ -52,7 +52,7 @@ ParseTreeBuilderHtml.prototype = {
|
||||
var _this = this;
|
||||
|
||||
// Create a pseudo root node
|
||||
this.handleStartElement(splitName('c:template'), []);
|
||||
this.handleStartElement(splitName('c-template'), []);
|
||||
|
||||
var parser = this.parser = new htmlparser.Parser({
|
||||
onopentag: function(name, attribs){
|
||||
|
||||
@ -15,23 +15,18 @@
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var createError = require('raptor-util').createError;
|
||||
var forEachEntry = require('raptor-util').forEachEntry;
|
||||
var ok = require('assert').ok;
|
||||
var makeClass = require('raptor-util').makeClass;
|
||||
|
||||
function Taglib(id) {
|
||||
ok(id, '"id" expected');
|
||||
this.id = id;
|
||||
this.dirname = null;
|
||||
this.namespace = null;
|
||||
this.namespaces = [];
|
||||
this.tags = {};
|
||||
this.textTransformers = [];
|
||||
this.attributeMap = {};
|
||||
this.functions = [];
|
||||
this.helperObject = null;
|
||||
this.attributes = {};
|
||||
this.patternAttributes = [];
|
||||
this.importPaths = [];
|
||||
this.inputFilesLookup = {};
|
||||
}
|
||||
|
||||
@ -46,19 +41,16 @@ Taglib.prototype = {
|
||||
},
|
||||
|
||||
addAttribute: function (attribute) {
|
||||
if (attribute.namespace) {
|
||||
throw createError(new Error('"namespace" is not allowed for taglib attributes'));
|
||||
}
|
||||
if (attribute.pattern) {
|
||||
this.patternAttributes.push(attribute);
|
||||
} else if (attribute.name) {
|
||||
this.attributeMap[attribute.name] = attribute;
|
||||
this.attributes[attribute.name] = attribute;
|
||||
} else {
|
||||
throw new Error('Invalid attribute: ' + require('util').inspect(attribute));
|
||||
}
|
||||
},
|
||||
getAttribute: function (name) {
|
||||
var attribute = this.attributeMap[name];
|
||||
var attribute = this.attributes[name];
|
||||
if (!attribute) {
|
||||
for (var i = 0, len = this.patternAttributes.length; i < len; i++) {
|
||||
var patternAttribute = this.patternAttributes[i];
|
||||
@ -72,9 +64,8 @@ Taglib.prototype = {
|
||||
addTag: function (tag) {
|
||||
ok(arguments.length === 1, 'Invalid args');
|
||||
ok(tag.name, '"tag.name" is required');
|
||||
|
||||
var key = (tag.namespace == null ? this.id : tag.namespace) + ':' + tag.name;
|
||||
this.tags[key] = tag;
|
||||
this.tags[tag.name] = tag;
|
||||
tag.taglibId = this.id;
|
||||
},
|
||||
addTextTransformer: function (transformer) {
|
||||
this.textTransformers.push(transformer);
|
||||
@ -83,247 +74,184 @@ Taglib.prototype = {
|
||||
forEachEntry(this.tags, function (key, tag) {
|
||||
callback.call(thisObj, tag);
|
||||
}, this);
|
||||
},
|
||||
addFunction: function (func) {
|
||||
this.functions.push(func);
|
||||
},
|
||||
setHelperObject: function (helperObject) {
|
||||
this.helperObject = helperObject;
|
||||
},
|
||||
getHelperObject: function () {
|
||||
return this.helperObject;
|
||||
},
|
||||
addNamespace: function (ns) {
|
||||
this.namespaces.push(ns);
|
||||
}
|
||||
};
|
||||
Taglib.Tag = (function () {
|
||||
function Tag(taglib) {
|
||||
ok(taglib, '"taglib" expected');
|
||||
this.taglib = taglib;
|
||||
|
||||
Taglib.Tag = makeClass({
|
||||
$init: function(taglib) {
|
||||
this.taglibId = taglib ? taglib.id : null;
|
||||
this.renderer = null;
|
||||
this.nodeClass = null;
|
||||
this.template = null;
|
||||
this.attributeMap = {};
|
||||
this.attributes = {};
|
||||
this.transformers = {};
|
||||
this.nestedVariables = {};
|
||||
this.importedVariables = {};
|
||||
this.patternAttributes = [];
|
||||
|
||||
}
|
||||
Tag.prototype = {
|
||||
inheritFrom: function (superTag) {
|
||||
var subTag = this;
|
||||
/*
|
||||
* Have the sub tag inherit any properties from the super tag that are not in the sub tag
|
||||
*/
|
||||
forEachEntry(superTag, function (k, v) {
|
||||
if (subTag[k] === undefined) {
|
||||
subTag[k] = v;
|
||||
},
|
||||
inheritFrom: function (superTag) {
|
||||
var subTag = this;
|
||||
/*
|
||||
* Have the sub tag inherit any properties from the super tag that are not in the sub tag
|
||||
*/
|
||||
forEachEntry(superTag, function (k, v) {
|
||||
if (subTag[k] === undefined) {
|
||||
subTag[k] = v;
|
||||
}
|
||||
});
|
||||
function inheritProps(sub, sup) {
|
||||
forEachEntry(sup, function (k, v) {
|
||||
if (!sub[k]) {
|
||||
sub[k] = v;
|
||||
}
|
||||
});
|
||||
function inheritProps(sub, sup) {
|
||||
forEachEntry(sup, function (k, v) {
|
||||
if (!sub[k]) {
|
||||
sub[k] = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
[
|
||||
'attributeMap',
|
||||
'transformers',
|
||||
'nestedVariables',
|
||||
'importedVariables'
|
||||
].forEach(function (propName) {
|
||||
inheritProps(subTag[propName], superTag[propName]);
|
||||
});
|
||||
subTag.patternAttributes = superTag.patternAttributes.concat(subTag.patternAttributes);
|
||||
},
|
||||
forEachVariable: function (callback, thisObj) {
|
||||
forEachEntry(this.nestedVariables, function (key, variable) {
|
||||
callback.call(thisObj, variable);
|
||||
});
|
||||
},
|
||||
forEachImportedVariable: function (callback, thisObj) {
|
||||
forEachEntry(this.importedVariables, function (key, importedVariable) {
|
||||
callback.call(thisObj, importedVariable);
|
||||
});
|
||||
},
|
||||
forEachTransformer: function (callback, thisObj) {
|
||||
forEachEntry(this.transformers, function (key, transformer) {
|
||||
callback.call(thisObj, transformer);
|
||||
});
|
||||
},
|
||||
hasTransformers: function () {
|
||||
/*jshint unused:false */
|
||||
for (var k in this.transformers) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
addAttribute: function (attr) {
|
||||
if (attr.pattern) {
|
||||
this.patternAttributes.push(attr);
|
||||
} else {
|
||||
var namespace = attr.namespace;
|
||||
if (namespace == null) {
|
||||
namespace = this.taglib.id;
|
||||
}
|
||||
|
||||
if (attr.name === '*') {
|
||||
attr.dynamicAttribute = true;
|
||||
|
||||
if (attr.targetProperty === null || attr.targetProperty === '') {
|
||||
attr.targetProperty = null;
|
||||
|
||||
}
|
||||
else if (!attr.targetProperty) {
|
||||
attr.targetProperty = '*';
|
||||
}
|
||||
}
|
||||
|
||||
this.attributeMap[namespace + ':' + attr.name] = attr;
|
||||
}
|
||||
},
|
||||
getAttribute: function (tagNS, localName) {
|
||||
|
||||
if (tagNS == null) {
|
||||
tagNS = this.taglib.id;
|
||||
}
|
||||
|
||||
var attr = this.attributeMap[tagNS + ':' + localName] || this.attributeMap[tagNS + ':*'] || this.attributeMap['*:' + localName] || this.attributeMap['*:*'];
|
||||
if (!attr && this.patternAttributes.length) {
|
||||
for (var i = 0, len = this.patternAttributes.length; i < len; i++) {
|
||||
var patternAttribute = this.patternAttributes[i];
|
||||
|
||||
var attrNS = patternAttribute.namespace;
|
||||
if (attrNS == null) {
|
||||
attrNS = tagNS;
|
||||
}
|
||||
|
||||
if (attrNS === tagNS && patternAttribute.pattern.test(localName)) {
|
||||
attr = patternAttribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
},
|
||||
toString: function () {
|
||||
var qName = this.namespace ? this.namespace + ':' + this.name : this.name;
|
||||
return '[Tag: <' + qName + '@' + this.taglib.id + '>]';
|
||||
},
|
||||
forEachAttribute: function (callback, thisObj) {
|
||||
for (var attrName in this.attributeMap) {
|
||||
if (this.attributeMap.hasOwnProperty(attrName)) {
|
||||
callback.call(thisObj, this.attributeMap[attrName]);
|
||||
}
|
||||
}
|
||||
},
|
||||
addNestedVariable: function (nestedVariable) {
|
||||
var key = nestedVariable.nameFromAttribute ? 'attr:' + nestedVariable.nameFromAttribute : nestedVariable.name;
|
||||
this.nestedVariables[key] = nestedVariable;
|
||||
},
|
||||
addImportedVariable: function (importedVariable) {
|
||||
var key = importedVariable.targetProperty;
|
||||
this.importedVariables[key] = importedVariable;
|
||||
},
|
||||
addTransformer: function (transformer) {
|
||||
var key = transformer.path;
|
||||
transformer.taglib = this.taglib;
|
||||
this.transformers[key] = transformer;
|
||||
}
|
||||
};
|
||||
return Tag;
|
||||
}());
|
||||
Taglib.Attribute = (function () {
|
||||
function Attribute(namespace, name) {
|
||||
this.namespace = namespace;
|
||||
[
|
||||
'attributes',
|
||||
'transformers',
|
||||
'nestedVariables',
|
||||
'importedVariables'
|
||||
].forEach(function (propName) {
|
||||
inheritProps(subTag[propName], superTag[propName]);
|
||||
});
|
||||
subTag.patternAttributes = superTag.patternAttributes.concat(subTag.patternAttributes);
|
||||
},
|
||||
forEachVariable: function (callback, thisObj) {
|
||||
forEachEntry(this.nestedVariables, function (key, variable) {
|
||||
callback.call(thisObj, variable);
|
||||
});
|
||||
},
|
||||
forEachImportedVariable: function (callback, thisObj) {
|
||||
forEachEntry(this.importedVariables, function (key, importedVariable) {
|
||||
callback.call(thisObj, importedVariable);
|
||||
});
|
||||
},
|
||||
forEachTransformer: function (callback, thisObj) {
|
||||
forEachEntry(this.transformers, function (key, transformer) {
|
||||
callback.call(thisObj, transformer);
|
||||
});
|
||||
},
|
||||
hasTransformers: function () {
|
||||
/*jshint unused:false */
|
||||
for (var k in this.transformers) {
|
||||
if (this.transformers.hasOwnProperty(k)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
},
|
||||
addAttribute: function (attr) {
|
||||
if (attr.pattern) {
|
||||
this.patternAttributes.push(attr);
|
||||
} else {
|
||||
if (attr.name === '*') {
|
||||
attr.dynamicAttribute = true;
|
||||
|
||||
if (attr.targetProperty === null || attr.targetProperty === '') {
|
||||
attr.targetProperty = null;
|
||||
|
||||
}
|
||||
else if (!attr.targetProperty) {
|
||||
attr.targetProperty = '*';
|
||||
}
|
||||
}
|
||||
|
||||
this.attributes[attr.name] = attr;
|
||||
}
|
||||
},
|
||||
toString: function () {
|
||||
return '[Tag: <' + this.name + '@' + this.taglibId + '>]';
|
||||
},
|
||||
forEachAttribute: function (callback, thisObj) {
|
||||
for (var attrName in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(attrName)) {
|
||||
callback.call(thisObj, this.attributes[attrName]);
|
||||
}
|
||||
}
|
||||
},
|
||||
addNestedVariable: function (nestedVariable) {
|
||||
var key = nestedVariable.nameFromAttribute ? 'attr:' + nestedVariable.nameFromAttribute : nestedVariable.name;
|
||||
this.nestedVariables[key] = nestedVariable;
|
||||
},
|
||||
addImportedVariable: function (importedVariable) {
|
||||
var key = importedVariable.targetProperty;
|
||||
this.importedVariables[key] = importedVariable;
|
||||
},
|
||||
addTransformer: function (transformer) {
|
||||
var key = transformer.path;
|
||||
transformer.taglibId = this.taglibId;
|
||||
this.transformers[key] = transformer;
|
||||
}
|
||||
});
|
||||
|
||||
Taglib.Attribute = makeClass({
|
||||
$init: function(name) {
|
||||
this.name = name;
|
||||
this.type = null;
|
||||
this.required = false;
|
||||
this.type = 'string';
|
||||
this.allowExpressions = true;
|
||||
}
|
||||
Attribute.prototype = {};
|
||||
return Attribute;
|
||||
}());
|
||||
Taglib.Property = (function () {
|
||||
function Property() {
|
||||
});
|
||||
|
||||
Taglib.Property = makeClass({
|
||||
$init: function() {
|
||||
this.name = null;
|
||||
this.type = 'string';
|
||||
this.value = undefined;
|
||||
}
|
||||
Property.prototype = {};
|
||||
return Property;
|
||||
}());
|
||||
Taglib.NestedVariable = (function () {
|
||||
function NestedVariable() {
|
||||
});
|
||||
|
||||
Taglib.NestedVariable = makeClass({
|
||||
$init: function() {
|
||||
this.name = null;
|
||||
}
|
||||
NestedVariable.prototype = {};
|
||||
return NestedVariable;
|
||||
}());
|
||||
Taglib.ImportedVariable = (function () {
|
||||
function ImportedVariable() {
|
||||
});
|
||||
|
||||
Taglib.ImportedVariable = makeClass({
|
||||
$init: function() {
|
||||
this.targetProperty = null;
|
||||
this.expression = null;
|
||||
}
|
||||
ImportedVariable.prototype = {};
|
||||
return ImportedVariable;
|
||||
}());
|
||||
Taglib.Transformer = (function () {
|
||||
var uniqueId = 0;
|
||||
function Transformer() {
|
||||
this.id = uniqueId++;
|
||||
});
|
||||
|
||||
var nextTransformerId = 0;
|
||||
|
||||
Taglib.Transformer = makeClass({
|
||||
$init: function() {
|
||||
this.id = nextTransformerId++;
|
||||
this.name = null;
|
||||
this.tag = null;
|
||||
this.path = null;
|
||||
this.after = null;
|
||||
this.before = null;
|
||||
this.priority = null;
|
||||
this.instance = null;
|
||||
this.properties = {};
|
||||
}
|
||||
Transformer.prototype = {
|
||||
getInstance: function () {
|
||||
if (!this.path) {
|
||||
throw createError(new Error('Transformer class not defined for tag transformer (tag=' + this.tag + ')'));
|
||||
}
|
||||
if (!this.instance) {
|
||||
var Clazz = require(this.path);
|
||||
if (Clazz.process) {
|
||||
return Clazz;
|
||||
}
|
||||
},
|
||||
|
||||
if (typeof Clazz !== 'function') {
|
||||
console.error('Invalid transformer: ', Clazz);
|
||||
throw new Error('Invalid transformer at path "' + this.path + '": ' + Clazz);
|
||||
}
|
||||
this.instance = new Clazz();
|
||||
this.instance.id = this.id;
|
||||
}
|
||||
return this.instance;
|
||||
},
|
||||
toString: function () {
|
||||
return '[Taglib.Transformer: ' + this.filename + ']';
|
||||
getInstance: function () {
|
||||
if (!this.path) {
|
||||
throw new Error('Transformer class not defined for tag transformer (tag=' + this.tag + ')');
|
||||
}
|
||||
};
|
||||
return Transformer;
|
||||
}());
|
||||
Taglib.Function = (function () {
|
||||
function Func() {
|
||||
this.name = null;
|
||||
this.path = null;
|
||||
this.bindToContext = false;
|
||||
|
||||
if (!this.instance) {
|
||||
var Clazz = require(this.path);
|
||||
if (Clazz.process) {
|
||||
return Clazz;
|
||||
}
|
||||
|
||||
if (typeof Clazz !== 'function') {
|
||||
console.error('Invalid transformer: ', Clazz);
|
||||
throw new Error('Invalid transformer at path "' + this.path + '": ' + Clazz);
|
||||
}
|
||||
this.instance = new Clazz();
|
||||
this.instance.id = this.id;
|
||||
}
|
||||
return this.instance;
|
||||
},
|
||||
toString: function () {
|
||||
return '[Taglib.Transformer: ' + this.path + ']';
|
||||
}
|
||||
Func.prototype = {};
|
||||
return Func;
|
||||
}());
|
||||
Taglib.HelperObject = (function () {
|
||||
function HelperObject() {
|
||||
this.path = null;
|
||||
}
|
||||
HelperObject.prototype = {};
|
||||
return HelperObject;
|
||||
}());
|
||||
});
|
||||
|
||||
module.exports = Taglib;
|
||||
@ -1,341 +1,214 @@
|
||||
var ok = require('assert').ok;
|
||||
var createError = require('raptor-util').createError;
|
||||
var forEachEntry = require('raptor-util').forEachEntry;
|
||||
|
||||
function transformerComparator(a, b) {
|
||||
a = a.priority;
|
||||
b = b.priority;
|
||||
|
||||
if (a == null) {
|
||||
a = Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (b == null) {
|
||||
b = Number.MAX_VALUE;
|
||||
}
|
||||
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function merge(target, source) {
|
||||
for (var k in source) {
|
||||
if (source.hasOwnProperty(k)) {
|
||||
if (target[k] && typeof target[k] === 'object' &&
|
||||
source[k] && typeof source[k] === 'object') {
|
||||
|
||||
if (Array.isArray(target[k]) || Array.isArray(source[k])) {
|
||||
|
||||
var targetArray = target[k];
|
||||
var sourceArray = source[k];
|
||||
|
||||
|
||||
if (!Array.isArray(targetArray)) {
|
||||
targetArray = [targetArray];
|
||||
}
|
||||
|
||||
if (!Array.isArray(sourceArray)) {
|
||||
sourceArray = [sourceArray];
|
||||
}
|
||||
|
||||
target[k] = [].concat(targetArray).concat(sourceArray);
|
||||
} else {
|
||||
var Ctor = target[k].constructor;
|
||||
var newTarget = new Ctor();
|
||||
merge(newTarget, target[k]);
|
||||
merge(newTarget, source[k]);
|
||||
target[k] = newTarget;
|
||||
}
|
||||
|
||||
} else {
|
||||
target[k] = source[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function TaglibLookup() {
|
||||
this.namespaces = {};
|
||||
this.tagTransformers = {};
|
||||
this.tags = {};
|
||||
this.textTransformers = [];
|
||||
this.functions = {};
|
||||
this.attributes = {};
|
||||
this.nestedTags = {};
|
||||
this.merged = {};
|
||||
this.taglibsById = {};
|
||||
this.unresolvedAttributes = [];
|
||||
this._inputFiles = null;
|
||||
}
|
||||
|
||||
TaglibLookup.prototype = {
|
||||
resolveNamespaceWithDefault: function(namespace, defaultTaglibId) {
|
||||
if (namespace === '*' || namespace === '') {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
if (namespace == null) {
|
||||
return defaultTaglibId;
|
||||
}
|
||||
|
||||
var target = this.namespaces[namespace];
|
||||
if (!target) {
|
||||
throw new Error('Invalid namespace of "' + namespace + '" in taglib "' + defaultTaglibId + '"');
|
||||
}
|
||||
return target;
|
||||
},
|
||||
|
||||
resolveNamespace: function(namespace) {
|
||||
if (namespace == null || namespace === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.taglibsById[namespace] ? namespace : this.namespaces[namespace];
|
||||
},
|
||||
|
||||
resolveNamespaceForTag: function(tag) {
|
||||
return tag.namespace ? this.resolveNamespace(tag.namespace) : tag.taglib.id;
|
||||
},
|
||||
|
||||
isTaglib: function(namespace) {
|
||||
return this.taglibsById[namespace] !=null || this.namespaces[namespace] != null;
|
||||
},
|
||||
|
||||
addTaglib: function (taglib) {
|
||||
ok(taglib, '"taglib" is required');
|
||||
ok(taglib.id, '"taglib.id" expected');
|
||||
|
||||
var id = taglib.id;
|
||||
|
||||
if (this.taglibsById[id]) {
|
||||
// The taglib has already been added
|
||||
if (this.taglibsById.hasOwnProperty(taglib.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _this = this;
|
||||
this.taglibsById[taglib.id] = taglib;
|
||||
|
||||
taglib.namespaces.forEach(function(ns) {
|
||||
_this.namespaces[ns] = id;
|
||||
});
|
||||
merge(this.merged, taglib);
|
||||
},
|
||||
|
||||
|
||||
getTag: function (element) {
|
||||
if (typeof element === 'string') {
|
||||
element = {
|
||||
localName: element
|
||||
};
|
||||
}
|
||||
var tags = this.merged.tags;
|
||||
if (!tags) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Index all of the tags in the taglib by registering them
|
||||
* based on the tag URI and the tag name
|
||||
*/
|
||||
taglib.forEachTag(function (tag, i) {
|
||||
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
|
||||
return tags[tagKey];
|
||||
},
|
||||
|
||||
// Use the fully resolved namespace for the tag
|
||||
// For example:
|
||||
// core --> /development/raptor-templates/taglibs/core/core.rtld
|
||||
|
||||
var tagNS = this.resolveNamespaceWithDefault(tag.namespace, id);
|
||||
tag.taglibId = id;
|
||||
|
||||
var name = tag.name;
|
||||
var key = name.indexOf('*') === -1 ? tagNS + ':' + name : name;
|
||||
getAttribute: function (element, attr) {
|
||||
|
||||
//The taglib will be registered using the combination of URI and tag name
|
||||
this.tags[key] = tag;
|
||||
if (typeof element === 'string') {
|
||||
element = {
|
||||
localName: element
|
||||
};
|
||||
}
|
||||
|
||||
//Register the tag using the combination of URI and tag name so that it can easily be looked up
|
||||
if (tag.hasTransformers()) {
|
||||
var tagTransformersForTags = this.tagTransformers[key] || (this.tagTransformers[key] = []);
|
||||
//A reference to the array of the tag transformers with the same key
|
||||
//Now add all of the transformers for the node (there will typically only be one...)
|
||||
tag.forEachTransformer(function (transformer) {
|
||||
if (!transformer) {
|
||||
throw createError(new Error('Transformer is null'));
|
||||
if (typeof attr === 'string') {
|
||||
attr = {
|
||||
localName: attr
|
||||
};
|
||||
}
|
||||
|
||||
var tags = this.merged.tags;
|
||||
if (!tags) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
|
||||
|
||||
var tag = tags[tagKey];
|
||||
if (!tag) {
|
||||
tag = tags['*'];
|
||||
|
||||
if (!tag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var attrKey = attr.namespace ? attr.namespace + ':' + attr.localName : attr.localName;
|
||||
|
||||
function findAttribute(attributes) {
|
||||
var attribute = attributes[attrKey];
|
||||
if (attribute === undefined) {
|
||||
if (tag.patternAttributes) {
|
||||
for (var i = 0, len = tag.patternAttributes.length; i < len; i++) {
|
||||
var patternAttribute = tag.patternAttributes[i];
|
||||
if (patternAttribute.pattern.test(attrKey)) {
|
||||
attribute = patternAttribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tagTransformersForTags.push(transformer);
|
||||
}, this);
|
||||
}
|
||||
|
||||
tag.forEachAttribute(function (attr) {
|
||||
if (attr.namespace) {
|
||||
this.unresolvedAttributes.push({
|
||||
tag: tag,
|
||||
attr: attr
|
||||
});
|
||||
} else {
|
||||
this.addAttribute(tag, attr);
|
||||
}
|
||||
|
||||
}, this);
|
||||
}, this);
|
||||
/*
|
||||
* Now register all of the text transformers that are part of the provided taglibs
|
||||
*/
|
||||
taglib.textTransformers.forEach(function (textTransformer) {
|
||||
this.textTransformers.push(textTransformer);
|
||||
}, this);
|
||||
|
||||
taglib.functions.forEach(function (func) {
|
||||
if (!func.name) {
|
||||
throw createError(new Error('Function name not set.'));
|
||||
}
|
||||
|
||||
this.functions[taglib.id + ':' + func.name] = func;
|
||||
|
||||
}, this);
|
||||
|
||||
this.taglibsById[id] = taglib;
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
for (var i=0; i<this.unresolvedAttributes.length; i++) {
|
||||
var unresolvedAttribute = this.unresolvedAttributes[i];
|
||||
this.addAttribute(unresolvedAttribute.tag, unresolvedAttribute.attr);
|
||||
}
|
||||
|
||||
this.unresolvedAttributes.length = 0;
|
||||
},
|
||||
|
||||
addAttribute: function(tag, attr) {
|
||||
var attrNS = this.resolveNamespaceWithDefault(attr.namespace, tag.taglibId);
|
||||
var tagNS = this.resolveNamespaceWithDefault(tag.namespace, tag.taglibId);
|
||||
|
||||
if (attrNS !== '*' && attrNS === tagNS) {
|
||||
attrNS = '';
|
||||
}
|
||||
|
||||
if (attr.name) {
|
||||
this.attributes[tagNS + ':' + tag.name + ':' + attrNS + ':' + attr.name] = attr;
|
||||
}
|
||||
},
|
||||
|
||||
getAttribute: function (tagNS, tagName, attrNS, attrName) {
|
||||
var tags = this.tags;
|
||||
|
||||
tagNS = this.resolveNamespace(tagNS);
|
||||
attrNS = this.resolveNamespace(attrNS);
|
||||
|
||||
var attributes = this.attributes;
|
||||
|
||||
function _findAttrForTag(tagLookupKey) {
|
||||
|
||||
|
||||
var attr = attributes[tagLookupKey + ':' + attrNS + ':' + attrName] ||
|
||||
attributes[tagLookupKey + ':' + attrNS + ':*'] ||
|
||||
attributes[tagLookupKey + ':*:' + attrName] ||
|
||||
attributes[tagLookupKey + ':*:*'];
|
||||
|
||||
if (!attr) {
|
||||
var tag = tags[tagLookupKey];
|
||||
if (tag) {
|
||||
attr = tag.getAttribute(attrNS, attrName);
|
||||
if (attribute === undefined) {
|
||||
attribute = tag.attributes['*'];
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
var attr = _findAttrForTag(tagNS + ':' + tagName) ||
|
||||
_findAttrForTag(tagNS + ':*') ||
|
||||
_findAttrForTag('*:*');
|
||||
|
||||
if (attr && attr.namespace && attr.namespace !== '*') {
|
||||
var sourceTaglibId = this.resolveNamespace(attr.namespace);
|
||||
|
||||
var taglib = this.taglibsById[sourceTaglibId];
|
||||
if (!taglib) {
|
||||
throw new Error('Taglib with namespace "' + attr.namespace + '" not found for imported attribute with name "' + attrName + '"');
|
||||
}
|
||||
var importedAttr = taglib.getAttribute(attrName);
|
||||
if (!importedAttr) {
|
||||
throw new Error('Attribute "' + attrName + '" imported from taglib with namespace "' + attr.namespace + '" not found in taglib "' + taglib.id + '".');
|
||||
}
|
||||
var attribute = findAttribute(tag.attributes);
|
||||
|
||||
attr = importedAttr;
|
||||
if (attribute === null) {
|
||||
// This is an imported attribute
|
||||
attribute = findAttribute(this.merged.attributes);
|
||||
}
|
||||
|
||||
return attr;
|
||||
},
|
||||
forEachTag: function (namespace, callback, thisObj) {
|
||||
var taglibId = this.resolveNamespace(namespace);
|
||||
var taglib = this.taglibsById[taglibId];
|
||||
if (!taglib) {
|
||||
return;
|
||||
}
|
||||
forEachEntry(taglib.tags, function (key, tag) {
|
||||
callback.call(thisObj, tag, taglib);
|
||||
});
|
||||
return attribute;
|
||||
},
|
||||
forEachNodeTransformer: function (node, callback, thisObj) {
|
||||
/*
|
||||
* Based on the type of node we have to choose how to transform it
|
||||
*/
|
||||
if (node.isElementNode()) {
|
||||
this.forEachTagTransformer(node.namespace, node.localName, callback, thisObj);
|
||||
this.forEachTagTransformer(node, callback, thisObj);
|
||||
} else if (node.isTextNode()) {
|
||||
this.forEachTextTransformer(callback, thisObj);
|
||||
}
|
||||
},
|
||||
forEachTagTransformer: function (tagNS, tagName, callback, thisObj) {
|
||||
forEachTagTransformer: function (element, callback, thisObj) {
|
||||
if (typeof element === 'string') {
|
||||
element = {
|
||||
localName: element
|
||||
};
|
||||
}
|
||||
|
||||
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
|
||||
/*
|
||||
* If the node is an element node then we need to find all matching
|
||||
* transformers based on the URI and the local name of the element.
|
||||
*/
|
||||
tagNS = this.resolveNamespace(tagNS);
|
||||
|
||||
var _this = this;
|
||||
|
||||
function resolveBeforeAfterName(name) {
|
||||
var parts = name.split(/[:\/]/);
|
||||
parts[0] = _this.resolveNamespace(parts[0]);
|
||||
return parts.join(':');
|
||||
}
|
||||
|
||||
var matchingTransformersByName = {};
|
||||
var matchingTransformers = [];
|
||||
var handled = {};
|
||||
var before = {};
|
||||
function _addTransformers(transformers) {
|
||||
if (!transformers) {
|
||||
return;
|
||||
var transformers = [];
|
||||
|
||||
function addTransformer(transformer) {
|
||||
if (!transformer || !transformer.getInstance) {
|
||||
throw createError(new Error('Invalid transformer'));
|
||||
}
|
||||
|
||||
transformers.forEach(function (transformer) {
|
||||
if (!transformer) {
|
||||
throw createError(new Error('Invalid transformer'));
|
||||
}
|
||||
|
||||
if (transformer.name) {
|
||||
var fullName = transformer.taglib.id + ':' + transformer.name;
|
||||
matchingTransformersByName[fullName] = transformer;
|
||||
}
|
||||
|
||||
matchingTransformers.push(transformer);
|
||||
|
||||
|
||||
if (transformer.before) {
|
||||
var beforeName = resolveBeforeAfterName(transformer.before);
|
||||
(before[beforeName] || (before[beforeName] = [])).push(transformer);
|
||||
}
|
||||
});
|
||||
transformers.push(transformer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle all of the transformers for all possible matching transformers.
|
||||
*
|
||||
* Start with the least specific and end with the most specific.
|
||||
*/
|
||||
_addTransformers(this.tagTransformers['*']);
|
||||
_addTransformers(this.tagTransformers['*:*']);
|
||||
//Wildcard for both URI and tag name (i.e. transformers that apply to every element)
|
||||
_addTransformers(this.tagTransformers[tagNS + ':*']);
|
||||
//Wildcard for tag name but matching URI (i.e. transformers that apply to every element with a URI, regadless of tag name)
|
||||
_addTransformers(this.tagTransformers[tagNS + ':' + tagName]);
|
||||
|
||||
|
||||
function _handleTransformer(transformer) {
|
||||
if (!handled[transformer.id]) {
|
||||
handled[transformer.id] = true;
|
||||
|
||||
if (transformer.after) {
|
||||
var afterName = resolveBeforeAfterName(transformer.after);
|
||||
//Check if this transformer is required to run
|
||||
if (!matchingTransformersByName[afterName]) {
|
||||
throw createError(new Error('After transformers not found for "' + transformer.after + '"'));
|
||||
}
|
||||
_handleTransformer(matchingTransformersByName[afterName]); //Handle any transformers that this transformer is supposed to run after
|
||||
}
|
||||
|
||||
if (transformer.name) {
|
||||
var transformerId = transformer.taglib.id + ':' + transformer.name;
|
||||
if (before[transformerId]) {
|
||||
before[transformerId].forEach(_handleTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
//Handle any transformers that are configured to run before this transformer
|
||||
callback.call(thisObj, transformer);
|
||||
}
|
||||
if (this.merged.tags[tagKey]) {
|
||||
this.merged.tags[tagKey].forEachTransformer(addTransformer);
|
||||
}
|
||||
matchingTransformers.forEach(_handleTransformer, this);
|
||||
|
||||
if (this.merged.tags['*']) {
|
||||
this.merged.tags['*'].forEachTransformer(addTransformer);
|
||||
}
|
||||
|
||||
transformers.sort(transformerComparator);
|
||||
|
||||
transformers.forEach(callback, thisObj);
|
||||
},
|
||||
forEachTextTransformer: function (callback, thisObj) {
|
||||
this.textTransformers.forEach(function(textTransformer) {
|
||||
var keepGoing = callback.call(thisObj, textTransformer);
|
||||
if (keepGoing === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
this.merged.textTransformers.sort(transformerComparator);
|
||||
this.merged.textTransformers.forEach(callback, thisObj);
|
||||
},
|
||||
getTag: function (namespace, localName) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var tag = this.tags[namespace + ':' + localName];
|
||||
if (!tag) {
|
||||
tag = this.tags[namespace + ':*']; //See if there was a wildcard tag definition in the taglib
|
||||
}
|
||||
return tag;
|
||||
},
|
||||
getNestedTag: function (parentTagNS, parentTagName, nestedTagNS, nestedTagName) {
|
||||
parentTagNS = this.resolveNamespace(parentTagNS);
|
||||
nestedTagNS = this.resolveNamespace(nestedTagNS);
|
||||
return this.nestedTags[parentTagNS + ':' + parentTagName + ':' + nestedTagNS + ':' + nestedTagName];
|
||||
},
|
||||
getFunction: function (namespace, functionName) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
return this.functions[namespace + ':' + functionName];
|
||||
},
|
||||
getHelperObject: function (namespace) {
|
||||
namespace = this.resolveNamespace(namespace);
|
||||
var taglib = this.taglibsById[namespace];
|
||||
if (!taglib) {
|
||||
throw new Error('Invalid taglib URI: ' + namespace);
|
||||
}
|
||||
return taglib.getHelperObject();
|
||||
},
|
||||
|
||||
getInputFiles: function() {
|
||||
if (!this._inputFiles) {
|
||||
var inputFilesSet = {};
|
||||
|
||||
@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 eBay Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
var createError = require('raptor-util').createError;
|
||||
var objectMapper = require('raptor-xml/object-mapper');
|
||||
var regexp = require('raptor-regexp');
|
||||
var Taglib = require('./Taglib');
|
||||
var Tag = Taglib.Tag;
|
||||
var Attribute = Taglib.Attribute;
|
||||
var NestedVariable = Taglib.NestedVariable;
|
||||
var ImportedVariable = Taglib.ImportedVariable;
|
||||
var Transformer = Taglib.Transformer;
|
||||
var Func = Taglib.Function;
|
||||
var HelperObject = Taglib.HelperObject;
|
||||
var nodePath = require('path');
|
||||
var fs = require('fs');
|
||||
var STRING = 'string';
|
||||
var BOOLEAN = 'boolean';
|
||||
var OBJECT = 'object';
|
||||
|
||||
function TaglibXmlLoader(src, filePath) {
|
||||
this.src = src;
|
||||
this.filePath = filePath;
|
||||
}
|
||||
TaglibXmlLoader.load = function (src, filePath) {
|
||||
var loader = new TaglibXmlLoader(src, filePath);
|
||||
return loader.load();
|
||||
};
|
||||
TaglibXmlLoader.prototype = {
|
||||
load: function () {
|
||||
var src = this.src;
|
||||
var filePath = this.filePath;
|
||||
var dirname = nodePath.dirname(filePath);
|
||||
var tagsById = {};
|
||||
|
||||
function resolvePath(path) {
|
||||
var resolvedPath = nodePath.resolve(dirname, path);
|
||||
if (!resolvedPath.endsWith('.js')) {
|
||||
resolvedPath += '.js';
|
||||
}
|
||||
|
||||
if (!fs.existsSync(resolvedPath)) {
|
||||
throw new Error('File does not exist: ' + resolvedPath);
|
||||
}
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
function handleTagExtends(subTag) {
|
||||
var extendsId = subTag['extends'];
|
||||
if (!extendsId) {
|
||||
return;
|
||||
}
|
||||
delete subTag['extends'];
|
||||
var superTag = tagsById[extendsId];
|
||||
if (!superTag) {
|
||||
throw createError(new Error('Parent tag with ID "' + extendsId + '" not found in taglib at path "' + filePath + '"'));
|
||||
}
|
||||
if (superTag['extends']) {
|
||||
handleTagExtends(superTag);
|
||||
}
|
||||
subTag.inheritFrom(superTag);
|
||||
}
|
||||
var taglib;
|
||||
var attributeHandler = {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new Attribute();
|
||||
},
|
||||
_end: function (attr, parent) {
|
||||
parent.addAttribute(attr);
|
||||
},
|
||||
'name': { _type: STRING },
|
||||
'pattern': {
|
||||
_type: STRING,
|
||||
_set: function (parent, name, value) {
|
||||
var patternRegExp = regexp.simple(value);
|
||||
parent.pattern = patternRegExp;
|
||||
}
|
||||
},
|
||||
'target-property': {
|
||||
_type: STRING,
|
||||
_targetProp: 'targetProperty'
|
||||
},
|
||||
'namespace': { _type: STRING },
|
||||
'deprecated': { _type: STRING },
|
||||
'required': { _type: BOOLEAN },
|
||||
'type': { _type: STRING },
|
||||
'allow-expressions': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'allowExpressions'
|
||||
},
|
||||
'preserve-name': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'preserveName'
|
||||
},
|
||||
'description': { _type: STRING },
|
||||
'remove-dashes': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'removeDashes'
|
||||
}
|
||||
};
|
||||
var importVariableHandler = {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new ImportedVariable();
|
||||
},
|
||||
_end: function (importedVariable, tag) {
|
||||
if (importedVariable.name) {
|
||||
if (!importedVariable.targetProperty) {
|
||||
importedVariable.targetProperty = importedVariable.name;
|
||||
}
|
||||
importedVariable.expression = importedVariable.name;
|
||||
delete importedVariable.name;
|
||||
}
|
||||
if (!importedVariable.targetProperty) {
|
||||
throw createError(new Error('The "target-property" attribute is required for an imported variable'));
|
||||
}
|
||||
if (!importedVariable.expression) {
|
||||
throw createError(new Error('The "expression" attribute is required for an imported variable'));
|
||||
}
|
||||
tag.addImportedVariable(importedVariable);
|
||||
},
|
||||
'name': { _type: STRING },
|
||||
'target-property': {
|
||||
_type: STRING,
|
||||
_targetProp: 'targetProperty'
|
||||
},
|
||||
'expression': { _type: STRING }
|
||||
};
|
||||
var variableHandler = {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new NestedVariable();
|
||||
},
|
||||
_end: function (nestedVariable, tag) {
|
||||
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
|
||||
throw createError(new Error('The "name" or "name-from-attribute" attribute is required for a nested variable'));
|
||||
}
|
||||
tag.addNestedVariable(nestedVariable);
|
||||
},
|
||||
'name': {
|
||||
_type: STRING,
|
||||
_targetProp: 'name'
|
||||
},
|
||||
'name-from-attribute': {
|
||||
_type: STRING,
|
||||
_targetProp: 'nameFromAttribute'
|
||||
},
|
||||
'name-from-attr': {
|
||||
_type: STRING,
|
||||
_targetProp: 'nameFromAttribute'
|
||||
}
|
||||
};
|
||||
var handlers = {
|
||||
'raptor-taglib': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
var newTaglib = new Taglib(filePath);
|
||||
if (!taglib) {
|
||||
taglib = newTaglib;
|
||||
}
|
||||
taglib.addInputFile(filePath);
|
||||
return newTaglib;
|
||||
},
|
||||
'attribute': attributeHandler,
|
||||
'tlib-version': {
|
||||
_type: STRING,
|
||||
_targetProp: 'version'
|
||||
},
|
||||
'uri': {
|
||||
_type: STRING,
|
||||
_set: function (taglib, name, value, context) {
|
||||
taglib.addNamespace(value);
|
||||
}
|
||||
},
|
||||
'namespace': {
|
||||
_type: STRING,
|
||||
_set: function (taglib, name, value, context) {
|
||||
taglib.addNamespace(value);
|
||||
}
|
||||
},
|
||||
'short-name': {
|
||||
_type: STRING,
|
||||
_set: function (taglib, name, value, context) {
|
||||
taglib.addNamespace(value);
|
||||
}
|
||||
},
|
||||
'prefix': {
|
||||
_type: STRING,
|
||||
_set: function (taglib, name, value, context) {
|
||||
taglib.addNamespace(value);
|
||||
}
|
||||
},
|
||||
'tag': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new Tag(taglib);
|
||||
},
|
||||
_end: function (tag) {
|
||||
if (tag.namespace === undefined) {
|
||||
tag.namespace = taglib.namespace;
|
||||
}
|
||||
tag.filename = filePath;
|
||||
tag.dirname = dirname;
|
||||
taglib.addTag(tag);
|
||||
if (tag.id) {
|
||||
tagsById[tag.id] = tag;
|
||||
}
|
||||
},
|
||||
'name': {
|
||||
_type: STRING,
|
||||
_targetProp: 'name'
|
||||
},
|
||||
'namespace': {
|
||||
_type: STRING,
|
||||
_set: function (tag, name, value, context) {
|
||||
tag.namespace = value || '';
|
||||
}
|
||||
},
|
||||
'id': { _type: STRING },
|
||||
'preserveSpace': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'preserveWhitespace'
|
||||
},
|
||||
'preserve-space': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'preserveWhitespace'
|
||||
},
|
||||
'preserve-whitespace': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'preserveWhitespace'
|
||||
},
|
||||
'preserveWhitespace': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'preserveWhitespace'
|
||||
},
|
||||
'extends': {
|
||||
_type: STRING,
|
||||
_targetProp: 'extends'
|
||||
},
|
||||
'handler-class': {
|
||||
_type: STRING,
|
||||
_set: function (tag, name, value, context) {
|
||||
tag.renderer = resolvePath(value);
|
||||
}
|
||||
},
|
||||
'renderer': {
|
||||
_type: STRING,
|
||||
_targetProp: 'renderer',
|
||||
_set: function (tag, name, value, context) {
|
||||
tag.renderer = resolvePath(value);
|
||||
}
|
||||
},
|
||||
'template': {
|
||||
_type: STRING,
|
||||
_targetProp: 'template'
|
||||
},
|
||||
'node-class': {
|
||||
_type: STRING,
|
||||
_set: function (tag, name, path) {
|
||||
tag.nodeClass = resolvePath(path);
|
||||
}
|
||||
},
|
||||
'<attribute>': attributeHandler,
|
||||
'nested-variable': variableHandler,
|
||||
'variable': variableHandler,
|
||||
'imported-variable': importVariableHandler,
|
||||
'import-variable': importVariableHandler,
|
||||
'transformer-path': {
|
||||
_type: STRING,
|
||||
_set: function (tag, name, path) {
|
||||
var transformer = new Transformer();
|
||||
transformer.dirname = dirname;
|
||||
transformer.path = resolvePath(path);
|
||||
tag.addTransformer(transformer);
|
||||
}
|
||||
},
|
||||
'transformer': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new Transformer();
|
||||
},
|
||||
_end: function (transformer, tag) {
|
||||
transformer.dirname = dirname;
|
||||
tag.addTransformer(transformer);
|
||||
},
|
||||
'path': {
|
||||
_type: STRING,
|
||||
_set: function (transformer, name, path) {
|
||||
transformer.path = resolvePath(path);
|
||||
}
|
||||
},
|
||||
'after': {
|
||||
_type: STRING,
|
||||
_targetProp: 'after'
|
||||
},
|
||||
'before': {
|
||||
_type: STRING,
|
||||
_targetProp: 'before'
|
||||
},
|
||||
'name': {
|
||||
_type: STRING,
|
||||
_targetProp: 'name'
|
||||
},
|
||||
'<properties>': {
|
||||
_type: OBJECT,
|
||||
_begin: function (parent) {
|
||||
return (parent.properties = {});
|
||||
},
|
||||
'<*>': { _type: STRING }
|
||||
}
|
||||
}
|
||||
},
|
||||
'text-transformer': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new Transformer();
|
||||
},
|
||||
_end: function (textTransformer) {
|
||||
taglib.addTextTransformer(textTransformer);
|
||||
},
|
||||
'path': {
|
||||
_type: STRING,
|
||||
_set: function (transformer, name, path) {
|
||||
transformer.path = resolvePath(path);
|
||||
}
|
||||
}
|
||||
},
|
||||
'import-taglib': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return {};
|
||||
},
|
||||
_end: function (importedTaglib) {
|
||||
var path = resolvePath(importedTaglib.path);
|
||||
taglib.importPaths.push(path);
|
||||
|
||||
if (!fs.existsSync(path)) {
|
||||
throw createError(new Error('Imported taglib with path "' + path + '" not found in taglib at path "' + filePath + '"'));
|
||||
}
|
||||
|
||||
var importedXmlSource = fs.readFileSync(path);
|
||||
require('../work-dir').recordLoadedTaglib(path);
|
||||
var oldDirname = dirname;
|
||||
dirname = nodePath.dirname(path);
|
||||
objectMapper.read(importedXmlSource, path, handlers);
|
||||
dirname = oldDirname;
|
||||
},
|
||||
'path': { _type: STRING }
|
||||
},
|
||||
'function': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new Func();
|
||||
},
|
||||
_end: function (func) {
|
||||
taglib.addFunction(func);
|
||||
},
|
||||
'name': { _type: STRING },
|
||||
'path': {
|
||||
_type: STRING,
|
||||
_set: function (func, name, path) {
|
||||
func.path = resolvePath(path);
|
||||
}
|
||||
},
|
||||
'bind-to-context': {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: 'bindToContext'
|
||||
}
|
||||
},
|
||||
'helper-object': {
|
||||
_type: OBJECT,
|
||||
_begin: function () {
|
||||
return new HelperObject();
|
||||
},
|
||||
_end: function (helperObject) {
|
||||
taglib.setHelperObject(helperObject);
|
||||
},
|
||||
'path': {
|
||||
_type: STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
objectMapper.read(src, filePath, handlers);
|
||||
taglib.forEachTag(function (tag) {
|
||||
handleTagExtends(tag);
|
||||
});
|
||||
taglib.dirname = dirname;
|
||||
return taglib;
|
||||
}
|
||||
};
|
||||
module.exports = TaglibXmlLoader;
|
||||
@ -21,6 +21,7 @@ var Expression = require('./Expression');
|
||||
var TypeConverter = require('./TypeConverter');
|
||||
var taglibLookup = require('./taglib-lookup');
|
||||
var nodePath = require('path');
|
||||
var ok = require('assert').ok;
|
||||
|
||||
function TemplateCompiler(path, options) {
|
||||
this.dirname = nodePath.dirname(path);
|
||||
@ -159,8 +160,10 @@ TemplateCompiler.prototype = {
|
||||
getErrors: function () {
|
||||
return this.errors;
|
||||
},
|
||||
getNodeClass: function (ns, localName) {
|
||||
var tag = this.taglibs.getTag(ns, localName);
|
||||
getNodeClass: function (tagName) {
|
||||
ok(arguments.length === 1, 'Invalid args');
|
||||
|
||||
var tag = this.taglibs.getTag(tagName);
|
||||
if (tag && tag.nodeClass) {
|
||||
var nodeClass = require(tag.nodeClass);
|
||||
if (nodeClass.prototype.constructor !== nodeClass) {
|
||||
@ -168,13 +171,17 @@ TemplateCompiler.prototype = {
|
||||
}
|
||||
return nodeClass;
|
||||
}
|
||||
throw createError(new Error('Node class not found for namespace "' + ns + '" and localName "' + localName + '"'));
|
||||
throw createError(new Error('Node class not found for tag "' + tagName + '"'));
|
||||
},
|
||||
createTag: function () {
|
||||
var Taglib = require('./Taglib');
|
||||
return new Taglib.Tag();
|
||||
},
|
||||
checkUpToDate: function(sourceFile, targetFile) {
|
||||
if (this.options.checkUpToDate === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
|
||||
|
||||
@ -31,7 +31,8 @@ var defaultOptions = {
|
||||
'meta': true,
|
||||
'link': true,
|
||||
'hr': true
|
||||
}
|
||||
},
|
||||
checkUpToDate: true
|
||||
};
|
||||
|
||||
extend(exports, {
|
||||
@ -86,3 +87,5 @@ extend(exports, {
|
||||
EscapeXmlContext: require('./EscapeXmlContext'),
|
||||
defaultOptions: defaultOptions
|
||||
});
|
||||
|
||||
exports.TemplateCompiler = require('./TemplateCompiler');
|
||||
|
||||
@ -6,67 +6,13 @@ var cache = {};
|
||||
var forEachEntry = require('raptor-util').forEachEntry;
|
||||
var raptorRegexp = require('raptor-regexp');
|
||||
var tagDefFromCode = require('./tag-def-from-code');
|
||||
|
||||
function removeDashes(str) {
|
||||
return str.replace(/-([a-z])/g, function (match, lower) {
|
||||
return lower.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function invokeHandlers(config, handlers, path) {
|
||||
if (!config) {
|
||||
throw new Error('"config" argument is required');
|
||||
}
|
||||
|
||||
if (typeof config !== 'object') {
|
||||
throw new Error('Object expected for ' + path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (var k in config) {
|
||||
if (config.hasOwnProperty(k)) {
|
||||
var value = config[k];
|
||||
k = removeDashes(k);
|
||||
var handler = handlers[k];
|
||||
if (!handler) {
|
||||
throw new Error('Invalid option of "' + k + '" for ' + path + '. Allowed: ' + Object.keys(handlers).join(', '));
|
||||
}
|
||||
try {
|
||||
handler(value);
|
||||
}
|
||||
catch(e) {
|
||||
if (!e.invokeHandlerError) {
|
||||
var error = new Error('Error while applying option of "' + k + '" for ' + path + '. Exception: ' + (e.stack || e));
|
||||
error.invokeHandlerError = e;
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handlers._end) {
|
||||
try {
|
||||
handlers._end();
|
||||
}
|
||||
catch(e) {
|
||||
if (!e.invokeHandlerError) {
|
||||
var error = new Error('Error for option ' + path + '. Exception: ' + (e.stack || e));
|
||||
error.invokeHandlerError = e;
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var resolve = require('raptor-modules/resolver').serverResolveRequire;
|
||||
var propertyHandlers = require('property-handlers');
|
||||
|
||||
function buildAttribute(attr, attrProps, path) {
|
||||
invokeHandlers(attrProps, {
|
||||
|
||||
|
||||
propertyHandlers(attrProps, {
|
||||
type: function(value) {
|
||||
attr.type = value;
|
||||
},
|
||||
@ -76,9 +22,6 @@ function buildAttribute(attr, attrProps, path) {
|
||||
defaultValue: function(value) {
|
||||
attr.defaultValue = value;
|
||||
},
|
||||
namespace: function(value) {
|
||||
attr.namespace = value;
|
||||
},
|
||||
pattern: function(value) {
|
||||
if (value === true) {
|
||||
var patternRegExp = raptorRegexp.simple(attr.name);
|
||||
@ -90,6 +33,12 @@ function buildAttribute(attr, attrProps, path) {
|
||||
},
|
||||
preserveName: function(value) {
|
||||
attr.preserveName = value;
|
||||
},
|
||||
required: function(value) {
|
||||
attr.required = value === true;
|
||||
},
|
||||
removeDashes: function(value) {
|
||||
attr.removeDashes = value === true;
|
||||
}
|
||||
}, path);
|
||||
|
||||
@ -98,21 +47,7 @@ function buildAttribute(attr, attrProps, path) {
|
||||
|
||||
function handleAttributes(value, parent, path) {
|
||||
forEachEntry(value, function(attrName, attrProps) {
|
||||
var parts = attrName.split(':');
|
||||
var namespace = null;
|
||||
var localName = null;
|
||||
|
||||
if (parts.length === 2) {
|
||||
namespace = parts[0];
|
||||
localName = parts[1];
|
||||
} else if (parts.length === 1) {
|
||||
localName = attrName;
|
||||
} else {
|
||||
throw new Error('Invalid attribute name: ' + attrName);
|
||||
}
|
||||
|
||||
|
||||
var attr = new Taglib.Attribute(namespace, localName);
|
||||
var attr = new Taglib.Attribute(attrName);
|
||||
|
||||
if (attrProps == null) {
|
||||
attrProps = {
|
||||
@ -139,18 +74,13 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
|
||||
var tag = new Taglib.Tag(taglib);
|
||||
|
||||
invokeHandlers(tagObject, {
|
||||
propertyHandlers(tagObject, {
|
||||
name: function(value) {
|
||||
tag.name = value;
|
||||
},
|
||||
|
||||
renderer: function(value) {
|
||||
var ext = nodePath.extname(value);
|
||||
if (ext === '') {
|
||||
value += '.js';
|
||||
}
|
||||
|
||||
var path = nodePath.resolve(dirname, value);
|
||||
var path = resolve(value, dirname);
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('Renderer at path "' + path + '" does not exist.');
|
||||
}
|
||||
@ -169,7 +99,7 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
handleAttributes(value, tag, path);
|
||||
},
|
||||
nodeClass: function(value) {
|
||||
var path = nodePath.resolve(dirname, value);
|
||||
var path = resolve(value, dirname);
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('Node module at path "' + path + '" does not exist.');
|
||||
}
|
||||
@ -185,14 +115,9 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
};
|
||||
}
|
||||
|
||||
invokeHandlers(value, {
|
||||
propertyHandlers(value, {
|
||||
path: function(value) {
|
||||
var ext = nodePath.extname(value);
|
||||
if (ext === '') {
|
||||
value += '.js';
|
||||
}
|
||||
|
||||
var path = nodePath.resolve(dirname, value);
|
||||
var path = resolve(value, dirname);
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('Transformer at path "' + path + '" does not exist.');
|
||||
}
|
||||
@ -200,16 +125,21 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
transformer.path = path;
|
||||
},
|
||||
|
||||
before: function(value) {
|
||||
transformer.before = value;
|
||||
},
|
||||
|
||||
after: function(value) {
|
||||
transformer.after = value;
|
||||
priority: function(value) {
|
||||
transformer.priority = value;
|
||||
},
|
||||
|
||||
name: function(value) {
|
||||
transformer.name = value;
|
||||
},
|
||||
|
||||
properties: function(value) {
|
||||
var properties = transformer.properties || (transformer.properties = {});
|
||||
for (var k in value) {
|
||||
if (value.hasOwnProperty(k)) {
|
||||
properties[k] = value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 'transformer in ' + path);
|
||||
@ -218,6 +148,7 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
|
||||
tag.addTransformer(transformer);
|
||||
},
|
||||
|
||||
'var': function(value) {
|
||||
tag.addNestedVariable({
|
||||
name: value
|
||||
@ -225,10 +156,34 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
},
|
||||
vars: function(value) {
|
||||
if (value) {
|
||||
value.forEach(function(varName) {
|
||||
tag.addNestedVariable({
|
||||
name: varName
|
||||
});
|
||||
value.forEach(function(v, i) {
|
||||
var nestedVariable;
|
||||
|
||||
if (typeof v === 'string') {
|
||||
nestedVariable = {
|
||||
name: v
|
||||
};
|
||||
} else {
|
||||
nestedVariable = {};
|
||||
|
||||
propertyHandlers(v, {
|
||||
|
||||
name: function(value) {
|
||||
nestedVariable.name = value;
|
||||
},
|
||||
|
||||
nameFromAttribute: function(value) {
|
||||
nestedVariable.nameFromAttribute = value;
|
||||
}
|
||||
|
||||
}, 'var at index ' + i);
|
||||
|
||||
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
|
||||
throw new Error('The "name" or "name-from-attribute" attribute is required for a nested variable');
|
||||
}
|
||||
}
|
||||
|
||||
tag.addNestedVariable(nestedVariable);
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -326,17 +281,6 @@ function load(path) {
|
||||
taglib.addInputFile(path);
|
||||
var dirname = nodePath.dirname(path);
|
||||
|
||||
function handleNS(ns) {
|
||||
if (Array.isArray(ns)) {
|
||||
ns.forEach(function(ns) {
|
||||
taglib.addNamespace(ns);
|
||||
});
|
||||
}
|
||||
else {
|
||||
taglib.addNamespace(ns);
|
||||
}
|
||||
}
|
||||
|
||||
var taglibObject;
|
||||
|
||||
try {
|
||||
@ -346,13 +290,11 @@ function load(path) {
|
||||
throw new Error('Unable to parse taglib JSON at path "' + path + '". Exception: ' + e);
|
||||
}
|
||||
|
||||
invokeHandlers(taglibObject, {
|
||||
'namespace': handleNS,
|
||||
'namespaces': handleNS,
|
||||
'attributes': function(value) {
|
||||
propertyHandlers(taglibObject, {
|
||||
attributes: function(value) {
|
||||
handleAttributes(value, taglib, path);
|
||||
},
|
||||
'tags': function(tags) {
|
||||
tags: function(tags) {
|
||||
forEachEntry(tags, function(tagName, path) {
|
||||
ok(path, 'Invalid tag definition for "' + tagName + '"');
|
||||
var tagObject;
|
||||
@ -399,6 +341,31 @@ function load(path) {
|
||||
} else {
|
||||
scanTagsDir(path, dirname, dir, taglib);
|
||||
}
|
||||
},
|
||||
textTransformer: function(value) {
|
||||
var transformer = new Taglib.Transformer();
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = {
|
||||
path: value
|
||||
};
|
||||
}
|
||||
|
||||
propertyHandlers(value, {
|
||||
path: function(value) {
|
||||
var path = resolve(value, dirname);
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('Transformer at path "' + path + '" does not exist.');
|
||||
}
|
||||
|
||||
transformer.path = path;
|
||||
}
|
||||
|
||||
}, 'text-transformer in ' + path);
|
||||
|
||||
ok(transformer.path, '"path" is required for transformer');
|
||||
|
||||
taglib.addTextTransformer(transformer);
|
||||
}
|
||||
}, path);
|
||||
|
||||
|
||||
@ -92,10 +92,11 @@ function buildLookup(dirname) {
|
||||
var lookup = lookupCache[lookupCacheKey];
|
||||
if (lookup === undefined) {
|
||||
lookup = new TaglibLookup();
|
||||
for (var i=taglibs.length-1; i>=0; i--) {
|
||||
|
||||
for (var i=taglibs.length-1; i>=0; i--) {
|
||||
lookup.addTaglib(taglibs[i]);
|
||||
}
|
||||
lookup.finish(); // Handle all of the imports
|
||||
|
||||
lookupCache[lookupCacheKey] = lookup;
|
||||
}
|
||||
|
||||
@ -106,10 +107,10 @@ function addCoreTaglib(taglib) {
|
||||
exports.coreTaglibs.push(taglib);
|
||||
}
|
||||
|
||||
addCoreTaglib(taglibLoader.loadTaglibXmlFromFile(nodePath.join(__dirname, '../../taglibs/core/core.rtld')));
|
||||
addCoreTaglib(taglibLoader.loadTaglibXmlFromFile(nodePath.join(__dirname, '../../taglibs/html/html.rtld')));
|
||||
addCoreTaglib(taglibLoader.loadTaglibXmlFromFile(nodePath.join(__dirname, '../../taglibs/caching/caching.rtld')));
|
||||
addCoreTaglib(taglibLoader.loadTaglibXmlFromFile(nodePath.join(__dirname, '../../taglibs/layout/layout.rtld')));
|
||||
addCoreTaglib(taglibLoader.loadTaglibXmlFromFile(nodePath.join(__dirname, '../../taglibs/async/async.rtld')));
|
||||
addCoreTaglib(taglibLoader.load(nodePath.join(__dirname, '../../taglibs/core/raptor-taglib.json')));
|
||||
addCoreTaglib(taglibLoader.load(nodePath.join(__dirname, '../../taglibs/html/raptor-taglib.json')));
|
||||
addCoreTaglib(taglibLoader.load(nodePath.join(__dirname, '../../taglibs/caching/raptor-taglib.json')));
|
||||
addCoreTaglib(taglibLoader.load(nodePath.join(__dirname, '../../taglibs/layout/raptor-taglib.json')));
|
||||
addCoreTaglib(taglibLoader.load(nodePath.join(__dirname, '../../taglibs/async/raptor-taglib.json')));
|
||||
|
||||
exports.buildLookup = buildLookup;
|
||||
8
migration.md
Normal file
8
migration.md
Normal file
@ -0,0 +1,8 @@
|
||||
Raptor Templates Migration Guide
|
||||
================================
|
||||
|
||||
# Migration from XML-based Raptor Templates
|
||||
|
||||
|
||||
# Migration from Dust
|
||||
|
||||
113
package.json
113
package.json
@ -1,57 +1,58 @@
|
||||
{
|
||||
"name": "raptor-templates",
|
||||
"description": "Raptor Templates",
|
||||
"keywords": [
|
||||
"templating",
|
||||
"template",
|
||||
"async",
|
||||
"streaming"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/raptorjs3/raptor-templates.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test"
|
||||
},
|
||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||
"maintainers": [
|
||||
"Patrick Steele-Idem <pnidem@gmail.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"raptor-detect": "^0.2.0-beta",
|
||||
"raptor-logging": "^0.2.0-beta",
|
||||
"raptor-strings": "^0.2.0-beta",
|
||||
"raptor-regexp": "^0.2.0-beta",
|
||||
"raptor-util": "^0.2.0-beta",
|
||||
"raptor-arrays": "^0.2.0-beta",
|
||||
"raptor-json": "^0.2.0-beta",
|
||||
"raptor-modules": "^0.2.0-beta",
|
||||
"raptor-render-context": "^0.2.0-beta",
|
||||
"raptor-data-providers": "^0.2.0-beta",
|
||||
"raptor-xml": "^0.2.0-beta",
|
||||
"raptor-objects": "^0.2.0-beta",
|
||||
"raptor-ecma": "^0.2.0-beta",
|
||||
"raptor-files": "^0.2.0-beta",
|
||||
"htmlparser2": "~3.5.1",
|
||||
"char-props": "~0.1.5",
|
||||
"raptor-promises": "^0.2.0-beta",
|
||||
"raptor-args": "^0.1.9-beta",
|
||||
"minimatch": "^0.2.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.15.1",
|
||||
"chai": "~1.8.1",
|
||||
"raptor-cache": "^0.2.0-beta"
|
||||
},
|
||||
"license": "Apache License v2.0",
|
||||
"bin": {
|
||||
"rhtmlc": "bin/rhtmlc"
|
||||
},
|
||||
"main": "runtime/lib/raptor-templates.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"ebay": {},
|
||||
"version": "0.2.30-beta"
|
||||
}
|
||||
"name": "raptor-templates",
|
||||
"description": "Raptor Templates",
|
||||
"keywords": [
|
||||
"templating",
|
||||
"template",
|
||||
"async",
|
||||
"streaming"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/raptorjs3/raptor-templates.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test"
|
||||
},
|
||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||
"maintainers": [
|
||||
"Patrick Steele-Idem <pnidem@gmail.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"raptor-detect": "^0.2.0-beta",
|
||||
"raptor-logging": "^0.2.0-beta",
|
||||
"raptor-strings": "^0.2.0-beta",
|
||||
"raptor-regexp": "^0.2.0-beta",
|
||||
"raptor-util": "^0.2.0-beta",
|
||||
"raptor-arrays": "^0.2.0-beta",
|
||||
"raptor-json": "^0.2.0-beta",
|
||||
"raptor-modules": "^0.2.0-beta",
|
||||
"raptor-render-context": "^0.2.0-beta",
|
||||
"raptor-data-providers": "^0.2.0-beta",
|
||||
"raptor-xml": "^0.2.0-beta",
|
||||
"raptor-objects": "^0.2.0-beta",
|
||||
"raptor-ecma": "^0.2.0-beta",
|
||||
"raptor-files": "^0.2.0-beta",
|
||||
"htmlparser2": "~3.5.1",
|
||||
"char-props": "~0.1.5",
|
||||
"raptor-promises": "^0.2.0-beta",
|
||||
"raptor-args": "^0.1.9-beta",
|
||||
"minimatch": "^0.2.14",
|
||||
"property-handlers": "^0.2.1-beta"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.15.1",
|
||||
"chai": "~1.8.1",
|
||||
"raptor-cache": "^0.2.0-beta"
|
||||
},
|
||||
"license": "Apache License v2.0",
|
||||
"bin": {
|
||||
"rhtmlc": "bin/rhtmlc"
|
||||
},
|
||||
"main": "runtime/lib/raptor-templates.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"ebay": {},
|
||||
"version": "0.2.30-beta"
|
||||
}
|
||||
|
||||
106
raptor-templates-dust.md
Normal file
106
raptor-templates-dust.md
Normal file
@ -0,0 +1,106 @@
|
||||
Raptor Templates vs Dust
|
||||
========================
|
||||
|
||||
The philosophy for Raptor Templates is the following:
|
||||
|
||||
* Syntax should not be cryptic
|
||||
* Stay as close to JavaScript for better performance and easier learning
|
||||
- JavaScript expressions throughout (no new expression language)
|
||||
- Utilized closures for scoped variables and data
|
||||
* Stay as close to HTML
|
||||
* The templating language should not be restrictive
|
||||
- Whether to go "less logic" or "more logic" is up to the developer
|
||||
* High performance based on the following criteria:
|
||||
- Fast and lightweight runtime
|
||||
- Small compiled JavaScript output
|
||||
* Must support asynchronous rendering
|
||||
- Allow additional data to be asynchronously loaded after rendering begins
|
||||
* Must support streaming
|
||||
- Stream out bytes as they are generated
|
||||
* Modular and extensible architecture
|
||||
- Support custom tags
|
||||
- Provide ability to generate custom JavaScript at compile-time
|
||||
* Embrace Node.js and npm
|
||||
|
||||
# Data Passing
|
||||
|
||||
Dust allows data to be passed to template as a context object that supports lookup by simple names or complex paths.
|
||||
|
||||
# Syntax
|
||||
|
||||
## Dynamic Text
|
||||
|
||||
### Dust
|
||||
|
||||
* Simple key lookup:
|
||||
```
|
||||
{name}
|
||||
```
|
||||
|
||||
|
||||
Dust supports the following syntax for :
|
||||
```html
|
||||
Hello {name}!
|
||||
```
|
||||
|
||||
In comparison, Raptor Templates uses the following syntax for expressions:
|
||||
```html
|
||||
Hello $name
|
||||
```
|
||||
|
||||
Alternatively:
|
||||
```html
|
||||
Hello ${name.toUpperCase()}
|
||||
```
|
||||
|
||||
|
||||
Raptor Templates is an HTML-based templating language that understands the HTML structure of a template. This allows Raptor Templates to recognize templating directives applied as both HTML attributes and HTML tags. Dust, however, is a text-based templating language that does not understand the HTML structure of the document. In most cases, an HTML-based templating language allows for less code and less obtrusive code as shown in the following sample code:
|
||||
|
||||
## Looping and Conditionals
|
||||
|
||||
### Raptor Templates
|
||||
|
||||
```html
|
||||
<ul c:if="notEmpty(data.colors)">
|
||||
<li class="color" c:for="color in data.colors">
|
||||
${color}
|
||||
</li>
|
||||
</ul>
|
||||
<div c:else>
|
||||
No colors!
|
||||
</div>
|
||||
```
|
||||
|
||||
### Dust
|
||||
|
||||
```html
|
||||
{?colors}
|
||||
<ul>
|
||||
{#colors}
|
||||
<li class="color">
|
||||
{.}
|
||||
</li>
|
||||
{/colors}
|
||||
</ul>
|
||||
{/colors}
|
||||
|
||||
{^colors}
|
||||
<div>
|
||||
No colors!
|
||||
</div>
|
||||
{/colors}
|
||||
```
|
||||
|
||||
## Expressions
|
||||
Raptor Templates allows JavaScript expressions wherever expressions are allowed.
|
||||
|
||||
|
||||
# Whitespace
|
||||
|
||||
# Custom Tags
|
||||
|
||||
Raptor Templates allows you to extend the
|
||||
|
||||
|
||||
|
||||
has a very different syntax from Dust
|
||||
@ -114,5 +114,6 @@ module.exports = {
|
||||
}
|
||||
runtime.render(path, data, context);
|
||||
return this;
|
||||
}
|
||||
},
|
||||
xt: extend
|
||||
};
|
||||
@ -21,7 +21,6 @@
|
||||
* in the {@link raptor/templating/compiler} module.
|
||||
*/
|
||||
var renderContext = require('raptor-render-context');
|
||||
var createError = require('raptor-util/createError');
|
||||
var Context = renderContext.Context;
|
||||
var helpers = require('./helpers');
|
||||
var loader = require('./loader');
|
||||
@ -56,12 +55,7 @@ exports.render = function (templatePath, data, callback, context) {
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
templateFunc(data || {}, context); //Invoke the template rendering function with the required arguments
|
||||
} catch (e) {
|
||||
// context.emit('error', e);
|
||||
throw createError(new Error('Unable to render template with name "' + templatePath + '". Exception: ' + e), e);
|
||||
}
|
||||
templateFunc(data || {}, context); //Invoke the template rendering function with the required arguments
|
||||
|
||||
if (callback) {
|
||||
context
|
||||
|
||||
@ -12,10 +12,12 @@ module.exports = {
|
||||
node.addError('Either "var" or "data-provider" is required');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var argProps = [];
|
||||
var propsToRemove = [];
|
||||
node.forEachProperty(function (namespace, name, value) {
|
||||
if (namespace === '' && name.startsWith('arg-')) {
|
||||
node.forEachProperty(function (name, value) {
|
||||
if (name.startsWith('arg-')) {
|
||||
var argName = name.substring('arg-'.length);
|
||||
argProps.push(JSON.stringify(argName) + ': ' + value);
|
||||
propsToRemove.push(name);
|
||||
@ -30,7 +32,8 @@ module.exports = {
|
||||
}
|
||||
var arg = node.getProperty('arg');
|
||||
if (arg) {
|
||||
argString = 'require("raptor").extend(' + arg + ', ' + argString + ')';
|
||||
var extendFuncName = template.getStaticHelperFunction('extend', 'xt');
|
||||
argString = extendFuncName + '(' + arg + ', ' + argString + ')';
|
||||
}
|
||||
if (argString) {
|
||||
node.setProperty('arg', template.makeExpression(argString));
|
||||
|
||||
@ -10,7 +10,7 @@ module.exports = {
|
||||
var arg = input.arg || {};
|
||||
|
||||
arg.context = context;
|
||||
var asyncContext =context.beginAsync(input.timeout);
|
||||
var asyncContext = context.beginAsync(input.timeout);
|
||||
|
||||
function onError(e) {
|
||||
asyncContext.error(e || 'Async fragment failed');
|
||||
@ -26,6 +26,12 @@ module.exports = {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
var method = input.method;
|
||||
if (method) {
|
||||
dataProvider = dataProvider[method].bind(dataProvider);
|
||||
}
|
||||
|
||||
try {
|
||||
dataProviders.requestData(dataProvider, arg, function(err, data) {
|
||||
if (err) {
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<namespace>raptor-templates/async</namespace>
|
||||
<namespace>http://raptorjs.org/templates/async</namespace>
|
||||
<namespace>async</namespace>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>fragment</name>
|
||||
|
||||
<handler-class>./async-fragment-tag</handler-class>
|
||||
|
||||
<attribute name="dependency" type="string" target-property="dataProvider" deprecated="Use 'data-provider' instead"/>
|
||||
<attribute name="data-provider" type="string" />
|
||||
<attribute name="arg" type="expression" preserve-name="true"/>
|
||||
<attribute pattern="arg-*" type="string" preserve-name="true"/>
|
||||
<attribute name="var" type="identifier"/>
|
||||
<attribute name="timeout" type="integer" />
|
||||
|
||||
<variable name="context"/>
|
||||
<variable name-from-attribute="var OR dependency OR data-provider|keep"/>
|
||||
|
||||
<transformer>
|
||||
<path>./AsyncFragmentTagTransformer</path>
|
||||
<after>core/CoreTagTransformer</after>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
34
taglibs/async/raptor-taglib.json
Normal file
34
taglibs/async/raptor-taglib.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"tags": {
|
||||
"async-fragment": {
|
||||
"renderer": "./async-fragment-tag",
|
||||
"attributes": {
|
||||
"data-provider": {
|
||||
"type": "string"
|
||||
},
|
||||
"arg": {
|
||||
"type": "expression",
|
||||
"preserve-name": true
|
||||
},
|
||||
"arg-*": {
|
||||
"pattern": true,
|
||||
"type": "string",
|
||||
"preserve-name": true
|
||||
},
|
||||
"var": {
|
||||
"type": "identifier"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"vars": [
|
||||
"context",
|
||||
{
|
||||
"name-from-attribute": "var OR dependency OR data-provider|keep"
|
||||
}
|
||||
],
|
||||
"transformer": "./AsyncFragmentTagTransformer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<namespace>http://raptorjs.org/templates/caching</namespace>
|
||||
<namespace>caching</namespace>
|
||||
<namespace>raptor-templates/caching</namespace>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>cached-fragment</name>
|
||||
|
||||
<renderer>./CachedFragmentTag</renderer>
|
||||
|
||||
<attribute name="cache-key"/>
|
||||
<attribute name="cache-name"/>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
15
taglibs/caching/raptor-taglib.json
Normal file
15
taglibs/caching/raptor-taglib.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"tags": {
|
||||
"cached-fragment": {
|
||||
"renderer": "./CachedFragmentTag",
|
||||
"attributes": {
|
||||
"cache-key": {
|
||||
"type": "string"
|
||||
},
|
||||
"cache-name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,45 +50,24 @@ CoreTagTransformer.prototype = {
|
||||
this.findNestedAttrs(node, compiler, template);
|
||||
var inputAttr;
|
||||
var forEachNode;
|
||||
var namespace;
|
||||
var tag;
|
||||
var coreNS = compiler.taglibs.resolveNamespace('core');
|
||||
|
||||
function forEachProp(callback, thisObj) {
|
||||
var foundProps = {};
|
||||
|
||||
node.forEachAttributeAnyNS(function (attr) {
|
||||
if (attr.namespace === 'xml' || attr.namespace === 'http://www.w3.org/2000/xmlns/' || attr.namespace === 'http://www.w3.org/XML/1998/namespace' || attr.prefix == 'xmlns') {
|
||||
return; //Skip xmlns attributes
|
||||
}
|
||||
var prefix = attr.prefix;
|
||||
var attrUri = attr.namespace;
|
||||
var resolvedAttrNamespace = attrUri ? compiler.taglibs.resolveNamespace(attrUri) : null;
|
||||
attrUri = attr.prefix && resolvedAttrNamespace === tag.taglib.id ? null : attr.namespace;
|
||||
|
||||
var attrDef = compiler.taglibs.getAttribute(namespace, node.localName, attrUri, attr.localName);
|
||||
var attrDef = compiler.taglibs.getAttribute(node, attr);
|
||||
var type = attrDef ? attrDef.type || 'string' : 'string';
|
||||
|
||||
var taglibIdForTag = compiler.taglibs.resolveNamespaceForTag(tag);
|
||||
|
||||
var value;
|
||||
|
||||
// Check if the attribute and tag are part of the same taglib
|
||||
if (attrUri && compiler.taglibs.resolveNamespace(attrUri) === taglibIdForTag) {
|
||||
// If so, then don't repeat prefix
|
||||
prefix = '';
|
||||
}
|
||||
|
||||
|
||||
if (!attrDef) {
|
||||
// var isAttrForTaglib = compiler.taglibs.isTaglib(attrUri);
|
||||
//Tag doesn't allow dynamic attributes
|
||||
node.addError('The tag "' + tag.name + '" in taglib "' + taglibIdForTag + '" does not support attribute "' + attr + '"');
|
||||
node.addError('The tag "' + tag.name + '" in taglib "' + tag.taglibId + '" does not support attribute "' + attr + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (attr.value instanceof Expression) {
|
||||
value = attr.value;
|
||||
} else {
|
||||
@ -102,7 +81,7 @@ CoreTagTransformer.prototype = {
|
||||
}
|
||||
var propName;
|
||||
if (attrDef.dynamicAttribute) {
|
||||
propName = attr.localName;
|
||||
propName = attr.qName;
|
||||
} else {
|
||||
if (attrDef.targetProperty) {
|
||||
propName = attrDef.targetProperty;
|
||||
@ -114,38 +93,34 @@ CoreTagTransformer.prototype = {
|
||||
}
|
||||
|
||||
foundProps[propName] = true;
|
||||
callback.call(thisObj, attrUri, propName, value, prefix, attrDef);
|
||||
callback.call(thisObj, propName, value, attrDef);
|
||||
});
|
||||
|
||||
tag.forEachAttribute(function (attr) {
|
||||
if (attr.hasOwnProperty('defaultValue') && !foundProps[attr.name]) {
|
||||
callback.call(thisObj, '', attr.name, template.makeExpression(JSON.stringify(attr.defaultValue)), '', attr);
|
||||
callback.call(thisObj, attr.name, template.makeExpression(JSON.stringify(attr.defaultValue)), '', attr);
|
||||
}
|
||||
});
|
||||
}
|
||||
namespace = node.namespace;
|
||||
if (!namespace && node.isRoot() && node.localName === 'template') {
|
||||
namespace = 'core';
|
||||
}
|
||||
|
||||
tag = node.tag || compiler.taglibs.getTag(namespace, node.localName);
|
||||
tag = node.tag || compiler.taglibs.getTag(node);
|
||||
|
||||
var coreAttrHandlers = {
|
||||
'space': function(attr) {
|
||||
this.whitespace(attr);
|
||||
'c-space': function(attr) {
|
||||
this['c-whitespace'](attr);
|
||||
},
|
||||
'whitespace': function(attr) {
|
||||
'c-whitespace': function(attr) {
|
||||
if (attr.value === 'preserve') {
|
||||
node.setPreserveWhitespace(true);
|
||||
}
|
||||
},
|
||||
'escape-xml': function(attr) {
|
||||
'c-escape-xml': function(attr) {
|
||||
node.setEscapeXmlBodyText(attr.value !== 'false');
|
||||
},
|
||||
'parse-body-text': function(attr) {
|
||||
'c-parse-body-text': function(attr) {
|
||||
node.parseBodyText = attr.value !== 'false';
|
||||
},
|
||||
'when': function(attr) {
|
||||
'c-when': function(attr) {
|
||||
var whenNode = new WhenNode({
|
||||
test: new Expression(attr.value),
|
||||
pos: node.getPosition()
|
||||
@ -153,18 +128,18 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(whenNode, node);
|
||||
whenNode.appendChild(node);
|
||||
},
|
||||
'otherwise': function(attr) {
|
||||
'c-otherwise': function(attr) {
|
||||
var otherwiseNode = new OtherwiseNode({ pos: node.getPosition() });
|
||||
node.parentNode.replaceChild(otherwiseNode, node);
|
||||
otherwiseNode.appendChild(node);
|
||||
},
|
||||
'attrs': function(attr) {
|
||||
'c-attrs': function(attr) {
|
||||
node.dynamicAttributesExpression = attr.value;
|
||||
},
|
||||
'for-each': function(attr) {
|
||||
'c-for-each': function(attr) {
|
||||
this['for'](attr);
|
||||
},
|
||||
'for': function(attr) {
|
||||
'c-for': function(attr) {
|
||||
var forEachProps = AttributeSplitter.parse(attr.value, {
|
||||
each: { type: 'custom' },
|
||||
separator: { type: 'expression' },
|
||||
@ -189,7 +164,7 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(forEachNode, node);
|
||||
forEachNode.appendChild(node);
|
||||
},
|
||||
'if': function(attr) {
|
||||
'c-if': function(attr) {
|
||||
var ifNode = new IfNode({
|
||||
test: new Expression(attr.value),
|
||||
pos: node.getPosition()
|
||||
@ -199,7 +174,7 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(ifNode, node);
|
||||
ifNode.appendChild(node);
|
||||
},
|
||||
'else-if': function(attr) {
|
||||
'c-else-if': function(attr) {
|
||||
var elseIfNode = new ElseIfNode({
|
||||
test: new Expression(attr.value),
|
||||
pos: node.getPosition()
|
||||
@ -209,14 +184,14 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(elseIfNode, node);
|
||||
elseIfNode.appendChild(node);
|
||||
},
|
||||
'else': function(attr) {
|
||||
'c-else': function(attr) {
|
||||
var elseNode = new ElseNode({ pos: node.getPosition() });
|
||||
//Surround the existing node with an "if" node by replacing the current
|
||||
//node with the new "if" node and then adding the current node as a child
|
||||
node.parentNode.replaceChild(elseNode, node);
|
||||
elseNode.appendChild(node);
|
||||
},
|
||||
'with': function(attr) {
|
||||
'c-with': function(attr) {
|
||||
var withNode = new WithNode({
|
||||
vars: attr.value,
|
||||
pos: node.getPosition()
|
||||
@ -224,10 +199,10 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(withNode, node);
|
||||
withNode.appendChild(node);
|
||||
},
|
||||
'body-content': function(attr) {
|
||||
'c-body-content': function(attr) {
|
||||
this.content(attr);
|
||||
},
|
||||
'content': function(attr) {
|
||||
'c-content': function(attr) {
|
||||
var newChild = new WriteNode({
|
||||
expression: attr.value,
|
||||
pos: node.getPosition()
|
||||
@ -235,18 +210,18 @@ CoreTagTransformer.prototype = {
|
||||
node.removeChildren();
|
||||
node.appendChild(newChild);
|
||||
},
|
||||
'trim-body-indent': function(attr) {
|
||||
'c-trim-body-indent': function(attr) {
|
||||
if (attr.value === 'true') {
|
||||
node.trimBodyIndent = true;
|
||||
}
|
||||
},
|
||||
'strip': function(attr) {
|
||||
'c-strip': function(attr) {
|
||||
if (!node.setStripExpression) {
|
||||
node.addError('The c:strip directive is not allowed for target node');
|
||||
}
|
||||
node.setStripExpression(attr.value);
|
||||
},
|
||||
'replace': function(attr) {
|
||||
'c-replace': function(attr) {
|
||||
var replaceWriteNode = new WriteNode({
|
||||
expression: attr.value,
|
||||
pos: node.getPosition()
|
||||
@ -255,26 +230,17 @@ CoreTagTransformer.prototype = {
|
||||
node.parentNode.replaceChild(replaceWriteNode, node);
|
||||
node = replaceWriteNode;
|
||||
},
|
||||
'input': function(attr) {
|
||||
'c-input': function(attr) {
|
||||
inputAttr = attr.value;
|
||||
}
|
||||
};
|
||||
|
||||
node.forEachAttributeAnyNS(function(attr) {
|
||||
var attrNS = attr.namespace;
|
||||
if (!attrNS) {
|
||||
return;
|
||||
}
|
||||
|
||||
attrNS = compiler.taglibs.resolveNamespace(attrNS);
|
||||
|
||||
if (attrNS === coreNS) {
|
||||
node.removeAttributeNS(attr.namespace, attr.localName);
|
||||
var handler = coreAttrHandlers[attr.localName];
|
||||
if (!handler) {
|
||||
node.addError('Unsupported attribute: ' + attr.qName);
|
||||
}
|
||||
coreAttrHandlers[attr.localName](attr);
|
||||
node.forEachAttributeNS('', function(attr) {
|
||||
|
||||
var handler = coreAttrHandlers[attr.qName];
|
||||
if (handler) {
|
||||
node.removeAttribute(attr.localName);
|
||||
coreAttrHandlers[attr.localName](attr);
|
||||
}
|
||||
});
|
||||
|
||||
@ -299,35 +265,32 @@ CoreTagTransformer.prototype = {
|
||||
IncludeNode.convertNode(node, templatePath);
|
||||
}
|
||||
|
||||
forEachProp(function (namespace, name, value, prefix, attrDef) {
|
||||
forEachProp(function (name, value, attrDef) {
|
||||
if (attrDef.dynamicAttribute && attrDef.targetProperty) {
|
||||
if (attrDef.removeDashes === true) {
|
||||
name = removeDashes(name);
|
||||
}
|
||||
node.addDynamicAttribute(prefix ? prefix + ':' + name : name, value);
|
||||
node.addDynamicAttribute(name, value);
|
||||
node.setDynamicAttributesProperty(attrDef.targetProperty);
|
||||
} else {
|
||||
node.setPropertyNS(namespace, name, value);
|
||||
node.setProperty(name, value);
|
||||
}
|
||||
});
|
||||
|
||||
} else if (tag.nodeClass) {
|
||||
var NodeCompilerClass = require(tag.nodeClass);
|
||||
extend(node, NodeCompilerClass.prototype);
|
||||
NodeCompilerClass.call(node);
|
||||
node.setNodeClass(NodeCompilerClass);
|
||||
forEachProp(function (namespace, name, value) {
|
||||
node.setPropertyNS(namespace, name, value);
|
||||
forEachProp(function (name, value) {
|
||||
node.setProperty(name, value);
|
||||
});
|
||||
}
|
||||
} else if (namespace && compiler.isTaglib(namespace)) {
|
||||
node.addError('Tag ' + node.toString() + ' is not allowed for taglib "' + namespace + '"');
|
||||
}
|
||||
},
|
||||
findNestedAttrs: function (node, compiler, template) {
|
||||
var coreNS = compiler.taglibs.resolveNamespace('core');
|
||||
|
||||
node.forEachChild(function (child) {
|
||||
if (compiler.taglibs.resolveNamespace(child.namespace) === coreNS && child.localName === 'attr') {
|
||||
if (child.qName === 'c-attr') {
|
||||
this.handleAttr(child, compiler, template);
|
||||
}
|
||||
}, this);
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
function ElseIfNode(props) {
|
||||
ElseIfNode.$super.call(this, 'http://raptorjs.org/templates/core', 'else-if', 'c');
|
||||
ElseIfNode.$super.call(this, 'c-else-if');
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
function ElseNode(props) {
|
||||
ElseNode.$super.call(this, 'else', 'http://raptorjs.org/templates/core', 'c');
|
||||
ElseNode.$super.call(this, 'c-else');
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ ElseTagTransformer.prototype = {
|
||||
process: function (node, compiler) {
|
||||
var curNode = node.previousSibling;
|
||||
var matchingNode;
|
||||
var IfNode = compiler.getNodeClass('http://raptorjs.org/templates/core', 'if');
|
||||
var ElseIfNode = compiler.getNodeClass('http://raptorjs.org/templates/core', 'else-if');
|
||||
var IfNode = compiler.getNodeClass('c-if');
|
||||
var ElseIfNode = compiler.getNodeClass('c-else-if');
|
||||
var whitespaceNodes = [];
|
||||
while (curNode) {
|
||||
if (curNode.getNodeClass() === ElseIfNode || curNode.getNodeClass() === IfNode) {
|
||||
@ -42,7 +42,7 @@ ElseTagTransformer.prototype = {
|
||||
curNode = curNode.previousSibling;
|
||||
}
|
||||
if (!matchingNode) {
|
||||
node.addError('<c:if> or <c:else-if> node not found immediately before ' + node.toString() + ' tag.');
|
||||
node.addError('<c-if> or <c-else-if> node not found immediately before ' + node.toString() + ' tag.');
|
||||
return;
|
||||
}
|
||||
whitespaceNodes.forEach(function (whitespaceNode) {
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
function IfNode(props) {
|
||||
IfNode.$super.call(this, 'if', 'http://raptorjs.org/templates/core', 'c');
|
||||
IfNode.$super.call(this, 'c-if');
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ IncludeNode.prototype = {
|
||||
var templateData = this.getProperty('templateData') || this.getProperty('template-data');
|
||||
var resourcePath;
|
||||
var _this = this;
|
||||
|
||||
if (templatePath) {
|
||||
this.removeProperty('template');
|
||||
|
||||
@ -46,15 +47,18 @@ IncludeNode.prototype = {
|
||||
dataExpression = {
|
||||
toString: function () {
|
||||
var propParts = [];
|
||||
_this.forEachPropertyNS('', function (name, value) {
|
||||
|
||||
_this.forEachProperty(function (name, value) {
|
||||
name = name.replace(/-([a-z])/g, function (match, lower) {
|
||||
return lower.toUpperCase();
|
||||
});
|
||||
propParts.push(stringify(name) + ': ' + value);
|
||||
}, _this);
|
||||
|
||||
if (_this.hasChildren()) {
|
||||
propParts.push(stringify('invokeBody') + ': ' + _this.getBodyContentFunctionExpression(template, false));
|
||||
}
|
||||
|
||||
return '{' + propParts.join(', ') + '}';
|
||||
}
|
||||
};
|
||||
|
||||
@ -58,7 +58,7 @@ InvokeNode.prototype = {
|
||||
* VALIDATION:
|
||||
* Loop over all of the provided attributes and make sure they are allowed
|
||||
*/
|
||||
this.forEachPropertyNS('', function (name, value) {
|
||||
this.forEachProperty(function (name, value) {
|
||||
if (name === 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -100,15 +100,13 @@ TagHandlerNode.prototype = {
|
||||
this.tag.forEachImportedVariable(function (importedVariable) {
|
||||
this.setProperty(importedVariable.targetProperty, new Expression(importedVariable.expression));
|
||||
}, this);
|
||||
var namespacedProps = extend({}, this.getPropertiesByNS());
|
||||
delete namespacedProps[''];
|
||||
var hasNamespacedProps = !objects.isEmpty(namespacedProps);
|
||||
|
||||
var _this = this;
|
||||
var variableNames = [];
|
||||
_this.tag.forEachVariable(function (v) {
|
||||
_this.tag.forEachVariable(function (nestedVar) {
|
||||
var varName;
|
||||
if (v.nameFromAttribute) {
|
||||
var possibleNameAttributes = v.nameFromAttribute.split(/\s+or\s+|\s*,\s*/i);
|
||||
if (nestedVar.nameFromAttribute) {
|
||||
var possibleNameAttributes = nestedVar.nameFromAttribute.split(/\s+or\s+|\s*,\s*/i);
|
||||
for (var i = 0, len = possibleNameAttributes.length; i < len; i++) {
|
||||
var attrName = possibleNameAttributes[i];
|
||||
var keep = false;
|
||||
@ -130,7 +128,7 @@ TagHandlerNode.prototype = {
|
||||
varName = '_var'; // Let it continue with errors
|
||||
}
|
||||
} else {
|
||||
varName = v.name;
|
||||
varName = nestedVar.name;
|
||||
if (!varName) {
|
||||
this.addError('Variable name is required');
|
||||
varName = '_var'; // Let it continue with errors
|
||||
@ -168,25 +166,10 @@ TagHandlerNode.prototype = {
|
||||
template.code(',\n').line('function(' + bodyParams.join(',') + ') {').indent(function () {
|
||||
_this.generateCodeForChildren(template);
|
||||
}).indent().code('}');
|
||||
} else {
|
||||
if (hasNamespacedProps) {
|
||||
template.code(',\n').indent().code('null');
|
||||
}
|
||||
}
|
||||
if (hasNamespacedProps) {
|
||||
template.code(',\n').line('{').indent(function () {
|
||||
var first = true;
|
||||
forEachEntry(namespacedProps, function (namespace, props) {
|
||||
if (!first) {
|
||||
template.code(',\n');
|
||||
}
|
||||
template.code(template.indentStr() + '"' + namespace + '": ' + getPropsStr(props, template));
|
||||
first = false;
|
||||
});
|
||||
}).indent().code('}');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (_this.postInvokeCode.length) {
|
||||
_this.postInvokeCode.forEach(function (code) {
|
||||
template.indent().code(code).code('\n');
|
||||
|
||||
@ -1,503 +0,0 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<namespace>http://raptorjs.org/templates/core</namespace>
|
||||
<namespace>core</namespace>
|
||||
<namespace>c</namespace>
|
||||
|
||||
<tag id="template">
|
||||
|
||||
<name>template</name>
|
||||
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>params</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>imports</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<namespace>*</namespace>
|
||||
<name>functions</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<namespace>*</namespace>
|
||||
<name>import-functions</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<namespace>*</namespace>
|
||||
<name>import-helper-object</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<node-class>./TemplateNode</node-class>
|
||||
</tag>
|
||||
|
||||
<tag extends="template">
|
||||
|
||||
<namespace></namespace>
|
||||
<name>template</name>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>*</name>
|
||||
<namespace>*</namespace> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<attribute>
|
||||
<name>space</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>whitespace</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>for</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>for-each</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>if</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>else</name>
|
||||
<type>empty</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>else-if</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>attrs</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>when</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>with</name>
|
||||
<type>custom</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>otherwise</name>
|
||||
<type>empty</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>parse-body-text</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>trim-body-indent</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>strip</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>bodyContent</name>
|
||||
<type>expression</type>
|
||||
<deprecated>Use "content" attribute instead. This will be removed in the future.</deprecated>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>content</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>replace</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>input</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<path>./CoreTagTransformer</path>
|
||||
<name>CoreTagTransformer</name>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>for</name>
|
||||
|
||||
<node-class>./ForNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>each</name>
|
||||
<required>false</required>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>separator</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>status-var</name>
|
||||
<type>identifier</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>varStatus</name>
|
||||
<type>identifier</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
<deprecated>Use status-var instead. This will be removed in the future.</deprecated>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>for-loop</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>iterator</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>write</name>
|
||||
|
||||
<node-class>./WriteNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<required>true</required>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>escapeXml</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
<deprecated>Use escape-xml instead. This will be removed in the future.</deprecated>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>escape-xml</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>if</name>
|
||||
|
||||
<node-class>./IfNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>test</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>else</name>
|
||||
<node-class>./ElseNode</node-class>
|
||||
|
||||
<transformer>
|
||||
<path>./ElseTagTransformer</path>
|
||||
<name>ElseTagTransformer</name>
|
||||
<after>core/CoreTagTransformer</after>
|
||||
<properties>
|
||||
<type>else</type>
|
||||
</properties>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>else-if</name>
|
||||
<attribute name="test" type="expression"/>
|
||||
|
||||
<node-class>./ElseIfNode</node-class>
|
||||
|
||||
<transformer>
|
||||
<path>./ElseTagTransformer</path>
|
||||
<name>ElseIfTagTransformer</name>
|
||||
<after>core/CoreTagTransformer</after>
|
||||
<properties>
|
||||
<type>else-if</type>
|
||||
</properties>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>invoke</name>
|
||||
|
||||
<node-class>./InvokeNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>function</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>*</name>
|
||||
<namespace></namespace>
|
||||
<type>string</type>
|
||||
<allow-expressions>true</allow-expressions>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>choose</name>
|
||||
|
||||
<node-class>./ChooseNode</node-class>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>when</name>
|
||||
|
||||
<node-class>./WhenNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>test</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>otherwise</name>
|
||||
|
||||
<node-class>./OtherwiseNode</node-class>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>def</name>
|
||||
|
||||
<node-class>./DefNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>function</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>body-param</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>with</name>
|
||||
|
||||
<node-class>./WithNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>vars</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>include</name>
|
||||
|
||||
<node-class>./IncludeNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>template</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>templateData</name>
|
||||
<type>expression</type>
|
||||
<deprecated>Use template-data instead. This will be removed in the future.</deprecated>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>template-data</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>resource</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>static</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>*</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>attr</name>
|
||||
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>namespace</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>prefix</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>var</name>
|
||||
|
||||
<node-class>./VarNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>static</name>
|
||||
<type>boolean</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>string-value</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>boolean-value</name>
|
||||
<type>boolean</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>number-value</name>
|
||||
<type>number</type>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>require</name>
|
||||
|
||||
<node-class>./RequireNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>module</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>assign</name>
|
||||
|
||||
<node-class>./AssignNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<text-transformer path="./CoreTextTransformer"/>
|
||||
|
||||
</raptor-taglib>
|
||||
291
taglibs/core/raptor-taglib.json
Normal file
291
taglibs/core/raptor-taglib.json
Normal file
@ -0,0 +1,291 @@
|
||||
{
|
||||
"tags": {
|
||||
"c-template": {
|
||||
"attributes": {
|
||||
"name": {
|
||||
"allow-expressions": false,
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"allow-expressions": false,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"node-class": "./TemplateNode"
|
||||
},
|
||||
"*": {
|
||||
"attributes": {
|
||||
"c-space": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"c-whitespace": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"c-for": {
|
||||
"allow-expressions": false,
|
||||
"type": "string"
|
||||
},
|
||||
"c-for-each": {
|
||||
"allow-expressions": false,
|
||||
"type": "string"
|
||||
},
|
||||
"c-if": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-else": {
|
||||
"type": "empty"
|
||||
},
|
||||
"c-else-if": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-attrs": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-when": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-with": {
|
||||
"type": "custom"
|
||||
},
|
||||
"c-otherwise": {
|
||||
"type": "empty"
|
||||
},
|
||||
"c-parse-body-text": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"c-trim-body-indent": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"c-strip": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"c-content": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-replace": {
|
||||
"type": "expression"
|
||||
},
|
||||
"c-input": {
|
||||
"type": "expression"
|
||||
}
|
||||
},
|
||||
"transformer": {
|
||||
"path": "./CoreTagTransformer",
|
||||
"priority": 0
|
||||
}
|
||||
},
|
||||
"c-for": {
|
||||
"node-class": "./ForNode",
|
||||
"attributes": {
|
||||
"each": {
|
||||
"required": false,
|
||||
"allow-expressions": false,
|
||||
"type": "string"
|
||||
},
|
||||
"separator": {
|
||||
"type": "string"
|
||||
},
|
||||
"status-var": {
|
||||
"type": "identifier",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"for-loop": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"iterator": {
|
||||
"type": "expression"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-write": {
|
||||
"node-class": "./WriteNode",
|
||||
"attributes": {
|
||||
"value": {
|
||||
"required": true,
|
||||
"type": "expression"
|
||||
},
|
||||
"escape-xml": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-if": {
|
||||
"node-class": "./IfNode",
|
||||
"attributes": {
|
||||
"test": {
|
||||
"type": "expression"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-else": {
|
||||
"node-class": "./ElseNode",
|
||||
"transformer": {
|
||||
"path": "./ElseTagTransformer",
|
||||
"name": "ElseTagTransformer",
|
||||
"properties": {
|
||||
"type": "else"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-else-if": {
|
||||
"attributes": {
|
||||
"test": {
|
||||
"type": "expression"
|
||||
}
|
||||
},
|
||||
"node-class": "./ElseIfNode",
|
||||
"transformer": {
|
||||
"path": "./ElseTagTransformer",
|
||||
"name": "ElseIfTagTransformer",
|
||||
"properties": {
|
||||
"type": "else-if"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-invoke": {
|
||||
"node-class": "./InvokeNode",
|
||||
"attributes": {
|
||||
"function": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false,
|
||||
"required": true
|
||||
},
|
||||
"*": {
|
||||
"type": "string",
|
||||
"allow-expressions": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-choose": {
|
||||
"node-class": "./ChooseNode"
|
||||
},
|
||||
"c-when": {
|
||||
"node-class": "./WhenNode",
|
||||
"attributes": {
|
||||
"test": {
|
||||
"type": "expression"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-otherwise": {
|
||||
"node-class": "./OtherwiseNode"
|
||||
},
|
||||
"c-def": {
|
||||
"node-class": "./DefNode",
|
||||
"attributes": {
|
||||
"function": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"body-param": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-with": {
|
||||
"node-class": "./WithNode",
|
||||
"attributes": {
|
||||
"vars": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-include": {
|
||||
"node-class": "./IncludeNode",
|
||||
"attributes": {
|
||||
"template": {
|
||||
"type": "string"
|
||||
},
|
||||
"template-data": {
|
||||
"type": "expression"
|
||||
},
|
||||
"resource": {
|
||||
"type": "string"
|
||||
},
|
||||
"static": {
|
||||
"type": "boolean",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"*": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-attr": {
|
||||
"attributes": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-var": {
|
||||
"node-class": "./VarNode",
|
||||
"attributes": {
|
||||
"name": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"value": {
|
||||
"type": "expression"
|
||||
},
|
||||
"static": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"string-value": {
|
||||
"type": "string"
|
||||
},
|
||||
"boolean-value": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"number-value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-require": {
|
||||
"node-class": "./RequireNode",
|
||||
"attributes": {
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"var": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"c-assign": {
|
||||
"node-class": "./AssignNode",
|
||||
"attributes": {
|
||||
"var": {
|
||||
"type": "custom",
|
||||
"allow-expressions": false
|
||||
},
|
||||
"value": {
|
||||
"type": "expression"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"text-transformer": {
|
||||
"path": "./CoreTextTransformer"
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ HtmlTagTransformer.prototype = {
|
||||
var allowSelfClosing = options.allowSelfClosing || {};
|
||||
var startTagOnly = options.startTagOnly || {};
|
||||
var lookupKey = node.namespace ? node.namespace + ':' + node.localName : node.localName;
|
||||
|
||||
if (node.isPreserveWhitespace() == null) {
|
||||
if (preserveWhitespace[lookupKey] === true) {
|
||||
node.setPreserveWhitespace(true);
|
||||
@ -36,16 +37,15 @@ HtmlTagTransformer.prototype = {
|
||||
if (compiler.options.xhtml !== true && startTagOnly[lookupKey] === true) {
|
||||
node.setStartTagOnly(true);
|
||||
}
|
||||
|
||||
|
||||
if (node.getQName() === 'html' && node.hasAttributeNS('raptor-templates/html', 'doctype')) {
|
||||
var doctype = node.getAttributeNS('raptor-templates/html', 'doctype');
|
||||
if (node.getQName() === 'html' && node.hasAttribute('html-doctype')) {
|
||||
var doctype = node.getAttribute('html-doctype');
|
||||
var docTypeNode = new DocTypeNode({
|
||||
value: doctype,
|
||||
pos: node.getPosition()
|
||||
});
|
||||
node.parentNode.insertBefore(docTypeNode, node);
|
||||
node.removeAttributeNS('raptor-templates/html', 'doctype');
|
||||
node.removeAttribute('html-doctype');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<namespace>raptor-templates/html</namespace>
|
||||
<namespace>http://raptorjs.org/templates/html</namespace>
|
||||
<namespace>html</namespace>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>pre</name>
|
||||
<namespace></namespace> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<path>./HtmlTagTransformer</path>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>html</name>
|
||||
<namespace></namespace> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<attribute name="doctype" type="string"/>
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<path>./HtmlTagTransformer</path>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>doctype</name>
|
||||
<attribute name="value" type="custom"/>
|
||||
<node-class>./DocTypeNode</node-class>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>*</name>
|
||||
<namespace>*</namespace> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<path>./HtmlTagTransformer</path>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>comment</name>
|
||||
<renderer>./CommentTag</renderer>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
25
taglibs/html/raptor-taglib.json
Normal file
25
taglibs/html/raptor-taglib.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"tags": {
|
||||
"html-html": {
|
||||
"attributes": {
|
||||
"doctype": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"html-doctype": {
|
||||
"attributes": {
|
||||
"value": {
|
||||
"type": "custom"
|
||||
}
|
||||
},
|
||||
"node-class": "./DocTypeNode"
|
||||
},
|
||||
"*": {
|
||||
"transformer": "./HtmlTagTransformer"
|
||||
},
|
||||
"html-comment": {
|
||||
"renderer": "./CommentTag"
|
||||
}
|
||||
}
|
||||
}
|
||||
42
taglibs/layout/raptor-taglib.json
Normal file
42
taglibs/layout/raptor-taglib.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"tags": {
|
||||
"layout-use": {
|
||||
"renderer": "./UseTag",
|
||||
"attributes": {
|
||||
"template": {
|
||||
"type": "path"
|
||||
},
|
||||
"*": {
|
||||
"remove-dashes": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"var": "_layout"
|
||||
},
|
||||
"layout-put": {
|
||||
"renderer": "./PutTag",
|
||||
"attributes": {
|
||||
"into": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"import-var": {
|
||||
"_layout": "_layout"
|
||||
}
|
||||
},
|
||||
"layout-placeholder": {
|
||||
"renderer": "./PlaceholderTag",
|
||||
"attributes": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"import-var": {
|
||||
"content": "data.layoutContent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,8 @@ function testRender(path, data, done, options) {
|
||||
var Context = raptorTemplates.Context;
|
||||
var context = options.context || new Context(new StringBuilder());
|
||||
|
||||
|
||||
require('../compiler').defaultOptions.checkUpToDate = false;
|
||||
|
||||
if (options.dataProviders) {
|
||||
var dataProviders = require('raptor-data-providers').forContext(context);
|
||||
dataProviders.register(options.dataProviders);
|
||||
|
||||
@ -27,6 +27,9 @@ function testRender(path, data, done, options) {
|
||||
|
||||
|
||||
var raptorTemplates = require('../');
|
||||
|
||||
require('../compiler').defaultOptions.checkUpToDate = false;
|
||||
|
||||
var Context = raptorTemplates.Context;
|
||||
var context = options.context || new Context(new StringBuilder());
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ function testRender(path, data, done, options) {
|
||||
|
||||
// console.log('\nCompiled (' + inputPath + '):\n---------\n' + compiledSrc);
|
||||
|
||||
|
||||
require('../compiler').defaultOptions.checkUpToDate = false;
|
||||
|
||||
var raptorTemplates = require('../');
|
||||
var Context = raptorTemplates.Context;
|
||||
|
||||
@ -27,7 +27,7 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
// console.log('LOOKUP: ', Object.keys(lookup.attributes));
|
||||
var ifAttr = lookup.getAttribute('', 'div', 'c', 'if');
|
||||
var ifAttr = lookup.getAttribute('div', 'c-if');
|
||||
expect(ifAttr != null).to.equal(true);
|
||||
expect(ifAttr.type).to.equal('expression');
|
||||
});
|
||||
@ -35,44 +35,42 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
it('should lookup core tag for top-level template', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
var ifTag = lookup.getTag('c', 'if');
|
||||
var ifTag = lookup.getTag('c-if');
|
||||
expect(ifTag != null).to.equal(true);
|
||||
expect(ifTag.name).to.equal('if');
|
||||
expect(ifTag.namespace).to.equal(null);
|
||||
expect(ifTag.name).to.equal('c-if');
|
||||
});
|
||||
|
||||
it('should lookup core template for top-level template', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
// console.log(Object.keys(lookup.tags));
|
||||
var templateTag = lookup.getTag('c', 'template');
|
||||
var templateTag = lookup.getTag('c-template');
|
||||
expect(templateTag != null).to.equal(true);
|
||||
expect(templateTag.name).to.equal('template');
|
||||
expect(templateTag.namespace).to.equal(null);
|
||||
expect(templateTag.name).to.equal('c-template');
|
||||
});
|
||||
|
||||
it('should lookup custom tag for top-level template', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
var tag = lookup.getTag('test', 'hello');
|
||||
var tag = lookup.getTag('test-hello');
|
||||
// console.log(Object.keys(lookup.tags));
|
||||
expect(tag != null).to.equal(true);
|
||||
expect(tag.name).to.equal('hello');
|
||||
expect(tag.namespace).to.equal(undefined);
|
||||
expect(tag.name).to.equal('test-hello');
|
||||
});
|
||||
|
||||
it('should lookup custom attributes for top-level template', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
// console.log(Object.keys(lookup.attributes));
|
||||
var attr = lookup.getAttribute('test', 'hello', '', 'name');
|
||||
var attr = lookup.getAttribute('test-hello', 'name');
|
||||
expect(attr != null).to.equal(true);
|
||||
expect(attr.type).to.equal('string');
|
||||
|
||||
var attr2 = lookup.getAttribute('test', 'hello', 'test', 'name');
|
||||
expect(attr2).to.equal(attr);
|
||||
var attr2 = lookup.getAttribute('test-hello', 'splat');
|
||||
expect(attr2 != null).to.equal(true);
|
||||
expect(attr2.type).to.equal('number');
|
||||
|
||||
attr = lookup.getAttribute('test', 'hello', '', 'expr');
|
||||
attr = lookup.getAttribute('test-hello', 'expr');
|
||||
expect(attr != null).to.equal(true);
|
||||
expect(attr.type).to.equal('expression');
|
||||
});
|
||||
@ -81,7 +79,7 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
|
||||
// console.log(Object.keys(lookup.attributes));
|
||||
var attr = lookup.getAttribute('test', 'hello', '', 'DYNAMIC');
|
||||
var attr = lookup.getAttribute('test-hello', 'DYNAMIC');
|
||||
expect(attr != null).to.equal(true);
|
||||
expect(attr.type).to.equal('number');
|
||||
});
|
||||
@ -100,18 +98,17 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
it('should lookup nested tags', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
var tag = lookup.getTag('nested', 'foo');
|
||||
var tag = lookup.getTag('nested-foo');
|
||||
|
||||
expect(tag != null).to.equal(true);
|
||||
expect(tag.name).to.equal('foo');
|
||||
expect(tag.namespace).to.equal(undefined);
|
||||
expect(tag.name).to.equal('nested-foo');
|
||||
});
|
||||
|
||||
it('should lookup attributes for nested tags', function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
// console.log(Object.keys(lookup.attributes));
|
||||
var attr = lookup.getAttribute('nested', 'foo', '', 'attr1');
|
||||
var attr = lookup.getAttribute('nested-foo', 'attr1');
|
||||
expect(attr != null).to.equal(true);
|
||||
expect(attr.type).to.equal('string');
|
||||
});
|
||||
@ -122,7 +119,7 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
|
||||
lookup.forEachTagTransformer('', 'div', function(transformer) {
|
||||
lookup.forEachTagTransformer('div', function(transformer) {
|
||||
transformers.push(transformer);
|
||||
});
|
||||
|
||||
@ -133,36 +130,37 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
var transformers;
|
||||
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
var lookup;
|
||||
// lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
|
||||
transformers = [];
|
||||
lookup.forEachTagTransformer('nested', 'foo', function(transformer) {
|
||||
transformers.push(transformer);
|
||||
});
|
||||
// transformers = [];
|
||||
// lookup.forEachTagTransformer('nested-foo', function(transformer) {
|
||||
// transformers.push(transformer);
|
||||
// });
|
||||
|
||||
expect(transformers.length).to.equal(2);
|
||||
// expect(transformers.length).to.equal(2);
|
||||
|
||||
lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/transformers'));
|
||||
|
||||
transformers = [];
|
||||
lookup.forEachTagTransformer('transform', 'foo', function(transformer) {
|
||||
lookup.forEachTagTransformer('transform-foo', function(transformer) {
|
||||
transformers.push(transformer);
|
||||
});
|
||||
|
||||
expect(transformers.length).to.equal(3);
|
||||
expect(transformers[0].path.indexOf('HtmlTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[1].name).to.equal('foo');
|
||||
expect(transformers[2].name).to.equal('CoreTagTransformer');
|
||||
|
||||
expect(transformers.length).to.equal(3);
|
||||
expect(transformers[0].path.indexOf('foo')).to.not.equal(-1);
|
||||
expect(transformers[1].path.indexOf('CoreTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[2].path.indexOf('HtmlTagTransformer')).to.not.equal(-1);
|
||||
|
||||
transformers = [];
|
||||
lookup.forEachTagTransformer('transform', 'bar', function(transformer) {
|
||||
lookup.forEachTagTransformer('transform-bar', function(transformer) {
|
||||
transformers.push(transformer);
|
||||
});
|
||||
|
||||
expect(transformers.length).to.equal(3);
|
||||
expect(transformers[0].path.indexOf('HtmlTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[1].name).to.equal('CoreTagTransformer');
|
||||
expect(transformers[2].name).to.equal('bar');
|
||||
expect(transformers[0].path.indexOf('CoreTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[1].path.indexOf('bar')).to.not.equal(-1);
|
||||
expect(transformers[2].path.indexOf('HtmlTagTransformer')).to.not.equal(-1);
|
||||
});
|
||||
|
||||
it('should lookup tag transformers core tag with custom node', function() {
|
||||
@ -171,14 +169,14 @@ describe('raptor-templates/taglib-lookup' , function() {
|
||||
var taglibLookup = require('../compiler/lib/taglib-lookup');
|
||||
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
|
||||
|
||||
lookup.forEachTagTransformer('c', 'else', function(transformer) {
|
||||
lookup.forEachTagTransformer('c-else', function(transformer) {
|
||||
transformers.push(transformer);
|
||||
});
|
||||
|
||||
expect(transformers.length).to.equal(3);
|
||||
// expect(transformers[0].name).to.equal('HTagTransformer');
|
||||
expect(transformers[1].name).to.equal('CoreTagTransformer');
|
||||
expect(transformers[2].name).to.equal('ElseTagTransformer');
|
||||
expect(transformers[0].path.indexOf('CoreTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[1].path.indexOf('ElseTagTransformer')).to.not.equal(-1);
|
||||
expect(transformers[2].path.indexOf('HtmlTagTransformer')).to.not.equal(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -1 +1 @@
|
||||
<test:hello name="World"/>
|
||||
<test-hello name="World"/>
|
||||
@ -1,11 +1,10 @@
|
||||
{
|
||||
"namespace": "nested",
|
||||
"tags": {
|
||||
"foo": {
|
||||
"renderer": "./foo-renderer.js",
|
||||
"attributes": {
|
||||
"attr1": "string"
|
||||
}
|
||||
}
|
||||
"nested-foo": {
|
||||
"renderer": "./foo-renderer.js",
|
||||
"attributes": {
|
||||
"attr1": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<c:var name="tag" value="data.tag"/>
|
||||
<c:var name="content" value="data.content"/>
|
||||
<c:var name="title" value="data.title"/>
|
||||
<c-var name="tag" value="data.tag"/>
|
||||
<c-var name="content" value="data.content"/>
|
||||
<c-var name="title" value="data.title"/>
|
||||
|
||||
<span title="$title" data-content="$content">
|
||||
<c:invoke function="tag.invokeBody()" c:if="tag.invokeBody"/>
|
||||
<c-invoke function="tag.invokeBody()" c-if="tag.invokeBody"/>
|
||||
</span>
|
||||
@ -1,19 +1,18 @@
|
||||
{
|
||||
"namespace": ["test", "http://raptorjs.org/templates/test"],
|
||||
"tags": {
|
||||
"hello": "hello-tag.json",
|
||||
"simpleHello": {
|
||||
"test-hello": "hello-tag.json",
|
||||
"test-simpleHello": {
|
||||
"renderer": "./simple-hello-tag.js",
|
||||
"attributes": {
|
||||
"name": "string",
|
||||
"adult": "boolean"
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"test-tabs": {
|
||||
"renderer": "./tabs-tag.js",
|
||||
"var": "tabs"
|
||||
},
|
||||
"tab": {
|
||||
"test-tab": {
|
||||
"renderer": "./tab-tag.js",
|
||||
"import-var": {
|
||||
"tabs": "tabs"
|
||||
@ -22,7 +21,7 @@
|
||||
"title": "string"
|
||||
}
|
||||
},
|
||||
"dynamic-attributes": {
|
||||
"test-dynamic-attributes": {
|
||||
"renderer": "./dynamic-attributes-tag.js",
|
||||
"attributes": {
|
||||
"test": "string",
|
||||
@ -32,14 +31,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dynamic-attributes2": {
|
||||
"test-dynamic-attributes2": {
|
||||
"renderer": "./dynamic-attributes-tag2.js",
|
||||
"attributes": {
|
||||
"test": "string",
|
||||
"*": "string"
|
||||
}
|
||||
},
|
||||
"dynamic-attributes3": {
|
||||
"test-dynamic-attributes3": {
|
||||
"renderer": "./dynamic-attributes-tag3.js",
|
||||
"attributes": {
|
||||
"test": "string",
|
||||
@ -49,21 +48,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"popover": {
|
||||
"test-popover": {
|
||||
"renderer": "./popover-tag.js",
|
||||
"attributes": {
|
||||
"title": "string",
|
||||
"content": "string"
|
||||
}
|
||||
},
|
||||
"page-title": {
|
||||
"test-page-title": {
|
||||
"template": "./page-title-tag.rhtml",
|
||||
"attributes": {
|
||||
"title": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"default-attributes": {
|
||||
"test-default-attributes": {
|
||||
"renderer": "./default-attributes-tag.js",
|
||||
"attributes": {
|
||||
"prop1": "string",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<ul>
|
||||
<li c:for="userId in [0, 1, 2, 3]">
|
||||
<async:fragment dependency="userInfo" var="userInfo" arg-userId="$userId">
|
||||
<li c-for="userId in [0, 1, 2, 3]">
|
||||
<async-fragment data-provider="userInfo" var="userInfo" arg-userId="$userId">
|
||||
<ul>
|
||||
<li>
|
||||
<b>Name:</b> $userInfo.name
|
||||
@ -12,6 +12,6 @@
|
||||
<b>Occupation:</b> $userInfo.occupation
|
||||
</li>
|
||||
</ul>
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
</li>
|
||||
</ul>
|
||||
@ -1,7 +1,7 @@
|
||||
<async:fragment dependency="contextData" var="d1">
|
||||
<async-fragment data-provider="contextData" var="d1">
|
||||
$d1.name
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
|
||||
<async:fragment dependency="sharedData" var="d2">
|
||||
<async-fragment data-provider="sharedData" var="d2">
|
||||
$d2.name
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
@ -1,3 +1,3 @@
|
||||
<async:fragment data-provider="${data.userInfo}" var="userInfo">
|
||||
<async-fragment data-provider="${data.userInfo}" var="userInfo">
|
||||
Hello $userInfo.name
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
@ -1,7 +1,7 @@
|
||||
<c:def function="asyncMacro(num)">
|
||||
<c-def function="asyncMacro(num)">
|
||||
$num
|
||||
</c:def>1
|
||||
<async:fragment dependency="D1">
|
||||
<c:invoke function="asyncMacro" num="2"/>
|
||||
</async:fragment>
|
||||
</c-def>1
|
||||
<async-fragment data-provider="D1">
|
||||
<c-invoke function="asyncMacro" num="2"/>
|
||||
</async-fragment>
|
||||
3
|
||||
@ -1,17 +1,17 @@
|
||||
1
|
||||
<async:fragment data-provider="D1" var="d1">
|
||||
<async-fragment data-provider="D1" var="d1">
|
||||
2
|
||||
<async:fragment data-provider="D2" var="d2">
|
||||
<async-fragment data-provider="D2" var="d2">
|
||||
3
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
4
|
||||
<async:fragment data-provider="D3" var="d3">
|
||||
<async-fragment data-provider="D3" var="d3">
|
||||
5
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
6
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
7
|
||||
<async:fragment data-provider="D4" var="d4">
|
||||
<async-fragment data-provider="D4" var="d4">
|
||||
8
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
9
|
||||
@ -1,33 +1,33 @@
|
||||
1
|
||||
<async:fragment dependency="D1">
|
||||
<async-fragment data-provider="D1">
|
||||
2
|
||||
<async:fragment dependency="D2" var="d2">
|
||||
<async-fragment data-provider="D2" var="d2">
|
||||
3
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
4
|
||||
<async:fragment dependency="D3" var="d3">
|
||||
<async-fragment data-provider="D3" var="d3">
|
||||
5
|
||||
<async:fragment dependency="D4" var="d4">
|
||||
<async-fragment data-provider="D4" var="d4">
|
||||
6
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
7
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
8
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
9
|
||||
<async:fragment dependency="D5" var="d5">
|
||||
<async-fragment data-provider="D5" var="d5">
|
||||
10
|
||||
<async:fragment dependency="D6" var="d6">
|
||||
<async-fragment data-provider="D6" var="d6">
|
||||
11
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
12
|
||||
<async:fragment dependency="D7" var="d7">
|
||||
<async-fragment data-provider="D7" var="d7">
|
||||
13
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
14
|
||||
<async:fragment dependency="D7" var="d7">
|
||||
<async-fragment data-provider="D7" var="d7">
|
||||
15
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
16
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
17
|
||||
@ -1,3 +1,3 @@
|
||||
<async:fragment dependency="promiseData" var="promiseData">
|
||||
<async-fragment data-provider="promiseData" var="promiseData">
|
||||
$promiseData
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
@ -1,21 +1,21 @@
|
||||
A
|
||||
<async:fragment dependency="user" var="user">
|
||||
<async-fragment data-provider="user" var="user">
|
||||
asyncB1
|
||||
Hello $user.name!
|
||||
<async:fragment dependency="messages" arg-user="${user}" var="messages">
|
||||
<async-fragment data-provider="messages" arg-user="${user}" var="messages">
|
||||
You have $messages.length new messages. Messages:
|
||||
<ul>
|
||||
<li c:for="message in messages">$message.text</li>
|
||||
<li c-for="message in messages">$message.text</li>
|
||||
</ul>
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
asyncB2
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
B
|
||||
<async:fragment dependency="todos" var="todos">
|
||||
<async-fragment data-provider="todos" var="todos">
|
||||
asyncA1
|
||||
<ul>
|
||||
<li c:for="todo in todos">$todo.text (status: $todo.status)</li>
|
||||
<li c-for="todo in todos">$todo.text (status: $todo.status)</li>
|
||||
</ul>
|
||||
asyncA2
|
||||
</async:fragment>
|
||||
</async-fragment>
|
||||
C
|
||||
@ -1,5 +1,5 @@
|
||||
<c:var name="myAttrs" value="data.myAttrs"/>
|
||||
<c-var name="myAttrs" value="data.myAttrs"/>
|
||||
|
||||
<div c:attrs="myAttrs" data-encoding=""hello"">
|
||||
<div c-attrs="myAttrs" data-encoding=""hello"">
|
||||
Hello World!
|
||||
</div>
|
||||
@ -1,3 +1,3 @@
|
||||
<c:var name="helper" value="data.helper"/>
|
||||
<c-var name="helper" value="data.helper"/>
|
||||
|
||||
A<c:invoke function="helper.beginAsync(context)"/>C
|
||||
A<c-invoke function="helper.beginAsync(context)"/>C
|
||||
@ -1,15 +1,15 @@
|
||||
<c:var name="count" value="0"/>
|
||||
<c-var name="count" value="0"/>
|
||||
|
||||
<c:def function="foo(cacheName, cacheKey)">
|
||||
<caching:cached-fragment cache-key="$cacheKey" cache-name="$cacheName">
|
||||
<c-def function="foo(cacheName, cacheKey)">
|
||||
<cached-fragment cache-key="$cacheKey" cache-name="$cacheName">
|
||||
Count: ${count++}
|
||||
</caching:cached-fragment>
|
||||
</c:def>
|
||||
</cached-fragment>
|
||||
</c-def>
|
||||
|
||||
<c:invoke function="foo('cacheA', 'keyA')"/>
|
||||
<c:invoke function="foo('cacheA', 'keyA')"/>
|
||||
<c:invoke function="foo('cacheA', 'keyB')"/>
|
||||
<c-invoke function="foo('cacheA', 'keyA')"/>
|
||||
<c-invoke function="foo('cacheA', 'keyA')"/>
|
||||
<c-invoke function="foo('cacheA', 'keyB')"/>
|
||||
|
||||
<c:invoke function="foo('cacheB', 'keyA')"/>
|
||||
<c:invoke function="foo('cacheB', 'keyA')"/>
|
||||
<c:invoke function="foo('cacheB', 'keyB')"/>
|
||||
<c-invoke function="foo('cacheB', 'keyA')"/>
|
||||
<c-invoke function="foo('cacheB', 'keyA')"/>
|
||||
<c-invoke function="foo('cacheB', 'keyB')"/>
|
||||
@ -1,14 +1,14 @@
|
||||
<div id="one">
|
||||
<c:choose>
|
||||
<div c:when="false">A</div>
|
||||
<div c:when="false">B</div>
|
||||
<div c:otherwise="">TRUE</div>
|
||||
</c:choose>
|
||||
<c-choose>
|
||||
<div c-when="false">A</div>
|
||||
<div c-when="false">B</div>
|
||||
<div c-otherwise="">TRUE</div>
|
||||
</c-choose>
|
||||
</div>
|
||||
<div id="two">
|
||||
<c:choose>
|
||||
<div c:when="false">A</div>
|
||||
<div c:when="true">TRUE</div>
|
||||
<div c:otherwise="">C</div>
|
||||
</c:choose>
|
||||
<c-choose>
|
||||
<div c-when="false">A</div>
|
||||
<div c-when="true">TRUE</div>
|
||||
<div c-otherwise="">C</div>
|
||||
</c-choose>
|
||||
</div>
|
||||
@ -1,12 +1,12 @@
|
||||
<c:choose>
|
||||
<c:when test="0">A</c:when>
|
||||
<c:when test="false">B</c:when>
|
||||
<c:when test="true">TRUE</c:when>
|
||||
<c:otherwise>C</c:otherwise>
|
||||
</c:choose>,
|
||||
<c-choose>
|
||||
<c-when test="0">A</c-when>
|
||||
<c-when test="false">B</c-when>
|
||||
<c-when test="true">TRUE</c-when>
|
||||
<c-otherwise>C</c-otherwise>
|
||||
</c-choose>,
|
||||
|
||||
<c:choose>
|
||||
<c:when test="false">A</c:when>
|
||||
<c:when test="false">B</c:when>
|
||||
<c:otherwise>TRUE</c:otherwise>
|
||||
</c:choose>
|
||||
<c-choose>
|
||||
<c-when test="false">A</c-when>
|
||||
<c-when test="false">B</c-when>
|
||||
<c-otherwise>TRUE</c-otherwise>
|
||||
</c-choose>
|
||||
@ -1 +1 @@
|
||||
<div c:content="'Hello'"><b>This content will be replaced with the text "Hello"</b></div>
|
||||
<div c-content="'Hello'"><b>This content will be replaced with the text "Hello"</b></div>
|
||||
@ -2,15 +2,15 @@
|
||||
<div>${bind:data.person.name}</div>
|
||||
<div>${bind:data.person.name; watch=data; event=update}</div>
|
||||
|
||||
<div><span bind:content="data.person.name"></span></div>
|
||||
<div><span bind:content="data.person.name; watch=data; event=update"></span></div>
|
||||
<div><span bind-content="data.person.name"></span></div>
|
||||
<div><span bind-content="data.person.name; watch=data; event=update"></span></div>
|
||||
|
||||
<div bind:class="data.divClass">
|
||||
<div bind-class="data.divClass">
|
||||
</div>
|
||||
|
||||
<div bind:attrs="data.divAttrs">
|
||||
<div bind-attrs="data.divAttrs">
|
||||
</div>
|
||||
|
||||
<div bind:fragment="watch=data; event=update">
|
||||
<div bind-fragment="watch=data; event=update">
|
||||
|
||||
</div>
|
||||
@ -1,12 +1,12 @@
|
||||
<c:def function="greeting(name)">
|
||||
<c-def function="greeting(name)">
|
||||
<p class="greeting">Hello, ${name}!</p>
|
||||
</c:def>
|
||||
</c-def>
|
||||
|
||||
${greeting("World")},
|
||||
${greeting("Frank")}
|
||||
|
||||
|
||||
<c:def function="section(url, title, body)" body-param="body">
|
||||
<c-def function="section(url, title, body)" body-param="body">
|
||||
<div class="section">
|
||||
<h1>
|
||||
<a href="$url">
|
||||
@ -17,10 +17,10 @@ ${greeting("Frank")}
|
||||
${body}
|
||||
</p>
|
||||
</div>
|
||||
</c:def>
|
||||
</c-def>
|
||||
|
||||
<c:invoke function="section" url="http://www.ebay.com/" title="ebay">
|
||||
<c-invoke function="section" url="http://www.ebay.com/" title="ebay">
|
||||
<i>
|
||||
Visit eBay
|
||||
</i>
|
||||
</c:invoke>
|
||||
</c-invoke>
|
||||
@ -1,2 +1,2 @@
|
||||
<test:default-attributes prop1="foo" prop2="50"/>
|
||||
<test:default-attributes prop1="foo" prop2="50" default1="bar"/>
|
||||
<test-default-attributes prop1="foo" prop2="50"/>
|
||||
<test-default-attributes prop1="foo" prop2="50" default1="bar"/>
|
||||
@ -1,6 +1,6 @@
|
||||
<html:doctype value="HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd""/>
|
||||
<html-doctype value="HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd""/>
|
||||
|
||||
<html html:doctype="html">
|
||||
<html html-doctype="html">
|
||||
<head>
|
||||
<title>DOCTYPE Test</title>
|
||||
</head>
|
||||
|
||||
@ -1 +1 @@
|
||||
<test:dynamic-attributes test="Hello" class="my-class" id="myId"/>
|
||||
<test-dynamic-attributes test="Hello" class="my-class" id="myId"/>
|
||||
@ -1 +1 @@
|
||||
<test:dynamic-attributes2 test="World" class="my-class" id="myId"/>
|
||||
<test-dynamic-attributes2 test="World" class="my-class" id="myId"/>
|
||||
@ -1 +1 @@
|
||||
<test:dynamic-attributes3 test="World" class="my-class" id="myId"/>
|
||||
<test-dynamic-attributes3 test="World" class="my-class" id="myId"/>
|
||||
@ -1,6 +1,6 @@
|
||||
<div data-attr="Hello <John> <hello>">
|
||||
<c:attr name="data-nested-attr">Hello <John> <![CDATA[<hello>]]></c:attr>
|
||||
<c:attr name="data-nested-attr2" value="Hello <John> <hello>"/>
|
||||
<c-attr name="data-nested-attr">Hello <John> <![CDATA[<hello>]]></c-attr>
|
||||
<c-attr name="data-nested-attr2" value="Hello <John> <hello>"/>
|
||||
Hello <John>© <![CDATA[<hello>]]>
|
||||
${startTag:START}
|
||||
</div>
|
||||
|
||||
@ -8,78 +8,78 @@ ${greeting("World")
|
||||
|
||||
|
||||
|
||||
<c:for>
|
||||
<c-for>
|
||||
Missing each attribute
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
<c:for each="item">
|
||||
</c:for>
|
||||
<c-for each="item">
|
||||
</c-for>
|
||||
|
||||
<c:for each="item in items" invalid="true">
|
||||
<c-for each="item in items" invalid="true">
|
||||
Invalid attribute
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
<c:for each="item in items" separator="${;">
|
||||
<c-for each="item in items" separator="${;">
|
||||
Invalid separator
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
<c:invalidTag>
|
||||
</c:invalidTag>
|
||||
<c-invalidTag>
|
||||
</c-invalidTag>
|
||||
|
||||
<div c:for="item in items; invalid=true">
|
||||
<div c-for="item in items; invalid=true">
|
||||
</div>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="true">A</c:when>
|
||||
<c:otherwise>INVALID</c:otherwise>
|
||||
<c:when test="true">B</c:when>
|
||||
</c:choose>
|
||||
<c-choose>
|
||||
<c-when test="true">A</c-when>
|
||||
<c-otherwise>INVALID</c-otherwise>
|
||||
<c-when test="true">B</c-when>
|
||||
</c-choose>
|
||||
|
||||
<c:choose>
|
||||
<c-choose>
|
||||
|
||||
<c:when test="false">A</c:when>
|
||||
<c-when test="false">A</c-when>
|
||||
INVALID TEXT
|
||||
<c:when test="true">TRUE</c:when>
|
||||
<c:otherwise>C</c:otherwise>
|
||||
</c:choose>
|
||||
<c-when test="true">TRUE</c-when>
|
||||
<c-otherwise>C</c-otherwise>
|
||||
</c-choose>
|
||||
|
||||
<c:def>
|
||||
</c:def>
|
||||
<c-def>
|
||||
</c-def>
|
||||
|
||||
<c:def function="invalid-function-name()">
|
||||
<c:invalidTag2></c:invalidTag2>
|
||||
</c:def>
|
||||
<c-def function="invalid-function-name()">
|
||||
<c-invalidTag2></c-invalidTag2>
|
||||
</c-def>
|
||||
|
||||
<c:include>Missing template attribute</c:include>
|
||||
<c-include>Missing template attribute</c-include>
|
||||
|
||||
<c:with vars="x=1;b=2;1">
|
||||
</c:with>
|
||||
<c-with vars="x=1;b=2;1">
|
||||
</c-with>
|
||||
|
||||
<c:if test="false">
|
||||
<c-if test="false">
|
||||
A
|
||||
</c:if>
|
||||
</c-if>
|
||||
INVALID
|
||||
<c:else>
|
||||
<c-else>
|
||||
C
|
||||
</c:else>
|
||||
</c-else>
|
||||
|
||||
<c:if test="false">
|
||||
<c-if test="false">
|
||||
A
|
||||
</c:if>
|
||||
</c-if>
|
||||
INVALID
|
||||
<c:else-if test="false">
|
||||
<c-else-if test="false">
|
||||
A
|
||||
</c:else-if>
|
||||
<c:else>
|
||||
</c-else-if>
|
||||
<c-else>
|
||||
C
|
||||
</c:else>
|
||||
</c-else>
|
||||
|
||||
<div class="test">
|
||||
<c:attr name="class" value="duplicate"/>
|
||||
<c-attr name="class" value="duplicate"/>
|
||||
</div>
|
||||
|
||||
<test:popover title="Popover Title" invalidAttr1="invalidValue1">
|
||||
<c:attr name="invalidAttr2" value="invalidValue2"/>
|
||||
<c:attr name="invalidAttr3">invalidValue3</c:attr>
|
||||
<c:attr name="invalidAttr4" c:if="invalidIf">invalidValue4</c:attr>
|
||||
</test:popover>
|
||||
<test-popover title="Popover Title" invalidAttr1="invalidValue1">
|
||||
<c-attr name="invalidAttr2" value="invalidValue2"/>
|
||||
<c-attr name="invalidAttr3">invalidValue3</c-attr>
|
||||
<c-attr name="invalidAttr4" c-if="invalidIf">invalidValue4</c-attr>
|
||||
</test-popover>
|
||||
@ -1,4 +1,4 @@
|
||||
<div c:escape-xml="false">
|
||||
<div c-escape-xml="false">
|
||||
< > &
|
||||
<div>< > &</div>
|
||||
</div>
|
||||
@ -1,4 +1,4 @@
|
||||
<div c:escape-xml="true">
|
||||
<div c-escape-xml="true">
|
||||
< > &
|
||||
<div>< > &</div>
|
||||
</div>
|
||||
@ -1,33 +1,33 @@
|
||||
<c:if test="true">
|
||||
<c-if test="true">
|
||||
A
|
||||
</c:if>
|
||||
<c:else>
|
||||
</c-if>
|
||||
<c-else>
|
||||
B
|
||||
</c:else>
|
||||
</c-else>
|
||||
,
|
||||
<c:if test="false">
|
||||
<c-if test="false">
|
||||
A
|
||||
</c:if>
|
||||
<c:else>
|
||||
</c-if>
|
||||
<c-else>
|
||||
B
|
||||
</c:else>
|
||||
</c-else>
|
||||
,
|
||||
<c:if test="false">
|
||||
<c-if test="false">
|
||||
A
|
||||
</c:if>
|
||||
<c:else-if test="false">
|
||||
</c-if>
|
||||
<c-else-if test="false">
|
||||
B
|
||||
</c:else-if>
|
||||
<c:else>
|
||||
</c-else-if>
|
||||
<c-else>
|
||||
C
|
||||
</c:else>
|
||||
</c-else>
|
||||
,
|
||||
<div c:if="false">
|
||||
<div c-if="false">
|
||||
A
|
||||
</div>
|
||||
<div c:else-if="false">
|
||||
<div c-else-if="false">
|
||||
B
|
||||
</div>
|
||||
<div c:else>
|
||||
<div c-else>
|
||||
C
|
||||
</div>
|
||||
@ -1,6 +1,6 @@
|
||||
<c:var name="name" value="data.name"/>
|
||||
<c:var name="count" value="data.count"/>
|
||||
<c:var name="invokeBody" value="data.invokeBody"/>
|
||||
<c-var name="name" value="data.name"/>
|
||||
<c-var name="count" value="data.count"/>
|
||||
<c-var name="invokeBody" value="data.invokeBody"/>
|
||||
|
||||
<div class="nested">
|
||||
<h1>
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
BEGIN
|
||||
<c:include resource="include-resource-target.txt" static="true"/>
|
||||
<c-include resource="include-resource-target.txt" static="true"/>
|
||||
END
|
||||
@ -1,4 +1,4 @@
|
||||
<c:var name="name" value="data.name"/>
|
||||
<c:var name="count" value="data.count"/>
|
||||
<c-var name="name" value="data.name"/>
|
||||
<c-var name="count" value="data.count"/>
|
||||
|
||||
Hello $name! You have $count new messages.
|
||||
@ -1,10 +1,10 @@
|
||||
<c:include template="./include-target.rhtml" name="${'Frank'}" count="20"/>
|
||||
<c:include template="./include-target.rhtml" name="Frank" count="${20}"/>
|
||||
<c:include template="./include-target.rhtml" template-data="{name: 'Frank', count: 20}"/>
|
||||
<c:include template="./include-nested-content.rhtml" name="Frank" count="${20}">
|
||||
<c-include template="./include-target.rhtml" name="${'Frank'}" count="20"/>
|
||||
<c-include template="./include-target.rhtml" name="Frank" count="${20}"/>
|
||||
<c-include template="./include-target.rhtml" template-data="{name: 'Frank', count: 20}"/>
|
||||
<c-include template="./include-nested-content.rhtml" name="Frank" count="${20}">
|
||||
Have a
|
||||
<b>
|
||||
wonderful
|
||||
</b>
|
||||
day!
|
||||
</c:include>
|
||||
</c-include>
|
||||
@ -1,4 +1,4 @@
|
||||
<c:var name="name" value="data.name"/>
|
||||
<c-var name="name" value="data.name"/>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -12,16 +12,16 @@
|
||||
A
|
||||
|
||||
<p>
|
||||
<c:invoke function="test('World')"/>
|
||||
<c:write value="test2('World')"/>
|
||||
<c-invoke function="test('World')"/>
|
||||
<c-write value="test2('World')"/>
|
||||
</p>
|
||||
|
||||
B
|
||||
|
||||
<p>
|
||||
<c:def function="greeting(name, count)">
|
||||
<c-def function="greeting(name, count)">
|
||||
Hello ${name}! You have ${count} new messages.
|
||||
</c:def>
|
||||
<c:invoke function="greeting" name="Frank" count="${10}"/>
|
||||
<c:invoke function="greeting('John', 20)"/>
|
||||
</c-def>
|
||||
<c-invoke function="greeting" name="Frank" count="${10}"/>
|
||||
<c-invoke function="greeting('John', 20)"/>
|
||||
</p>
|
||||
@ -1,10 +1,10 @@
|
||||
<h1 c:if="data.showHeader !== false">
|
||||
<layout:placeholder name="header">
|
||||
<h1 c-if="data.showHeader !== false">
|
||||
<layout-placeholder name="header">
|
||||
DEFAULT TITLE
|
||||
</layout:placeholder>
|
||||
</layout-placeholder>
|
||||
</h1>
|
||||
<div>
|
||||
<layout:placeholder name="body"/>
|
||||
<layout-placeholder name="body"/>
|
||||
</div>
|
||||
<layout:placeholder name="footer"/>
|
||||
<layout:placeholder name="empty"/>
|
||||
<layout-placeholder name="footer"/>
|
||||
<layout-placeholder name="empty"/>
|
||||
@ -1,18 +1,18 @@
|
||||
<layout:use template="./layout-default.rhtml" show-header="$false">
|
||||
<layout:put into="body">BODY CONTENT</layout:put>
|
||||
<layout:put into="footer">FOOTER CONTENT</layout:put>
|
||||
</layout:use>
|
||||
<layout:use template="./layout-default.rhtml" show-header="$true">
|
||||
<layout:put into="header">HEADER CONTENT</layout:put>
|
||||
<layout:put into="body">BODY CONTENT</layout:put>
|
||||
<layout:put into="footer">FOOTER CONTENT</layout:put>
|
||||
</layout:use>
|
||||
<layout:use template="./layout-default.rhtml" show-header="$true">
|
||||
<layout:put into="header" value="VALUE HEADER"/>
|
||||
<layout:put into="body">BODY CONTENT</layout:put>
|
||||
<layout:put into="footer">FOOTER CONTENT</layout:put>
|
||||
</layout:use>
|
||||
<layout:use template="${require.resolve('./layout-default.rhtml')}" show-header="$true">
|
||||
<layout:put into="body">BODY CONTENT</layout:put>
|
||||
<layout:put into="footer">FOOTER CONTENT</layout:put>
|
||||
</layout:use>
|
||||
<layout-use template="./layout-default.rhtml" show-header="$false">
|
||||
<layout-put into="body">BODY CONTENT</layout-put>
|
||||
<layout-put into="footer">FOOTER CONTENT</layout-put>
|
||||
</layout-use>
|
||||
<layout-use template="./layout-default.rhtml" show-header="$true">
|
||||
<layout-put into="header">HEADER CONTENT</layout-put>
|
||||
<layout-put into="body">BODY CONTENT</layout-put>
|
||||
<layout-put into="footer">FOOTER CONTENT</layout-put>
|
||||
</layout-use>
|
||||
<layout-use template="./layout-default.rhtml" show-header="$true">
|
||||
<layout-put into="header" value="VALUE HEADER"/>
|
||||
<layout-put into="body">BODY CONTENT</layout-put>
|
||||
<layout-put into="footer">FOOTER CONTENT</layout-put>
|
||||
</layout-use>
|
||||
<layout-use template="${require.resolve('./layout-default.rhtml')}" show-header="$true">
|
||||
<layout-put into="body">BODY CONTENT</layout-put>
|
||||
<layout-put into="footer">FOOTER CONTENT</layout-put>
|
||||
</layout-use>
|
||||
@ -1,17 +1,17 @@
|
||||
<c:for each="item in ['a', 'b', 'c']" iterator="data.reverseIterator">
|
||||
<c-for each="item in ['a', 'b', 'c']" iterator="data.reverseIterator">
|
||||
${item}
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
|
||||
<c:for each="item in ['a', 'b', 'c']" iterator="data.reverseIterator" status-var="status">
|
||||
<c-for each="item in ['a', 'b', 'c']" iterator="data.reverseIterator" status-var="status">
|
||||
${status.index}-${item}
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
<div c:for="item in ['a', 'b', 'c']; iterator=data.reverseIterator">
|
||||
<div c-for="item in ['a', 'b', 'c']; iterator=data.reverseIterator">
|
||||
${item}
|
||||
</div>
|
||||
|
||||
|
||||
<div c:for="item in ['a', 'b', 'c']; iterator=data.reverseIterator; status-var=status">
|
||||
<div c-for="item in ['a', 'b', 'c']; iterator=data.reverseIterator; status-var=status">
|
||||
${status.index}-${item}
|
||||
</div>
|
||||
@ -1,9 +1,9 @@
|
||||
<c:for each="(name,value) in {'foo': 'low', 'bar': 'high'}">
|
||||
<c-for each="(name,value) in {'foo': 'low', 'bar': 'high'}">
|
||||
[$name=$value]
|
||||
</c:for>
|
||||
</c-for>
|
||||
|
||||
<ul>
|
||||
<li c:for="(name, value) in {'foo': 'low', 'bar': 'high'}">
|
||||
<li c-for="(name, value) in {'foo': 'low', 'bar': 'high'}">
|
||||
[$name=$value]
|
||||
</li>
|
||||
</ul>
|
||||
@ -1,9 +1,9 @@
|
||||
<c:for each="item in ['a', 'b', 'c']">
|
||||
<c-for each="item in ['a', 'b', 'c']">
|
||||
${item}
|
||||
</c:for>
|
||||
<c:for each="item in ['a', 'b', 'c']" separator=", " status-var="loop">
|
||||
</c-for>
|
||||
<c-for each="item in ['a', 'b', 'c']" separator=", " status-var="loop">
|
||||
${item} - ${loop.isFirst()} - ${loop.isLast()} - ${loop.getIndex()} - ${loop.getLength()}
|
||||
</c:for>
|
||||
<div c:for="item in ['red', 'green', 'blue']; separator = ', '; status-var=loop" >
|
||||
</c-for>
|
||||
<div c-for="item in ['red', 'green', 'blue']; separator = ', '; status-var=loop" >
|
||||
${item} - ${loop.isFirst()} - ${loop.isLast()} - ${loop.getIndex()} - ${loop.getLength()}
|
||||
</div>
|
||||
@ -1,19 +1,19 @@
|
||||
<c:var name="active" value="data.active"/>
|
||||
<c-var name="active" value="data.active"/>
|
||||
|
||||
<test:popover>
|
||||
<c:attr name="title">Popover Title</c:attr>
|
||||
<c:attr name="content">Popover Content</c:attr>
|
||||
<test-popover>
|
||||
<c-attr name="title">Popover Title</c-attr>
|
||||
<c-attr name="content">Popover Content</c-attr>
|
||||
|
||||
Link Text
|
||||
</test:popover>
|
||||
</test-popover>
|
||||
|
||||
<div>
|
||||
<c:attr name="class" value="{?active;tab-active}"/>
|
||||
<c:attr name="align">center</c:attr>
|
||||
<c-attr name="class" value="{?active;tab-active}"/>
|
||||
<c-attr name="align">center</c-attr>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<c:attr name="title">
|
||||
<c:for each="color in ['red', 'green', 'blue']"> $color! </c:for>
|
||||
</c:attr>
|
||||
<c-attr name="title">
|
||||
<c-for each="color in ['red', 'green', 'blue']"> $color! </c-for>
|
||||
</c-attr>
|
||||
</div>
|
||||
@ -1,15 +1,15 @@
|
||||
<c:var name="showConditionalTab" value="data.showConditionalTab"/>
|
||||
<c-var name="showConditionalTab" value="data.showConditionalTab"/>
|
||||
|
||||
<test:tabs>
|
||||
<test:tab title="Tab 1">
|
||||
<test-tabs>
|
||||
<test-tab title="Tab 1">
|
||||
Tab 1 content
|
||||
</test:tab>
|
||||
</test-tab>
|
||||
|
||||
<test:tab title="Tab 2">
|
||||
<test-tab title="Tab 2">
|
||||
Tab 2 content
|
||||
</test:tab>
|
||||
</test-tab>
|
||||
|
||||
<test:tab title="Tab 3" c:if="showConditionalTab">
|
||||
<test-tab title="Tab 3" c-if="showConditionalTab">
|
||||
Tab 3 content
|
||||
</test:tab>
|
||||
</test:tabs>
|
||||
</test-tab>
|
||||
</test-tabs>
|
||||
@ -1,15 +1,15 @@
|
||||
<optimizer:page name="optimizer-dynamic">
|
||||
<optimizer-page name="optimizer-dynamic">
|
||||
<dependencies>
|
||||
<module name="${data.dependency}"/>
|
||||
<module name="test/optimizer/mixedB" c:if="data.mixedBEnabled"/>
|
||||
<module name="test/optimizer/mixedB" c-if="data.mixedBEnabled"/>
|
||||
</dependencies>
|
||||
</optimizer:page>
|
||||
</optimizer-page>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<optimizer:slot name="head"/>
|
||||
<optimizer-slot name="head"/>
|
||||
</head>
|
||||
<body>
|
||||
<optimizer:slot name="body"/>
|
||||
<optimizer-slot name="body"/>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,15 +1,15 @@
|
||||
<optimizer:page name="optimizer">
|
||||
<optimizer-page name="optimizer">
|
||||
<dependencies>
|
||||
<module name="test/optimizer/mixedA"/>
|
||||
<module name="test/optimizer/mixedB"/>
|
||||
</dependencies>
|
||||
</optimizer:page>
|
||||
</optimizer-page>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<optimizer:slot name="head"/>
|
||||
<optimizer-slot name="head"/>
|
||||
</head>
|
||||
<body>
|
||||
<optimizer:slot name="body"/>
|
||||
<optimizer-slot name="body"/>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,4 +1,4 @@
|
||||
<c:var name="message" value="data.message"/>
|
||||
<c-var name="message" value="data.message"/>
|
||||
|
||||
<div c:replace="'Hello'"><b>This content and parent DIV will be replaced with the text "Hello"</b></div>,
|
||||
<div c:replace="message"><b>This content and parent DIV will be replaced with the value of the "message" variable</b></div>
|
||||
<div c-replace="'Hello'"><b>This content and parent DIV will be replaced with the text "Hello"</b></div>,
|
||||
<div c-replace="message"><b>This content and parent DIV will be replaced with the value of the "message" variable</b></div>
|
||||
@ -1,3 +1,3 @@
|
||||
<c:require module="./test-helpers" var="testHelpers" />
|
||||
<c-require module="./test-helpers" var="testHelpers" />
|
||||
Hello ${testHelpers.upperCase("world")}!
|
||||
Hello ${testHelpers.trim(" World ")}!
|
||||
@ -1,5 +1,5 @@
|
||||
<c:var name="name" value="data.name"/>
|
||||
<c:var name="count" value="data.count"/>
|
||||
<c-var name="name" value="data.name"/>
|
||||
<c-var name="count" value="data.count"/>
|
||||
|
||||
<div class="{?count>50;over-50}"></div>
|
||||
<div class="{?count lt 50;under-50}"></div>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<c:var name="dynamic" value="data.dynamic"/>
|
||||
<c-var name="dynamic" value="data.dynamic"/>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<test:simpleHello name="world"/>
|
||||
<test-simpleHello name="world"/>
|
||||
</li>
|
||||
<li>
|
||||
<test:simpleHello name="${dynamic}" adult="true"/>
|
||||
<test-simpleHello name="${dynamic}" adult="true"/>
|
||||
</li>
|
||||
<li>
|
||||
<test:simpleHello name="Dynamic: ${dynamic}" adult="false"/>
|
||||
<test-simpleHello name="Dynamic: ${dynamic}" adult="false"/>
|
||||
</li>
|
||||
</ul>
|
||||
@ -1,13 +1,13 @@
|
||||
<c:var name="rootClass" value="data.rootClass"/>
|
||||
<c:var name="colors" value="data.colors"/>
|
||||
<c:var name="message" value="data.message"/>
|
||||
<c-var name="rootClass" value="data.rootClass"/>
|
||||
<c-var name="colors" value="data.colors"/>
|
||||
<c-var name="message" value="data.message"/>
|
||||
|
||||
<div class="hello-world ${rootClass}">${message}</div>
|
||||
|
||||
<ul c:if="notEmpty(colors)">
|
||||
<li class="color" c:for="color in colors">${color}</li>
|
||||
<ul c-if="notEmpty(colors)">
|
||||
<li class="color" c-for="color in colors">${color}</li>
|
||||
</ul>
|
||||
|
||||
<div c:if="empty(colors)">
|
||||
<div c-if="empty(colors)">
|
||||
No colors!
|
||||
</div>
|
||||
@ -1,4 +1,4 @@
|
||||
<c:var name="name" value="data.name"/>
|
||||
<c:var name="count" value="data.count"/>
|
||||
<c-var name="name" value="data.name"/>
|
||||
<c-var name="count" value="data.count"/>
|
||||
|
||||
${name != null ? "Hello ${name.toUpperCase()}! You have $count new messages." : null}
|
||||
@ -1,13 +1,13 @@
|
||||
<div>
|
||||
<span c:strip="true"><b>A</b></span>
|
||||
<span c-strip="true"><b>A</b></span>
|
||||
</div>
|
||||
<div>
|
||||
<span c:strip="false"><b>B</b></span>
|
||||
<span c-strip="false"><b>B</b></span>
|
||||
</div>
|
||||
<div>
|
||||
<span c:strip="1 === 1"><b>c</b></span>
|
||||
<span c-strip="1 === 1"><b>c</b></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span c:strip="1 === 2"><b>d</b></span>
|
||||
<span c-strip="1 === 2"><b>d</b></span>
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
</script>
|
||||
</head>
|
||||
<body id="body"
|
||||
style="position:absolute; z-index:0; border:1px solid black; left:5%; top:5%; width:90%; height:90%;">
|
||||
style="position:absolute; z-index-0; border-1px solid black; left-5%; top-5%; width-90%; height-90%;">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>HTML Form</legend>
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
|
||||
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice"
|
||||
style="width:100%; height:100%; position:absolute; top:0; left:0; z-index:-1;">
|
||||
style="width:100%; height-100%; position-absolute; top-0; left-0; z-index--1;">
|
||||
<linearGradient id="gradient">
|
||||
<stop class="begin" offset="0%"/>
|
||||
<stop class="end" offset="100%"/>
|
||||
|
||||
@ -1 +1 @@
|
||||
<test:simpleHello c:input="data"/>
|
||||
<test-simpleHello c-input="data"/>
|
||||
@ -1,3 +1,3 @@
|
||||
<div>
|
||||
<test:page-title title="My Page Title"/>
|
||||
<test-page-title title="My Page Title"/>
|
||||
</div>
|
||||
@ -1,3 +1,3 @@
|
||||
<c:var name="person" value="data.person"/>
|
||||
<c-var name="person" value="data.person"/>
|
||||
|
||||
Hello $person.name. You are from ${person.address.city}, $person.address.state
|
||||
@ -1,11 +1,11 @@
|
||||
<c:var name="colors" value="['red', 'green', 'blue']"/>
|
||||
<c-var name="colors" value="['red', 'green', 'blue']"/>
|
||||
|
||||
<div c:for="color in colors">
|
||||
<div c-for="color in colors">
|
||||
$color
|
||||
</div>
|
||||
|
||||
<c:assign var="colors" value="['orange', 'purple', 'yellow']"/>
|
||||
<c-assign var="colors" value="['orange', 'purple', 'yellow']"/>
|
||||
|
||||
<div c:for="color in colors">
|
||||
<div c-for="color in colors">
|
||||
$color
|
||||
</div>
|
||||
@ -22,14 +22,14 @@ should <!-- This whitespace should be normalized --> retain spacing between l
|
||||
begin <!-- this whitespace should not be normalized --> end
|
||||
</pre>
|
||||
|
||||
<div c:space="preserve">
|
||||
<div c-space="preserve">
|
||||
begin <!-- this whitespace should not be normalized --> end
|
||||
</div>
|
||||
|
||||
<div xml:space="preserve">
|
||||
<div c-space="preserve">
|
||||
begin <!-- this whitespace should not be normalized --> end
|
||||
</div>
|
||||
<c:if test="true">begin <!-- this whitespace should be preserved -->end</c:if>
|
||||
<c-if test="true">begin <!-- this whitespace should be preserved -->end</c-if>
|
||||
<!--
|
||||
- In not "xml:space" === "preserve":
|
||||
- newline followed by whitespace should be removed
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user