Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

etpl

Package Overview
Dependencies
Maintainers
2
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

etpl - npm Package Compare versions

Comparing version 2.1.0-rc to 3.0.0

.editorconfig

6

package.json
{
"name": "etpl",
"version": "2.1.0-rc",
"version": "3.0.0",
"contributors": [

@@ -12,3 +12,3 @@ { "name": "erik", "email": "errorrik@gmail.com" },

"repository": "git://github.com/ecomfe/etpl",
"description": "ETPL是一个灵活、具有强大复用能力的高性能Javascript模板引擎,适用于WEB前端应用中视图的生成,特别是SPA(Single Page APP)类型的应用。",
"description": "ETPL是一个强复用、灵活、高性能的JavaScript模板引擎,适用于浏览器端或Node环境中视图的生成。",
"scripts": {

@@ -18,4 +18,4 @@ "test": "jasmine-node test/spec"

"devDependencies": {
"jasmine-node": "~1.14.2"
"jasmine-node": "1.14.2"
}
}

@@ -5,653 +5,99 @@ # ETPL (Enterprise Template)

ETPL是一个灵活、具有强大复用能力的高性能的模板引擎,适用于WEB前端应用中视图的生成,特别是SPA(Single Page APP)类型的应用。
ETPL是一个强复用、灵活、高性能的JavaScript模板引擎,适用于浏览器端或Node环境中视图的生成。
- [开始](#start)
- [了解ETPL的语法](#syntax)
- [基础](#基础)
- [语法形式](#语法形式)
- [自动结束](#自动结束)
- [target](#target)
- [变量声明](#变量声明)
- [变量替换](#变量替换)
- [内容块过滤](#内容块过滤)
- [模板复用](#模板复用)
- [import](#import)
- [母版](#母版)
- [use](#use)
- [分支与循环](#分支与循环)
- [if](#if)
- [for](#for)
- [浏览ETPL的API](#api)
- [methods](#methods)
- [classes](#classes)
- [与ER中模板引擎的兼容性](#compatibility)
## Start
ETPL可以在`CommonJS/AMD`的模块定义环境中使用,也可以直接在页面下通过`<script src=`的方式引用。`CommonJS/AMD`环境下需要通过如下代码得到ETPL的模块。
ETpl可以在`CommonJS/AMD`的模块定义环境中使用,也能直接在页面下通过`script`标签引用。
```javascript
var etpl = require( 'etpl' );
```
得到ETPL模块对象后,首先对模板源代码进行编译,就能够得到模板编译后的`function`。
### 浏览器环境
```javascript
var render = etpl.compile( 'Hello ${name}!' );
```
直接通过script标签引用,你可以获得一个全局的`etpl`变量
然后执行这个`function`,传入数据对象,就能得到模板执行的结果了。
```javascript
var text = render( {name: 'etpl'} );
```
编写模板和数据前,如果对执行结果有疑虑,就去ETPL的[homepage](http://ecomfe.github.io/etpl/)试试看吧。
## Syntax
### 基础
#### 语法形式
ETPL的指令标签默认为HTML注释的形式,在指令标签内允许声明 `指令起始`、`指令结束`和`注释`。
`指令起始`的语法形式为: *<!-- command-name: command-value -->*。其中, *command-value* 的具体语法形式详情请参见各指令相关章节。
```html
<!-- target: targetName -->
<!-- if: ${number} > 0 -->
<!-- for: ${persons} as ${person}, ${index} -->
<script src="etpl.js"></script>
```
`指令结束`的语法形式为: *<!-- /command-name -->*。
在AMD环境的模块定义时,你可以通过`同步require`获得ETpl模块
```html
<!-- /if -->
<!-- /for -->
<!-- /target -->
```
`注释`的语法形式为: *<!-- //message -->*,注释指令在render时将不输出。
```html
<!-- // just add some message -->
```
如果不期望使用HTML注释形式的指令标签,可以通过config API可以配置指令标签的形式:
```javascript
etpl.config({
commandOpen: '<%',
commandClose: '%>'
define(function (require) {
var etpl = require('etpl');
});
/*
配置指令标签的形式为“<% ... %>”,然后指令标签可以像下面一样声明:
<% if: ${number} > 0 %>
greater than zero
<% /if %>
*/
```
#### 自动结束
在AMD环境,你也可以通过`异步require`获得ETpl模块
为了减少开发者的工作量,部分指令标签支持`自动结束`,模板开发者无需手工编写`指令结束`。比如:当遇见target指令起始时,ETPL自动认为上一个target已经结束。
具体指令的`自动结束`支持,请参考相应指令相关章节。
#### target
`target`是ETPL的基本单元,其含义是 **一个模版片段** 。`target`可用于render,也可用于被其他`target`所import或use。
如果仅仅编写的是一个模板片段,可以省略`target`的声明。这样的编写方式与其他模板引擎类似,ETPL将默认生成匿名target,但模板片段将不可复用(不可被import或use,不可指定母版)。
匿名target应位于模板源码起始。下面例子中,位于其他target后的模板片段`Bye`将没有任何作用。
```
<!-- use: hello(name=${name}) -->
<!-- target: hello -->
Hello ${name}!
<!-- /target -->
Bye
```
##### 语法
target的语法形式为:
target: target-name
target: target-name(master=master-name)
target声明可以为其指定相应的母版。母版功能请参考模板复用章节。
##### 自动结束
target支持自动结束,当遇见 *target* 或 *master* 时自动结束。
##### 示例
```html
<!-- target: hello -->
Hello <strong>ETPL</strong>!
<!-- target: bye -->
Bye <strong>ETPL</strong>!
```
#### 变量声明
通过`var`指令可以在模板内部声明一个变量。声明的变量在整个`target`内有效。
##### 语法
var的语法形式为:
var: var-name=expression
`expression`中可以使用静态或动态数据。
##### 示例
```html
<!-- var: age = 18 -->
<!-- var: age = ${person.age} -->
<!-- var: name = 'etpl' -->
```
##### 自动结束
var无需编写`指令结束`,其将在`指令起始`后立即自动结束。
#### 变量替换
绝大多数模板引擎都支持变量替换功能。ETPL变量替换的语法为:
${variable-name}
${variable-name|filter-name}
${variable-name|filter-name(arguments)}
${variable-name|filter1|filter2(arguments)|filter3|...}
variable-name支持`.`形式的property access。
编写模板时可以指定filter,默认使用html filter进行HTML转义。如果想要保留变量的原形式,需要手工指定使用名称为raw的filter,或者通过config API配置引擎的defaultFilter参数。
${myVariable|raw}
```javascript
etpl.config({
defaultFilter: ''
require([ 'etpl' ], function (etpl) {
});
```
ETPL默认支持3种filters,可以通过引擎的`addFilter`方法自定义扩展filter。
*在AMD环境下,请确保你的require.config配置能够让Loader找到ETpl模块*
- html: html转义
- url: url转义
- raw: 不进行任何转义
### Node.JS环境
你可以通过`npm`来安装ETpl
变量替换支持多filter处理。filter之间以类似管道的方式,前一个filter的输出做为后一个filter的输入,向后传递。
```html
${myVariable|html|url}
```
filter支持参数传递,参数可以使用动态数据。
```html
<!-- // 假设存在扩展filter: comma -->
${myVariable|comma(3)}
${myVariable|comma(${commaLength})}
${myVariable|comma(${commaLength}+1)}
$ npm install etpl
```
在变量替换中,引擎会默认将数据toString后传递给filter,以保证filter输入输出的类型一致性。如果filter期望接受的是原始数据,模板开发者需要通过前缀的`*`指定。
安装完成后,你就可以通过`require`获得一个ETpl模块,正常地使用它
```html
<!-- // 假设存在扩展filter: dateFormat -->
${*myDate|dateFormat('yyyy-MM-dd')}
```
#### 内容块过滤
除了在变量替换中可以使用filter进行处理,ETPL还可以通过`filter`指令,使用指定的filter对一个模板内容块进行过滤处理。
##### 语法
filter的语法形式为:
filter: filter-name
filter: filter-name(arguments)
##### 示例
下面的例子假定使用者实现了一个markdown的filter
```html
<!-- filter: markdown(${useExtra}, true) -->
## markdown document
This is the content, also I can use `${variables}`
<!-- /filter -->
```
##### 自动结束
filter指令不支持自动结束,必须手工编写`指令结束`。
```html
<!-- /filter -->
```
### 模板复用
ETPL支持多种形式的模板复用方式,帮助模板开发者减少模板编写的重复劳动和维护成本。
#### import
通过import指令,可以在当前位置插入指定target的源码。
##### 语法
import的语法形式为:
import: target-name
##### 示例
```html
<!-- target: hello -->
Hello <strong>${name}</strong>!
<!-- target: main -->
<div class="main"><!-- import: hello --></div>
```
##### 自动结束
import无需编写`指令结束`,其将在`指令起始`后立即自动结束。
#### 母版
通过`master`指令可以声明一个母版,母版中通过`contentplaceholder`指令声明可被替换的部分。
`target`声明时通过 **master=master-name** 指定一个母版,就可以继承于这个母版的片段,并且通过`content`指令,替换母版中`contentplaceholder`指令声明部分的内容。指定母版的target中只允许包含`content`指令声明的片段。
母版功能支持多层母版,`master`声明时也可以通过 **master=master-name** 指定一个母版。母板中的`contentplaceholder`不会再传递下去,即`contentplaceholder`只在一层有效。
##### 语法
master的语法形式为:
master: master-name
master: master-name(master=master-name)
contentplaceholder的语法形式为:
contentplaceholder: content-name
content的语法形式为:
content: content-name
##### 示例
```html
<!-- master: myMaster -->
<div class="title"><!-- contentplaceholder: title -->title<!-- /contentplaceholder --></div>
<div class="main"><!-- contentplaceholder: main --></div>
<!-- master: myMaster-has-sidebar(master=myMaster) -->
<!-- content: title -->
title for has sidebar
<!-- content: main -->
<div class="sidebar"><!-- contentplaceholder: sidebar --></div>
<div class="article"><!-- contentplaceholder: article --></div>
<!-- target: myTarget(master=myMaster) -->
<!-- content: title -->
Building WebKit from Xcode
<!-- content: main -->
<p>To build from within Xcode, you can use the WebKit workspace. </p>
<!-- target: myTarget-has-sidebar(master=myMaster-has-sidebar) -->
<!-- content: sidebar -->
<ul class="navigator">...</ul>
<!-- content: article -->
<p>To build from within Xcode, you can use the WebKit workspace. </p>
```
##### 自动结束
master支持自动结束,当遇见 *target* 或 *master* 时自动结束。
contentplaceholder支持自动结束,当遇见 *contentplaceholder* 或 *target* 或 *master* 时,在`指令标签起始`后自动结束。
content支持自动结束,当遇见 *content* 或 *target* 或 *master* 时自动结束。
#### use
通过`use`指令,可以调用指定`target`,在当前位置插入其render后的结果。允许使用静态或动态数据指定数据项。
##### 语法
use的语法形式为:
use: target-name
use: target-name(data-name=expression, data-name=expression)
##### 示例
```html
<!-- target: info -->
name: ${name}
<!-- if: ${email} -->
email: ${email}
<!-- /if -->
<!-- target: main -->
<div class="main"><!-- use: info(name=${person.name}, email=${person.email}) --></div>
```
##### 自动结束
use无需编写`指令结束`,其将在`指令起始`后立即自动结束。
### 分支与循环
#### if
ETPL提供了分支的支持,相关指令有`if`、`elif`、`else`。
##### 语法
if的语法形式为:
if: conditional-expression
elif的语法形式为:
elif: conditional-expression
else的语法形式为:
else
conditional-expression中可以使用动态数据,通过`${variable}`的形式,可以使用模板render的data。`${variable}`支持`.`的property accessor。
##### 自动结束
if指令不支持自动结束,必须手工编写指令结束`<!-- /if -->`。
##### 示例
```html
<!-- if: ${number} > 0 -->
larger than zero
<!-- elif: ${number} == 0 -->
zero
<!-- else -->
invalid
<!-- /if -->
```
##### 自动结束
if指令不支持自动结束,必须手工编写`指令结束`。
```html
<!-- /if -->
```
#### for
通过for指令的支持,可以实现对Array和Object的遍历。Array为正序遍历,Object为不保证顺序的forin。
##### 语法
for的语法形式为:
for: ${variable} as ${item-variable}
for: ${variable} as ${item-variable}, ${index-variable}
for: ${variable} as ${item-variable}, ${key-variable}
其中,`${variable}`为想要遍历的对象,支持`.`形式的property access。在遍历过程中,声明的`${item-variable}`和`${index-variable}`,分别代表数据项和索引(遍历Object时为键名)。
##### 示例
```html
<ul>
<!-- for: ${persons} as ${person}, ${index} -->
<li>${index}: ${person.name}
<!-- /for -->
</ul>
```
##### 自动结束
for指令不支持自动结束,必须手工编写`指令结束`。
```html
<!-- /for -->
```
## API
### methods
ETPL初始化时自动创建一个默认的引擎实例,并将其暴露。大多数应用场景可直接使用默认的引擎实例。
```javascript
var etpl = require( 'etpl' );
var etpl = require('etpl');
```
##### {void} addFilter( {string}name, {function({string}, {...*}):string}filter )
### 使用
为默认引擎添加过滤器。过滤函数的第一个参数为过滤源字符串,其后的参数可由模板开发者传入。过滤函数必须返回string。
使用ETPL模块,对模板源代码进行编译,会能得到编译后的function
- `{string}`name - 过滤器名称
- `{Function}`filter - 过滤函数
```javascript
etpl.addFilter( 'markdown', function ( source, useExtra ) {
// ......
} );
var render = etpl.compile('Hello ${name}!');
```
##### {Function} compile( {string}source )
执行这个function,传入数据对象,就能得到模板执行的结果了
使用默认引擎编译模板。返回第一个target编译后的renderer函数。
- `{string}`source - 模板源代码
```javascript
var helloRenderer = etpl.compile( 'Hello ${name}!' );
helloRenderer( {name: 'ETPL'} ); // Hello ETPL!
var text = render({ name: 'etpl' });
```
##### {void} config( {Object}options )
查看更多例子,或者对模板渲染结果有疑虑,就去ETPL的[example](http://ecomfe.github.io/etpl/example.html)看看吧。
对默认引擎进行配置,配置参数将合并到引擎现有的参数中。
- `{Object}`options - 配置参数对象
- `{string}`options.commandOpen - 命令语法起始串,默认值为 *<!--*
- `{string}`options.commandClose - 命令语法结束串,默认值为 *-->*
- `{string}`options.defaultFilter - 默认变量替换的filter,默认值为 *html*
- `{boolean}`options.strip - 是否清除命令标签前后的空白字符,默认值为 *false*
- `{string}`options.namingConflict - target或master名字冲突时的处理策略,值可以是`error` | `ignore` | `override`,分别代表`抛出错误`、`保留现有目标,忽略新目标`、`覆盖现有目标`。默认值为 *error*
## Documents
```javascript
etplEngine.config( {
defaultFilter: ''
} );
```
通过文档,你可以更详细地了解ETpl的语法格式、使用方法、API等内容。
##### {string} get( {string}name )
- [模板语法](doc/syntax.md)
- [API](doc/api.md)
- [配置参数](doc/config.md)
从默认引擎中,根据target名称获取模板内容。
- `{string}`name - target名称
## Compatibility
```javascript
etpl.compile( '<!-- target: hello -->Hello ${name}!' );
etpl.get( 'hello' ); // Hello ${name}!
```
### ETpl3的新语法
我们认为,当前流行的通过`block`来表达模板继承中的变化,是更好的表达方式。所以在ETpl3中,我们优化了母版的语法,删除了`master`、`contentplacehoder`、`content`标签,引入了`block`标签。
对于ETpl2的使用者,我们提供一个[etpl2to3](https://github.com/ecomfe/etpl2to3)工具,能够帮助你平滑地将ETpl2的模板翻译成ETpl3。
##### {Function} getRenderer( {string}name )
从默认引擎中,根据target名称获取编译后的renderer函数。
### get
- `{string}`name - target名称
ETpl2中,为了前向兼容,Engine的`get`方法可以根据target名称获取模板内容。
```javascript
etpl.compile( '<!-- target: hello -->Hello ${name}!' );
var helloRenderer = etpl.getRenderer( 'hello' );
helloRenderer( {name: 'ETPL'} ); // Hello ETPL!
```
ETpl3不再支持该方法,所有的模板都通过render来使用:
##### {Function} parse( {string}source )
- 直接使用engine实例的render方法
- 调用renderer function
同`compile`方法。该方法的存在是为了兼容老版本的模板引擎api,不建议使用。
如果仍需要该功能,说明你正在维护一个遗留系统,并且没有很频繁的升级需求。请继续使用ETpl2。
##### {string} render( {string}name, {Object}data )
使用默认引擎执行模板渲染,返回渲染后的字符串。
- `{string}`name - target名称
- `{Object}`data - 模板数据。可以是plain object,也可以是带有 **{string}get({string}name)** 方法的对象
```javascript
etpl.compile( '<!-- target: hello -->Hello ${name}!' );
etpl.render( 'hello', {name: 'ETPL'} ); // Hello ETPL!
```
### classes
#### Engine
*不同的引擎实例可有效避免target命名冲突的问题。*
##### 初始化
下面的代码可以初始化一个新的引擎实例。
```javascript
var etpl = require( 'etpl' );
var etplEngine = new etpl.Engine();
```
引擎实例的初始化允许传入引擎参数。支持的引擎参数见下面的`config`方法。
```javascript
var etpl = require( 'etpl' );
var etplEngine = new etpl.Engine({
commandOpen: '<%',
commandClose: '%>'
});
```
##### {void} addFilter( {string}name, {function({string}, {...*}):string}filter )
添加过滤器。过滤函数的第一个参数为过滤源字符串,其后的参数可由模板开发者传入。过滤函数必须返回string。
- `{string}`name - 过滤器名称
- `{Function}`filter - 过滤函数
```javascript
etplEngine.addFilter( 'markdown', function ( source, useExtra ) {
// ......
} );
```
##### {Function} compile( {string}source )
编译模板。返回第一个target编译后的renderer函数。
- `{string}`source - 模板源代码
```javascript
var helloRenderer = etplEngine.compile( 'Hello ${name}!' );
helloRenderer( {name: 'ETPL'} ); // Hello ETPL!
```
##### {void} config( {Object}options )
对引擎进行配置,配置参数将合并到引擎现有的参数中。
- `{Object}`options - 配置参数对象
- `{string}`options.commandOpen - 命令语法起始串,默认值为 *<!--*
- `{string}`options.commandClose - 命令语法结束串,默认值为 *-->*
- `{string}`options.defaultFilter - 默认变量替换的filter,默认值为 *html*
- `{boolean}`options.strip - 是否清除命令标签前后的空白字符,默认值为 *false*
- `{string}`options.namingConflict - target或master名字冲突时的处理策略,值可以是`error` | `ignore` | `override`,分别代表`抛出错误`、`保留现有目标,忽略新目标`、`覆盖现有目标`。默认值为 *error*
```javascript
etplEngine.config( {
defaultFilter: ''
} );
```
##### {string} get( {string}name )
根据target名称获取模板内容。
- `{string}`name - target名称
```javascript
etplEngine.compile( '<!-- target: hello -->Hello ${name}!' );
etplEngine.get( 'hello' ); // Hello ${name}!
```
##### {Function} getRenderer( {string}name )
根据target名称获取编译后的renderer函数。
- `{string}`name - target名称
```javascript
etplEngine.compile( '<!-- target: hello -->Hello ${name}!' );
var helloRenderer = etplEngine.getRenderer( 'hello' );
helloRenderer( {name: 'ETPL'} ); // Hello ETPL!
```
##### {string} render( {string}name, {Object}data )
执行模板渲染,返回渲染后的字符串。
- `{string}`name - target名称
- `{Object}`data - 模板数据。可以是plain object,也可以是带有 **{string}get({string}name)** 方法的对象
```javascript
etplEngine.compile( '<!-- target: hello -->Hello ${name}!' );
etplEngine.render( 'hello', {name: 'ETPL'} ); // Hello ETPL!
```
## Compatibility
ETPL的前身是[ER框架](https://github.com/ecomfe/er)自带的简易模板引擎,其基本与前身保持兼容。但由于一些考虑因素,存在以下一些不兼容的地方。
### merge
出于代码体积和使用频度的考虑,ETPL删除了`merge`API。如果想要该API,请在自己的应用中加入如下代码:
ETpl的前身是[ER框架](https://github.com/ecomfe/er)自带的简易模板引擎,其基本与前身保持兼容。但出于代码体积和使用频度的考虑,ETpl删除了`merge`API。如果想要该API,请在自己的应用中加入如下代码:

@@ -658,0 +104,0 @@ ```javascript

/**
* ETPL (Enterprise Template)
* Copyright 2013 Baidu Inc. All rights reserved.
*
*
* @file 模板引擎

@@ -10,4 +10,2 @@ * @author errorrik(errorrik@gmail.com)

// 有的正则比较长,所以特别放开一些限制
/* jshint maxdepth: 10, unused: false, white: false */

@@ -19,3 +17,3 @@ // HACK: 可见的重复代码未抽取成function和var是为了gzip size,吐槽的一边去

* 对象属性拷贝
*
*
* @inner

@@ -26,6 +24,6 @@ * @param {Object} target 目标对象

*/
function extend( target, source ) {
for ( var key in source ) {
if ( source.hasOwnProperty( key ) ) {
target[ key ] = source[ key ];
function extend(target, source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}

@@ -54,4 +52,4 @@ }

*/
push: function ( elem ) {
this.raw[ this.length++ ] = elem;
push: function (elem) {
this.raw[this.length++] = elem;
},

@@ -65,4 +63,4 @@

pop: function () {
if ( this.length > 0 ) {
var elem = this.raw[ --this.length ];
if (this.length > 0) {
var elem = this.raw[--this.length];
this.raw.length = this.length;

@@ -79,3 +77,3 @@ return elem;

top: function () {
return this.raw[ this.length - 1 ];
return this.raw[this.length - 1];
},

@@ -89,3 +87,3 @@

bottom: function () {
return this.raw[ 0 ];
return this.raw[0];
},

@@ -95,11 +93,11 @@

* 根据查询条件获取元素
*
*
* @param {Function} condition 查询函数
* @return {*}
*/
find: function ( condition ) {
find: function (condition) {
var index = this.length;
while ( index-- ) {
var item = this.raw[ index ];
if ( condition( item ) ) {
while (index--) {
var item = this.raw[index];
if (condition(item)) {
return item;

@@ -113,3 +111,3 @@ }

* 唯一id的起始值
*
*
* @inner

@@ -122,3 +120,3 @@ * @type {number}

* 获取唯一id,用于匿名target或编译代码的变量名生成
*
*
* @inner

@@ -133,3 +131,3 @@ * @return {string}

* 构建类之间的继承关系
*
*
* @inner

@@ -139,3 +137,4 @@ * @param {Function} subClass 子类函数

*/
function inherits( subClass, superClass ) {
function inherits(subClass, superClass) {
/* jshint -W054 */
var F = new Function();

@@ -145,2 +144,3 @@ F.prototype = superClass.prototype;

subClass.prototype.constructor = subClass;
/* jshint +W054 */
// 由于引擎内部的使用场景都是inherits后,逐个编写子类的prototype方法

@@ -152,3 +152,3 @@ // 所以,不考虑将原有子类prototype缓存再逐个拷贝回去

* HTML Filter替换的字符实体表
*
*
* @const

@@ -159,2 +159,3 @@ * @inner

var HTML_ENTITY = {
/* jshint ignore:start */
'&': '&amp;',

@@ -165,2 +166,3 @@ '<': '&lt;',

"'": '&#39;'
/* jshint ignore:end */
};

@@ -170,3 +172,3 @@

* HTML Filter的替换函数
*
*
* @inner

@@ -176,4 +178,4 @@ * @param {string} c 替换字符

*/
function htmlFilterReplacer( c ) {
return HTML_ENTITY[ c ];
function htmlFilterReplacer(c) {
return HTML_ENTITY[c];
}

@@ -183,3 +185,3 @@

* 默认filter
*
*
* @inner

@@ -192,8 +194,8 @@ * @const

* HTML转义filter
*
*
* @param {string} source 源串
* @return {string}
*/
html: function ( source ) {
return source.replace( /[&<>"']/g, htmlFilterReplacer );
html: function (source) {
return source.replace(/[&<>"']/g, htmlFilterReplacer);
},

@@ -203,3 +205,3 @@

* URL编码filter
*
*
* @param {string} source 源串

@@ -212,7 +214,7 @@ * @return {string}

* 源串filter,用于在默认开启HTML转义时获取源串,不进行转义
*
*
* @param {string} source 源串
* @return {string}
*/
raw: function ( source ) {
raw: function (source) {
return source;

@@ -224,3 +226,3 @@ }

* 字符串字面化
*
*
* @inner

@@ -230,10 +232,10 @@ * @param {string} source 需要字面化的字符串

*/
function stringLiteralize( source ) {
function stringLiteralize(source) {
return '"'
+ source
.replace( /\x5C/g, '\\\\' )
.replace( /"/g, '\\"' )
.replace( /\x0A/g, '\\n' )
.replace( /\x09/g, '\\t' )
.replace( /\x0D/g, '\\r' )
.replace(/\x5C/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\x0A/g, '\\n')
.replace(/\x09/g, '\\t')
.replace(/\x0D/g, '\\r')
// .replace( /\x08/g, '\\b' )

@@ -245,4 +247,17 @@ // .replace( /\x0C/g, '\\f' )

/**
* 对字符串进行可用于new RegExp的字面化
*
* @inner
* @param {string} source 需要字面化的字符串
* @return {string}
*/
function regexpLiteral(source) {
return source.replace(/[\^\[\]\$\(\)\{\}\?\*\.\+]/g, function (c) {
return '\\' + c;
});
}
/**
* 字符串格式化
*
*
* @inner

@@ -253,9 +268,9 @@ * @param {string} source 目标模版字符串

*/
function stringFormat( source ) {
function stringFormat(source) {
var args = arguments;
return source.replace(
return source.replace(
/\{([0-9]+)\}/g,
function ( match, index ) {
return args[ index - 0 + 1 ];
} );
function (match, index) {
return args[index - 0 + 1];
});
}

@@ -265,3 +280,3 @@

* 用于render的字符串变量声明语句
*
*
* @inner

@@ -275,3 +290,3 @@ * @const

* 用于render的字符串内容添加语句(起始)
*
*
* @inner

@@ -285,3 +300,3 @@ * @const

* 用于render的字符串内容添加语句(结束)
*
*
* @inner

@@ -295,3 +310,3 @@ * @const

* 用于render的字符串内容返回语句
*
*
* @inner

@@ -303,5 +318,5 @@ * @const

// HACK: IE8-时,编译后的renderer使用push+join的策略进行字符串拼接
if ( typeof navigator != 'undefined'
&& /msie\s*([0-9]+)/i.test( navigator.userAgent )
// HACK: IE8-时,编译后的renderer使用join Array的策略进行字符串拼接
if (typeof navigator !== 'undefined'
&& /msie\s*([0-9]+)/i.test(navigator.userAgent)
&& RegExp.$1 - 0 < 8

@@ -317,3 +332,3 @@ ) {

* 用于if、var等命令生成编译代码
*
*
* @inner

@@ -323,14 +338,15 @@ * @param {string} name 访问变量名

*/
function toGetVariableLiteral( name ) {
function toGetVariableLiteral(name) {
name = name.replace(/^\s*\*/, '');
return stringFormat(
'gv({0},["{1}"])',
stringLiteralize( name ),
stringLiteralize(name),
name.replace(
/\[['"]?([^'"]+)['"]?\]/g,
function ( match, name ) {
/\[['"]?([^'"]+)['"]?\]/g,
function (match, name) {
return '.' + name;
}
)
.split( '.' )
.join( '","' )
.split('.')
.join('","')
);

@@ -340,22 +356,5 @@ }

/**
* 替换字符串中的${...}成getVariable调用的编译语句
* 用于if、var等命令生成编译代码
*
* @inner
* @param {string} source 源字符串
* @return {string}
*/
function replaceGetVariableLiteral( source ) {
return source.replace(
/\$\{([0-9a-z_\.\[\]'"-]+)\}/ig,
function( match, name ){
return toGetVariableLiteral( name );
}
);
}
/**
* 解析文本片段中以固定字符串开头和结尾的包含块
* 用于 命令串:<!-- ... --> 和 变量替换串:${...} 的解析
*
*
* @inner

@@ -369,37 +368,38 @@ * @param {string} source 要解析的文本

*/
function parseTextBlock( source, open, close, greedy, onInBlock, onOutBlock ) {
function parseTextBlock(source, open, close, greedy, onInBlock, onOutBlock) {
var closeLen = close.length;
var texts = source.split( open );
var texts = source.split(open);
var level = 0;
var buf = [];
for ( var i = 0, len = texts.length; i < len; i++ ) {
var text = texts[ i ];
for (var i = 0, len = texts.length; i < len; i++) {
var text = texts[i];
if ( i ) {
if (i) {
var openBegin = 1;
level++;
while ( 1 ) {
var closeIndex = text.indexOf( close );
if ( closeIndex < 0 ) {
buf.push( text );
while (1) {
var closeIndex = text.indexOf(close);
if (closeIndex < 0) {
buf.push(level > 1 && openBegin ? open : '', text);
break;
}
else {
level = greedy ? level - 1 : 0;
buf.push(
level > 0 ? open : '',
text.slice( 0, closeIndex ),
level > 0 ? close : ''
);
text = text.slice( closeIndex + closeLen );
if ( level === 0 ) {
break;
}
level = greedy ? level - 1 : 0;
buf.push(
level > 0 && openBegin ? open : '',
text.slice(0, closeIndex),
level > 0 ? close : ''
);
text = text.slice(closeIndex + closeLen);
openBegin = 0;
if (level === 0) {
break;
}
}
if ( level === 0 ) {
onInBlock( buf.join( '' ) );
onOutBlock( text );
if (level === 0) {
onInBlock(buf.join(''));
onOutBlock(text);
buf = [];

@@ -409,9 +409,9 @@ }

else {
text && onOutBlock( text );
text && onOutBlock(text);
}
}
if ( level > 0 && buf.length > 0 ) {
onOutBlock( open );
onOutBlock( buf.join( '' ) );
if (level > 0 && buf.length > 0) {
onOutBlock(open);
onOutBlock(buf.join(''));
}

@@ -421,4 +421,107 @@ }

/**
* 编译变量访问和变量替换的代码
* 用于普通文本或if、var、filter等命令生成编译代码
*
* @inner
* @param {string} source 源代码
* @param {Engine} engine 引擎实例
* @param {boolean} forText 是否为输出文本的变量替换
* @return {string}
*/
function compileVariable(source, engine, forText) {
var code = [];
var options = engine.options;
var toStringHead = '';
var toStringFoot = '';
var wrapHead = '';
var wrapFoot = '';
// 默认的filter,当forText模式时有效
var defaultFilter;
if (forText) {
toStringHead = 'ts(';
toStringFoot = ')';
wrapHead = RENDER_STRING_ADD_START;
wrapFoot = RENDER_STRING_ADD_END;
defaultFilter = options.defaultFilter;
}
parseTextBlock(
source, options.variableOpen, options.variableClose, 1,
function (text) {
// 加入默认filter
// 只有当处理forText时,需要加入默认filter
// 处理if/var/use等command时,不需要加入默认filter
if (forText && text.indexOf('|') < 0 && defaultFilter) {
text += '|' + defaultFilter;
}
// variableCode是一个gv调用,然后通过循环,在外面包filter的调用
// 形成filter["b"](filter["a"](gv(...)))
//
// 当forText模式,处理的是文本中的变量替换时
// 传递给filter的需要是字符串形式,所以gv外需要包一层ts调用
// 形成filter["b"](filter["a"](ts(gv(...))))
//
// 当variableName以*起始时,忽略ts调用,直接传递原值给filter
var filterCharIndex = text.indexOf('|');
var variableName = (
filterCharIndex > 0
? text.slice(0, filterCharIndex)
: text
).replace(/^\s+/, '').replace(/\s+$/, '');
var filterSource = filterCharIndex > 0
? text.slice(filterCharIndex + 1)
: '';
var variableRawValue = variableName.indexOf('*') === 0;
var variableCode = [
variableRawValue ? '' : toStringHead,
toGetVariableLiteral(variableName),
variableRawValue ? '' : toStringFoot
];
if (filterSource) {
filterSource = compileVariable(filterSource, engine);
var filterSegs = filterSource.split('|');
for (var i = 0, len = filterSegs.length; i < len; i++) {
var seg = filterSegs[i];
if (/^\s*([a-z0-9_-]+)(\((.*)\))?\s*$/i.test(seg)) {
variableCode.unshift('fs["' + RegExp.$1 + '"](');
if (RegExp.$3) {
variableCode.push(',', RegExp.$3);
}
variableCode.push(')');
}
}
}
code.push(
wrapHead,
variableCode.join(''),
wrapFoot
);
},
function (text) {
code.push(
wrapHead,
forText ? stringLiteralize(text) : text,
wrapFoot
);
}
);
return code.join('');
}
/**
* 文本节点类
*
*
* @inner

@@ -429,16 +532,19 @@ * @constructor

*/
function TextNode( value, engine ) {
function TextNode(value, engine) {
this.value = value;
this.engine = engine;
}
TextNode.prototype = {
/**
* 获取renderer body的生成代码
*
*
* @return {string}
*/
getRendererBody: function () {
if ( !this.value
|| ( this.engine.options.strip && /^\s*$/.test( this.value ) )
var value = this.value;
var options = this.engine.options;
if (!value
|| (options.strip && /^\s*$/.test(value))
) {

@@ -448,75 +554,12 @@ return '';

var defaultFilter = this.engine.options.defaultFilter;
var code = [];
parseTextBlock(
this.value, '${', '}', 1,
function ( text ) { // ${...}内文本的处理函数
// 加入默认filter
if ( text.indexOf( '|' ) < 0 && defaultFilter ) {
text += '|' + defaultFilter;
}
var segs = text.split( /\s*\|\s*/ );
// variableCode最先通过gv和ts调用,取得variable的string形式
// 然后通过循环,在外面包filter的调用
// 形成filter["b"](filter["a"](gvs(...)))
// 当variableName以*起始时,忽略toString,直接传递原值给filter
var variableName = segs[ 0 ];
var toStringHead = 'ts(';
var toStringFoot = ')';
if ( variableName.indexOf( '*' ) === 0 ) {
variableName = variableName.slice( 1 );
toStringHead = toStringFoot = '';
}
var variableCode = [
toStringHead,
toGetVariableLiteral( variableName ),
toStringFoot
];
for ( var i = 1, len = segs.length; i < len; i++ ) {
var seg = segs[ i ];
if ( /^\s*([a-z0-9_-]+)(\((.*)\))?\s*$/i.test( seg ) ) {
variableCode.unshift( 'fs["' + RegExp.$1 + '"](' );
if ( RegExp.$3 ) {
variableCode.push(
',',
replaceGetVariableLiteral( RegExp.$3 )
);
}
variableCode.push( ')' );
}
}
code.push(
RENDER_STRING_ADD_START,
variableCode.join( '' ),
RENDER_STRING_ADD_END
);
},
function ( text ) { // ${...}外普通文本的处理函数
code.push(
RENDER_STRING_ADD_START,
stringLiteralize( text ),
RENDER_STRING_ADD_END
);
}
);
return code.join( '' );
return compileVariable(value, this.engine, 1);
},
/**
* 获取内容
*
* @return {string}
* 复制节点的方法
*
* @return {TextNode}
*/
getContent: function () {
return this.value;
clone: function () {
return this;
}

@@ -527,3 +570,3 @@ };

* 命令节点类
*
*
* @inner

@@ -534,6 +577,7 @@ * @constructor

*/
function Command( value, engine ) {
function Command(value, engine) {
this.value = value;
this.engine = engine;
this.children = [];
this.cloneProps = [];
}

@@ -544,7 +588,7 @@

* 添加子节点
*
*
* @param {TextNode|Command} node 子节点
*/
addChild: function ( node ) {
this.children.push( node );
addChild: function (node) {
this.children.push(node);
},

@@ -554,10 +598,9 @@

* 节点open,解析开始
*
*
* @param {Object} context 语法分析环境对象
*/
open: function ( context ) {
open: function (context) {
var parent = context.stack.top();
this.parent = parent;
parent && parent.addChild( this );
context.stack.push( this );
parent && parent.addChild(this);
context.stack.push(this);
},

@@ -567,21 +610,14 @@

* 节点闭合,解析结束
*
*
* @param {Object} context 语法分析环境对象
*/
close: function ( context ) {
while (context.stack.pop().constructor !== this.constructor) {}
close: function (context) {
if (context.stack.top() === this) {
context.stack.pop();
}
},
/**
* 添加文本节点
*
* @param {TextNode} node 节点
*/
addTextNode: function ( node ) {
this.addChild( node );
},
/**
* 获取renderer body的生成代码
*
*
* @return {string}

@@ -592,7 +628,26 @@ */

var children = this.children;
for ( var i = 0; i < children.length; i++ ) {
buf.push( children[ i ].getRendererBody() );
for (var i = 0; i < children.length; i++) {
buf.push(children[i].getRendererBody());
}
return buf.join( '' );
return buf.join('');
},
/**
* 复制节点的方法
*
* @return {Command}
*/
clone: function () {
var node = new this.constructor(this.value, this.engine);
for (var i = 0, l = this.children.length; i < l; i++) {
node.addChild(this.children[i].clone());
}
for (var i = 0, l = this.cloneProps.length; i < l; i++) {
var prop = this.cloneProps[i];
node[prop] = this[prop];
}
return node;
}

@@ -603,3 +658,3 @@ };

* 命令自动闭合
*
*
* @inner

@@ -609,24 +664,29 @@ * @param {Object} context 语法分析环境对象

*/
function autoCloseCommand( context, CommandType ) {
function autoCloseCommand(context, CommandType) {
var stack = context.stack;
var closeEnd = CommandType
? stack.find( function ( item ) {
return item instanceof CommandType;
} )
var closeEnd = CommandType
? stack.find(
function (item) {
return item instanceof CommandType;
}
)
: stack.bottom();
if ( closeEnd ) {
if (closeEnd) {
var node;
do {
node = stack.top();
while ((node = stack.top()) !== closeEnd) {
/* jshint ignore:start */
// 如果节点对象不包含autoClose方法
// 则认为该节点不支持自动闭合,需要抛出错误
// for等节点不支持自动闭合
if ( !node.autoClose ) {
throw new Error( node.type + ' must be closed manually: ' + node.value );
if (!node.autoClose) {
throw new Error(node.type + ' must be closed manually: ' + node.value);
}
node.autoClose( context );
} while ( node !== closeEnd );
/* jshint ignore:end */
node.autoClose(context);
}
closeEnd.close(context);
}

@@ -639,3 +699,3 @@

* renderer body起始代码段
*
*
* @inner

@@ -673,3 +733,3 @@ * @const

* Target命令节点类
*
*
* @inner

@@ -680,19 +740,22 @@ * @constructor

*/
function TargetCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*(\(\s*master\s*=\s*([a-z0-9_-]+)\s*\))?\s*/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function TargetCommand(value, engine) {
/* jshint ignore:start */
if (!/^\s*([a-z0-9\/_-]+)\s*(\(\s*master\s*=\s*([a-z0-9\/_-]+)\s*\))?\s*/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}
/* jshint ignore:end */
this.master = RegExp.$3;
this.name = RegExp.$1;
Command.call( this, value, engine );
this.contents = {};
Command.call(this, value, engine);
this.blocks = {};
}
// 创建Target命令节点继承关系
inherits( TargetCommand, Command );
inherits(TargetCommand, Command);
/**
* Master命令节点类
*
* Block命令节点类
*
* @inner

@@ -703,59 +766,18 @@ * @constructor

*/
function MasterCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*(\(\s*master\s*=\s*([a-z0-9_-]+)\s*\))?\s*/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function BlockCommand(value, engine) {
if (!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}
this.master = RegExp.$3;
this.name = RegExp.$1;
Command.call( this, value, engine );
this.contents = {};
}
// 创建Master命令节点继承关系
inherits( MasterCommand, Command );
/**
* Content命令节点类
*
* @inner
* @constructor
* @param {string} value 命令节点的value
* @param {Engine} engine 引擎实例
*/
function ContentCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
}
this.name = RegExp.$1;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'name' ];
}
// 创建Content命令节点继承关系
inherits( ContentCommand, Command );
// 创建Block命令节点继承关系
inherits(BlockCommand, Command);
/**
* ContentPlaceHolder命令节点类
*
* @inner
* @constructor
* @param {string} value 命令节点的value
* @param {Engine} engine 引擎实例
*/
function ContentPlaceHolderCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
}
this.name = RegExp.$1;
Command.call( this, value, engine );
}
// 创建ContentPlaceHolder命令节点继承关系
inherits( ContentPlaceHolderCommand, Command );
/**
* Import命令节点类
*
*
* @inner

@@ -766,17 +788,19 @@ * @constructor

*/
function ImportCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function ImportCommand(value, engine) {
if (!/^\s*([a-z0-9\/_-]+)\s*$/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}
this.name = RegExp.$1;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'name', 'state', 'blocks' ];
this.blocks = {};
}
// 创建Import命令节点继承关系
inherits( ImportCommand, Command );
inherits(ImportCommand, Command);
/**
* Var命令节点类
*
*
* @inner

@@ -787,5 +811,5 @@ * @constructor

*/
function VarCommand( value, engine ) {
if ( !/^\s*([a-z0-9_]+)\s*=([\s\S]*)$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function VarCommand(value, engine) {
if (!/^\s*([a-z0-9_]+)\s*=([\s\S]*)$/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}

@@ -795,11 +819,12 @@

this.expr = RegExp.$2;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'name', 'expr' ];
}
// 创建Var命令节点继承关系
inherits( VarCommand, Command );
inherits(VarCommand, Command);
/**
* filter命令节点类
*
*
* @inner

@@ -810,5 +835,5 @@ * @constructor

*/
function FilterCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function FilterCommand(value, engine) {
if (!/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}

@@ -818,11 +843,12 @@

this.args = RegExp.$3;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'name', 'args' ];
}
// 创建filter命令节点继承关系
inherits( FilterCommand, Command );
inherits(FilterCommand, Command);
/**
* Use命令节点类
*
*
* @inner

@@ -833,5 +859,5 @@ * @constructor

*/
function UseCommand( value, engine ) {
if ( !/^\s*([a-z0-9_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function UseCommand(value, engine) {
if (!/^\s*([a-z0-9\/_-]+)\s*(\(([\s\S]*)\))?\s*$/i.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}

@@ -841,11 +867,12 @@

this.args = RegExp.$3;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'name', 'args' ];
}
// 创建Use命令节点继承关系
inherits( UseCommand, Command );
inherits(UseCommand, Command);
/**
* for命令节点类
*
*
* @inner

@@ -856,19 +883,32 @@ * @constructor

*/
function ForCommand( value, engine ) {
if ( !/^\s*\$\{([0-9a-z_\.\[\]'"-]+)\}\s+as\s+\$\{([0-9a-z_]+)\}\s*(,\s*\$\{([0-9a-z_]+)\})?\s*$/i.test( value ) ) {
throw new Error( 'Invalid ' + this.type + ' syntax: ' + value );
function ForCommand(value, engine) {
var rule = new RegExp(
stringFormat(
/* jshint ignore:start */
'^\\s*({0}[\\s\\S]+{1})\\s+as\\s+{0}([0-9a-z_]+){1}\\s*(,\\s*{0}([0-9a-z_]+){1})?\\s*$',
/* jshint ignore:end */
regexpLiteral(engine.options.variableOpen),
regexpLiteral(engine.options.variableClose)
),
'i'
);
if (!rule.test(value)) {
throw new Error('Invalid ' + this.type + ' syntax: ' + value);
}
this.list = RegExp.$1;
this.item = RegExp.$2;
this.index = RegExp.$4;
Command.call( this, value, engine );
Command.call(this, value, engine);
this.cloneProps = [ 'list', 'item', 'index' ];
}
// 创建for命令节点继承关系
inherits( ForCommand, Command );
inherits(ForCommand, Command);
/**
* if命令节点类
*
*
* @inner

@@ -879,12 +919,12 @@ * @constructor

*/
function IfCommand( value, engine ) {
Command.call( this, value, engine );
function IfCommand(value, engine) {
Command.call(this, value, engine);
}
// 创建if命令节点继承关系
inherits( IfCommand, Command );
inherits(IfCommand, Command);
/**
* elif命令节点类
*
*
* @inner

@@ -895,12 +935,12 @@ * @constructor

*/
function ElifCommand( value, engine ) {
IfCommand.call( this, value, engine );
function ElifCommand(value, engine) {
IfCommand.call(this, value, engine);
}
// 创建elif命令节点继承关系
inherits( ElifCommand, IfCommand );
inherits(ElifCommand, IfCommand);
/**
* else命令节点类
*
*
* @inner

@@ -911,15 +951,15 @@ * @constructor

*/
function ElseCommand( value, engine ) {
Command.call( this, value, engine );
function ElseCommand(value, engine) {
Command.call(this, value, engine);
}
// 创建else命令节点继承关系
inherits( ElseCommand, Command );
inherits(ElseCommand, IfCommand);
/**
* Target和Master的节点状态
*
* Target的节点状态
*
* @inner
*/
var TMNodeState = {
var TargetState = {
READING: 1,

@@ -932,69 +972,40 @@ READED: 2,

/**
* 节点闭合,解析结束
*
* @param {Object} context 语法分析环境对象
*/
MasterCommand.prototype.close =
/**
* 节点闭合,解析结束。自闭合时被调用
*
* @param {Object} context 语法分析环境对象
*/
MasterCommand.prototype.autoClose =
/**
* 节点闭合,解析结束
*
* @param {Object} context 语法分析环境对象
*/
TargetCommand.prototype.close =
/**
* 节点闭合,解析结束。自闭合时被调用
*
* @param {Object} context 语法分析环境对象
*/
TargetCommand.prototype.autoClose = function ( context ) {
Command.prototype.close.call( this, context );
this.state = this.master ? TMNodeState.READED : TMNodeState.APPLIED;
context.targetOrMaster = null;
};
/**
* 应用其继承的母版,返回是否成功应用母版
*
*
* @return {boolean}
*/
TargetCommand.prototype.applyMaster =
ImportCommand.prototype.applyMaster =
/**
* 应用其继承的母版,返回是否成功应用母版
*
*
* @return {boolean}
*/
MasterCommand.prototype.applyMaster = function () {
if ( this.state >= TMNodeState.APPLIED ) {
TargetCommand.prototype.applyMaster = function (masterName) {
if (this.state >= TargetState.APPLIED) {
return 1;
}
var masterNode = this.engine.masters[ this.master ];
if ( masterNode && masterNode.applyMaster() ) {
this.children = [];
var blocks = this.blocks;
for ( var i = 0, len = masterNode.children.length; i < len; i++ ) {
var child = masterNode.children[ i ];
function replaceBlock(node) {
var children = node.children;
if ( child instanceof ContentPlaceHolderCommand ) {
this.children.push.apply(
this.children,
(this.contents[ child.name ] || child).children
);
if (children instanceof Array) {
for (var i = 0, len = children.length; i < len; i++) {
var child = children[i];
if (child instanceof BlockCommand && blocks[child.name]) {
child = children[i] = blocks[child.name];
}
replaceBlock(child);
}
else {
this.children.push( child );
}
}
}
this.state = TMNodeState.APPLIED;
var master = this.engine.targets[masterName];
if (master && master.applyMaster(master.master)) {
this.children = master.clone().children;
replaceBlock(this);
this.state = TargetState.APPLIED;
return 1;

@@ -1006,8 +1017,8 @@ }

* 判断target是否ready
* 包括是否成功应用母版,以及import和use语句依赖的target是否ready
*
* 包括是否成功应用母版,以及import语句依赖的target是否ready
*
* @return {boolean}
*/
TargetCommand.prototype.isReady = function () {
if ( this.state >= TMNodeState.READY ) {
if (this.state >= TargetState.READY) {
return 1;

@@ -1021,16 +1032,16 @@ }

* 递归检查节点的ready状态
*
*
* @inner
* @param {Command|TextNode} node 目标节点
*/
function checkReadyState( node ) {
for ( var i = 0, len = node.children.length; i < len; i++ ) {
var child = node.children[ i ];
if ( child instanceof ImportCommand ) {
var target = engine.targets[ child.name ];
readyState = readyState
&& target && target.isReady( engine );
function checkReadyState(node) {
for (var i = 0, len = node.children.length; i < len; i++) {
var child = node.children[i];
if (child instanceof ImportCommand) {
var target = engine.targets[child.name];
readyState = readyState
&& target && target.isReady(engine);
}
else if ( child instanceof Command ) {
checkReadyState( child );
else if (child instanceof Command) {
checkReadyState(child);
}

@@ -1040,5 +1051,5 @@ }

if ( this.applyMaster() ) {
checkReadyState( this );
readyState && (this.state = TMNodeState.READY);
if (this.applyMaster(this.master)) {
checkReadyState(this);
readyState && (this.state = TargetState.READY);
return readyState;

@@ -1050,16 +1061,18 @@ }

* 获取target的renderer函数
*
*
* @return {function(Object):string}
*/
TargetCommand.prototype.getRenderer = function () {
if ( this.renderer ) {
if (this.renderer) {
return this.renderer;
}
if ( this.isReady() ) {
// console.log(RENDERER_BODY_START +RENDER_STRING_DECLATION
// + this.getRendererBody()
// + RENDER_STRING_RETURN)
if (this.isReady()) {
// console.log(this.name + ' ------------------');
// console.log(RENDERER_BODY_START + RENDER_STRING_DECLATION
// + this.getRendererBody()
// + RENDER_STRING_RETURN);
var realRenderer = new Function(
/* jshint -W054 */
var realRenderer = new Function(
'data', 'engine',

@@ -1071,9 +1084,9 @@ [

RENDER_STRING_RETURN
].join( '\n' )
].join('\n')
);
/* jshint +W054 */
var engine = this.engine;
this.renderer = function ( data ) {
return realRenderer( data, engine );
this.renderer = function (data) {
return realRenderer(data, engine);
};

@@ -1088,50 +1101,30 @@

/**
* 获取内容
*
* @return {string}
*/
TargetCommand.prototype.getContent = function () {
if ( this.isReady() ) {
var buf = [];
var children = this.children;
for ( var i = 0; i < children.length; i++ ) {
buf.push( children[ i ].getContent() );
}
return buf.join( '' );
}
return '';
};
/**
* 将target或master节点对象添加到语法分析环境中
*
* 将target节点对象添加到语法分析环境中
*
* @inner
* @param {TargetCommand|MasterCommand} targetOrMaster target或master节点对象
* @param {TargetCommand} target target节点对象
* @param {Object} context 语法分析环境对象
*/
function addTargetOrMasterToContext( targetOrMaster, context ) {
context.targetOrMaster = targetOrMaster;
function addTargetToContext(target, context) {
context.target = target;
var engine = context.engine;
var name = targetOrMaster.name;
var isTarget = targetOrMaster instanceof TargetCommand;
var prop = isTarget ? 'targets' : 'masters';
var name = target.name;
if ( engine[ prop ][ name ] ) {
switch ( engine.options.namingConflict ) {
if (engine.targets[name]) {
switch (engine.options.namingConflict) {
/* jshint ignore:start */
case 'override':
engine[ prop ][ name ] = targetOrMaster;
isTarget && context.targets.push( name );
engine.targets[name] = target;
context.targets.push(name);
case 'ignore':
break;
/* jshint ignore:end */
default:
throw new Error( ( isTarget ? 'Target' :'Master' )
+ ' is exists: ' + name );
throw new Error('Target exists: ' + name);
}
}
else {
engine[ prop ][ name ] = targetOrMaster;
isTarget && context.targets.push( name );
engine.targets[name] = target;
context.targets.push(name);
}

@@ -1142,151 +1135,208 @@ }

* target节点open,解析开始
*
*
* @param {Object} context 语法分析环境对象
*/
TargetCommand.prototype.open =
TargetCommand.prototype.open = function (context) {
autoCloseCommand(context);
Command.prototype.open.call(this, context);
this.state = TargetState.READING;
addTargetToContext(this, context);
};
/**
* master节点open,解析开始
*
* Var节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
MasterCommand.prototype.open = function ( context ) {
autoCloseCommand( context );
Command.prototype.open.call( this, context );
this.state = TMNodeState.READING;
addTargetOrMasterToContext( this, context );
VarCommand.prototype.open =
/**
* Use节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
UseCommand.prototype.open = function (context) {
context.stack.top().addChild(this);
};
/**
* Import节点open,解析开始
*
* Block节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.open =
BlockCommand.prototype.open = function (context) {
Command.prototype.open.call(this, context);
(context.imp || context.target).blocks[this.name] = this;
};
/**
* Var节点open,解析开始
*
* elif节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
VarCommand.prototype.open =
ElifCommand.prototype.open = function (context) {
var elseCommand = new ElseCommand();
elseCommand.open(context);
var ifCommand = autoCloseCommand(context, IfCommand);
ifCommand.addChild(this);
context.stack.push(this);
};
/**
* Use节点open,解析开始
*
* else节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
UseCommand.prototype.open = function ( context ) {
var parent = context.stack.top();
this.parent = parent;
parent.addChild( this );
ElseCommand.prototype.open = function (context) {
var ifCommand = autoCloseCommand(context, IfCommand);
ifCommand.addChild(this);
context.stack.push(this);
};
/**
* import节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.open = function (context) {
this.parent = context.stack.top();
this.target = context.target;
Command.prototype.open.call(this, context);
this.state = TargetState.READING;
context.imp = this;
};
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* 节点解析结束
* 由于use节点无需闭合,处理时不会入栈,所以将close置为空函数
*
* @param {Object} context 语法分析环境对象
*/
UseCommand.prototype.beforeOpen =
UseCommand.prototype.close =
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* 节点解析结束
* 由于var节点无需闭合,处理时不会入栈,所以将close置为空函数
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.beforeOpen =
VarCommand.prototype.close = function () {};
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* 节点解析结束
*
* @param {Object} context 语法分析环境对象
*/
VarCommand.prototype.beforeOpen =
ImportCommand.prototype.close = function (context) {
Command.prototype.close.call(this, context);
this.state = TargetState.READED;
context.imp = null;
};
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* 节点闭合,解析结束
*
* @param {Object} context 语法分析环境对象
*/
ForCommand.prototype.beforeOpen =
TargetCommand.prototype.close = function (context) {
Command.prototype.close.call(this, context);
this.state = this.master ? TargetState.READED : TargetState.APPLIED;
context.target = null;
};
/**
* 节点自动闭合,解析结束
* ImportCommand的自动结束逻辑为,在其开始位置后马上结束
* 所以,其自动结束时children应赋予其所属的parent
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.autoClose = function (context) {
// move children to parent
var parentChildren = this.parent.children;
parentChildren.push.apply(parentChildren, this.children);
this.children.length = 0;
// move blocks to target
for (var key in this.blocks) {
this.target.blocks[key] = this.blocks[key];
}
this.blocks = {};
// do close
this.close(context);
};
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
*
* @param {Object} context 语法分析环境对象
*/
FilterCommand.prototype.beforeOpen =
UseCommand.prototype.beforeOpen =
/**
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
*
* @param {Object} context 语法分析环境对象
*/
IfCommand.prototype.beforeOpen =
ImportCommand.prototype.beforeOpen =
/**
* 文本节点被添加到分析环境前的处理动作:节点不在target中时,自动创建匿名target
*
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
TextNode.prototype.beforeAdd = function ( context ) {
if ( context.stack.bottom() ) {
return;
}
VarCommand.prototype.beforeOpen =
var target = new TargetCommand( generateGUID(), context.engine );
target.open( context );
};
/**
* 节点解析结束
* 由于use节点无需闭合,处理时不会入栈,所以将close置为空函数
*
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
UseCommand.prototype.close =
ForCommand.prototype.beforeOpen =
/**
* 节点解析结束
* 由于import节点无需闭合,处理时不会入栈,所以将close置为空函数
*
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.close =
*/
FilterCommand.prototype.beforeOpen =
/**
* 节点解析结束
* 由于else节点无需闭合,处理时不会入栈,闭合由if负责。所以将close置为空函数
*
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
ElseCommand.prototype.close =
BlockCommand.prototype.beforeOpen =
/**
* 节点解析结束
* 由于var节点无需闭合,处理时不会入栈,所以将close置为空函数
*
* 节点open前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
VarCommand.prototype.close = function () {};
IfCommand.prototype.beforeOpen =
/**
* 获取内容
*
* @return {string}
* 文本节点被添加到分析环境前的处理动作:节点不在target中时,自动创建匿名target
*
* @param {Object} context 语法分析环境对象
*/
ImportCommand.prototype.getContent = function () {
var target = this.engine.targets[ this.name ];
return target.getContent();
TextNode.prototype.beforeAdd = function (context) {
if (context.stack.bottom()) {
return;
}
var target = new TargetCommand(generateGUID(), context.engine);
target.open(context);
};
/**
* 获取renderer body的生成代码
*
*
* @return {string}
*/
ImportCommand.prototype.getRendererBody = function () {
var target = this.engine.targets[ this.name ];
return target.getRendererBody();
this.applyMaster(this.name);
return Command.prototype.getRendererBody.call(this);
};

@@ -1296,3 +1346,3 @@

* 获取renderer body的生成代码
*
*
* @return {string}

@@ -1305,25 +1355,23 @@ */

RENDER_STRING_ADD_END,
stringLiteralize( this.name ),
replaceGetVariableLiteral(
this.args.replace(
/(^|,)\s*([a-z0-9_]+)\s*=/ig,
function ( match, start, argName ) {
return (start || '') + stringLiteralize( argName ) + ':';
}
)
stringLiteralize(this.name),
compileVariable(this.args, this.engine).replace(
/(^|,)\s*([a-z0-9_]+)\s*=/ig,
function (match, start, argName) {
return (start || '') + stringLiteralize(argName) + ':';
}
)
);
};
/**
* 获取renderer body的生成代码
*
*
* @return {string}
*/
VarCommand.prototype.getRendererBody = function () {
if ( this.expr ) {
return stringFormat(
if (this.expr) {
return stringFormat(
'v[{0}]={1};',
stringLiteralize( this.name ),
replaceGetVariableLiteral( this.expr )
stringLiteralize(this.name),
compileVariable(this.expr, this.engine)
);

@@ -1337,24 +1385,23 @@ }

* 获取renderer body的生成代码
*
*
* @return {string}
*/
IfCommand.prototype.getRendererBody = function () {
var rendererBody = stringFormat(
return stringFormat(
'if({0}){{1}}',
replaceGetVariableLiteral( this.value ),
Command.prototype.getRendererBody.call( this )
compileVariable(this.value, this.engine),
Command.prototype.getRendererBody.call(this)
);
};
var elseCommand = this[ 'else' ];
if ( elseCommand ) {
return [
rendererBody,
stringFormat(
'else{{0}}',
elseCommand.getRendererBody()
)
].join( '' );
}
return rendererBody;
/**
* 获取renderer body的生成代码
*
* @return {string}
*/
ElseCommand.prototype.getRendererBody = function () {
return stringFormat(
'}else{{0}',
Command.prototype.getRendererBody.call(this)
);
};

@@ -1364,3 +1411,3 @@

* 获取renderer body的生成代码
*
*
* @return {string}

@@ -1370,2 +1417,3 @@ */

return stringFormat(
/* jshint ignore:start */
''

@@ -1377,9 +1425,10 @@ + 'var {0}={1};'

+ 'for(var {4} in {0}){v[{2}]={4};v[{3}]={0}[{4}];{6}}',
/* jshint ignore:end */
generateGUID(),
toGetVariableLiteral( this.list ),
stringLiteralize( this.index || generateGUID() ),
stringLiteralize( this.item ),
compileVariable(this.list, this.engine),
stringLiteralize(this.index || generateGUID()),
stringLiteralize(this.item),
generateGUID(),
generateGUID(),
Command.prototype.getRendererBody.call( this )
Command.prototype.getRendererBody.call(this)
);

@@ -1390,3 +1439,3 @@ };

* 获取renderer body的生成代码
*
*
* @return {string}

@@ -1402,5 +1451,5 @@ */

RENDER_STRING_ADD_END,
Command.prototype.getRendererBody.call( this ),
stringLiteralize( this.name ),
args ? ',' + replaceGetVariableLiteral( args ) : ''
Command.prototype.getRendererBody.call(this),
stringLiteralize(this.name),
args ? ',' + compileVariable(args, this.engine) : ''
);

@@ -1410,92 +1459,4 @@ };

/**
* content节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ContentCommand.prototype.open = function ( context ) {
autoCloseCommand( context, ContentCommand );
Command.prototype.open.call( this, context );
context.targetOrMaster.contents[ this.name ] = this;
};
/**
* content节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ContentPlaceHolderCommand.prototype.open = function ( context ) {
autoCloseCommand( context, ContentPlaceHolderCommand );
Command.prototype.open.call( this, context );
};
/**
* 节点自动闭合,解析结束
*
* @param {Object} context 语法分析环境对象
*/
ContentCommand.prototype.autoClose =
/**
* 节点自动闭合,解析结束
*
* @param {Object} context 语法分析环境对象
*/
IfCommand.prototype.autoClose = Command.prototype.close;
/**
* 节点自动闭合,解析结束
* contentplaceholder的自动结束逻辑为,在其开始位置后马上结束
* 所以,其自动结束时children应赋予其所属的parent,也就是master
*
* @param {Object} context 语法分析环境对象
*/
ContentPlaceHolderCommand.prototype.autoClose = function ( context ) {
var parentChildren = this.parent.children;
parentChildren.push.apply( parentChildren, this.children );
this.children.length = 0;
this.close( context );
};
/**
* 添加子节点
*
* @param {TextNode|Command} node 子节点
*/
IfCommand.prototype.addChild = function ( node ) {
var elseCommand = this[ 'else' ];
( elseCommand
? elseCommand.children
: this.children
).push( node );
};
/**
* elif节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ElifCommand.prototype.open = function ( context ) {
var elseCommand = new ElseCommand();
elseCommand.open( context );
var ifCommand = autoCloseCommand( context, IfCommand );
ifCommand.addChild( this );
context.stack.push( this );
};
/**
* else节点open,解析开始
*
* @param {Object} context 语法分析环境对象
*/
ElseCommand.prototype.open = function ( context ) {
var ifCommand = autoCloseCommand( context, IfCommand );
ifCommand[ 'else' ] = this;
context.stack.push( ifCommand );
};
/**
* 命令类型集合
*
*
* @type {Object}

@@ -1507,3 +1468,3 @@ */

* 添加命令类型
*
*
* @inner

@@ -1513,24 +1474,22 @@ * @param {string} name 命令名称

*/
function addCommandType( name, Type ) {
commandTypes[ name ] = Type;
function addCommandType(name, Type) {
commandTypes[name] = Type;
Type.prototype.type = name;
}
addCommandType( 'target', TargetCommand );
addCommandType( 'master', MasterCommand );
addCommandType( 'content', ContentCommand );
addCommandType( 'contentplaceholder', ContentPlaceHolderCommand );
addCommandType( 'import', ImportCommand );
addCommandType( 'use', UseCommand );
addCommandType( 'var', VarCommand );
addCommandType( 'for', ForCommand );
addCommandType( 'if', IfCommand );
addCommandType( 'elif', ElifCommand );
addCommandType( 'else', ElseCommand );
addCommandType( 'filter', FilterCommand );
addCommandType('target', TargetCommand);
addCommandType('block', BlockCommand);
addCommandType('import', ImportCommand);
addCommandType('use', UseCommand);
addCommandType('var', VarCommand);
addCommandType('for', ForCommand);
addCommandType('if', IfCommand);
addCommandType('elif', ElifCommand);
addCommandType('else', ElseCommand);
addCommandType('filter', FilterCommand);
/**
* etpl引擎类
*
*
* @constructor

@@ -1540,15 +1499,19 @@ * @param {Object=} options 引擎参数

* @param {string=} options.commandClose 命令语法结束串
* @param {string=} options.variableOpen 变量语法起始串
* @param {string=} options.variableClose 变量语法结束串
* @param {string=} options.defaultFilter 默认变量替换的filter
* @param {boolean=} options.strip 是否清除命令标签前后的空白字符
* @param {string=} options.namingConflict target或master名字冲突时的处理策略
* @param {string=} options.namingConflict target名字冲突时的处理策略
*/
function Engine( options ) {
function Engine(options) {
this.options = {
commandOpen: '<!--',
commandClose: '-->',
commandSyntax: /^\s*(\/)?([a-z]+)\s*(?::([\s\S]*))?$/,
variableOpen: '${',
variableClose: '}',
defaultFilter: 'html'
};
this.config( options );
this.masters = {};
this.config(options);
this.targets = {};

@@ -1560,12 +1523,14 @@ this.filters = extend({}, DEFAULT_FILTERS);

* 配置引擎参数,设置的参数将被合并到现有参数中
*
*
* @param {Object} options 参数对象
* @param {string=} options.commandOpen 命令语法起始串
* @param {string=} options.commandClose 命令语法结束串
* @param {string=} options.variableOpen 变量语法起始串
* @param {string=} options.variableClose 变量语法结束串
* @param {string=} options.defaultFilter 默认变量替换的filter
* @param {boolean=} options.strip 是否清除命令标签前后的空白字符
* @param {string=} options.namingConflict target或master名字冲突时的处理策略
* @param {string=} options.namingConflict target名字冲突时的处理策略
*/
Engine.prototype.config = function ( options ) {
extend( this.options, options );
Engine.prototype.config = function (options) {
extend(this.options, options);
};

@@ -1575,7 +1540,7 @@

* 解析模板并编译,返回第一个target编译后的renderer函数。
*
*
* @param {string} source 模板源代码
* @return {function(Object):string}
*/
Engine.prototype.compile =
Engine.prototype.compile =

@@ -1585,26 +1550,28 @@ /**

* 该方法的存在为了兼容老模板引擎
*
*
* @param {string} source 模板源代码
* @return {function(Object):string}
*/
Engine.prototype.parse = function ( source ) {
if ( source ) {
var targetNames = parseSource( source, this );
if ( targetNames.length ) {
return this.targets[ targetNames[ 0 ] ].getRenderer();
Engine.prototype.parse = function (source) {
if (source) {
var targetNames = parseSource(source, this);
if (targetNames.length) {
return this.targets[targetNames[0]].getRenderer();
}
}
/* jshint -W054 */
return new Function('return ""');
/* jshint +W054 */
};
/**
* 根据target名称获取编译后的renderer函数
*
*
* @param {string} name target名称
* @return {function(Object):string}
*/
Engine.prototype.getRenderer = function ( name ) {
var target = this.targets[ name ];
if ( target ) {
Engine.prototype.getRenderer = function (name) {
var target = this.targets[name];
if (target) {
return target.getRenderer();

@@ -1615,19 +1582,4 @@ }

/**
* 根据target名称获取模板内容
*
* @param {string} name target名称
* @return {string}
*/
Engine.prototype.get = function ( name ) {
var target = this.targets[ name ];
if ( target ) {
return target.getContent();
}
return '';
};
/**
* 执行模板渲染,返回渲染后的字符串。
*
*
* @param {string} name target名称

@@ -1639,6 +1591,6 @@ * @param {Object=} data 模板数据。

*/
Engine.prototype.render = function ( name, data ) {
var renderer = this.getRenderer( name );
if ( renderer ) {
return renderer( data );
Engine.prototype.render = function (name, data) {
var renderer = this.getRenderer(name);
if (renderer) {
return renderer(data);
}

@@ -1651,9 +1603,9 @@

* 增加过滤器
*
*
* @param {string} name 过滤器名称
* @param {Function} filter 过滤函数
*/
Engine.prototype.addFilter = function ( name, filter ) {
if ( typeof filter == 'function' ) {
this.filters[ name ] = filter;
Engine.prototype.addFilter = function (name, filter) {
if (typeof filter === 'function') {
this.filters[name] = filter;
}

@@ -1664,3 +1616,3 @@ };

* 解析源代码
*
*
* @inner

@@ -1671,5 +1623,6 @@ * @param {string} source 模板源代码

*/
function parseSource( source, engine ) {
function parseSource(source, engine) {
var commandOpen = engine.options.commandOpen;
var commandClose = engine.options.commandClose;
var commandSyntax = engine.options.commandSyntax;

@@ -1680,3 +1633,4 @@ var stack = new Stack();

targets: [],
stack: stack
stack: stack,
target: null
};

@@ -1693,14 +1647,14 @@

function flushTextBuf() {
if ( textBuf.length > 0 ) {
var text = textBuf.join( '' );
var textNode = new TextNode( text, engine );
textNode.beforeAdd( analyseContext );
var text;
if (textBuf.length > 0 && (text = textBuf.join(''))) {
var textNode = new TextNode(text, engine);
textNode.beforeAdd(analyseContext);
stack.top().addTextNode( textNode );
stack.top().addChild(textNode);
textBuf = [];
if ( engine.options.strip
&& analyseContext.current instanceof Command
if (engine.options.strip
&& analyseContext.current instanceof Command
) {
textNode.value = text.replace( /^[\x20\t\r]*\n/, '' );
textNode.value = text.replace(/^[\x20\t\r]*\n/, '');
}

@@ -1713,45 +1667,32 @@ analyseContext.current = textNode;

/**
* 判断节点是否是NodeType类型的实例
* 用于在stack中find提供filter
*
* @inner
* @param {Command} node 目标节点
* @return {boolean}
*/
function isInstanceofNodeType( node ) {
return node instanceof NodeType;
}
parseTextBlock(
source, commandOpen, commandClose, 0,
function ( text ) { // <!--...-->内文本的处理函数
var match = /^\s*(\/)?([a-z]+)\s*(:([\s\S]*))?$/.exec( text );
function (text) { // <!--...-->内文本的处理函数
var match = commandSyntax.exec(text);
// 符合command规则,并且存在相应的Command类,说明是合法有含义的Command
// 否则,为不具有command含义的普通文本
if ( match
&& ( NodeType = commandTypes[ match[2].toLowerCase() ] )
&& typeof NodeType == 'function'
if (match
&& (NodeType = commandTypes[match[2].toLowerCase()])
&& typeof NodeType === 'function'
) {
// 先将缓冲区中的text节点内容写入
flushTextBuf();
flushTextBuf();
var currentNode = analyseContext.current;
if ( engine.options.strip && currentNode instanceof TextNode ) {
if (engine.options.strip && currentNode instanceof TextNode) {
currentNode.value = currentNode.value
.replace( /\r?\n[\x20\t]*$/, '\n' );
.replace(/\r?\n[\x20\t]*$/, '\n');
}
if ( match[1] ) {
currentNode = stack.find( isInstanceofNodeType );
currentNode && currentNode.close( analyseContext );
if (match[1]) {
currentNode = autoCloseCommand(analyseContext, NodeType);
}
else {
currentNode = new NodeType( match[4], engine );
if ( typeof currentNode.beforeOpen == 'function' ) {
currentNode.beforeOpen( analyseContext );
currentNode = new NodeType(match[3], engine);
if (typeof currentNode.beforeOpen === 'function') {
currentNode.beforeOpen(analyseContext);
}
currentNode.open( analyseContext );
currentNode.open(analyseContext);
}

@@ -1761,5 +1702,5 @@

}
else if ( !/^\s*\/\//.test( text ) ) {
else if (!/^\s*\/\//.test(text)) {
// 如果不是模板注释,则作为普通文本,写入缓冲区
textBuf.push( commandOpen, text, commandClose );
textBuf.push(commandOpen, text, commandClose);
}

@@ -1770,5 +1711,5 @@

function ( text ) { // <!--...-->外,普通文本的处理函数
function (text) { // <!--...-->外,普通文本的处理函数
// 普通文本直接写入缓冲区
textBuf.push( text );
textBuf.push(text);
}

@@ -1779,3 +1720,3 @@ );

flushTextBuf(); // 将缓冲区中的text节点内容写入
autoCloseCommand( analyseContext );
autoCloseCommand(analyseContext);

@@ -1787,10 +1728,10 @@ return analyseContext.targets;

etpl.Engine = Engine;
if ( typeof exports == 'object' && typeof module == 'object' ) {
if (typeof exports === 'object' && typeof module === 'object') {
// For CommonJS
exports = module.exports = etpl;
}
else if ( typeof define == 'function' && define.amd ) {
else if (typeof define === 'function' && define.amd) {
// For AMD
define( etpl );
define(etpl);
}

@@ -1797,0 +1738,0 @@ else {

/**
* ETPL (Enterprise Template)
* Copyright 2013 Baidu Inc. All rights reserved.
*
*
* @file 加载模板的amd模块

@@ -9,27 +9,31 @@ * @author errorrik(errorrik@gmail.com)

define(
function ( require, exports, module ) {
var etpl = require( '.' );
define(
function (require, exports, module) {
var etpl = require('.');
return {
load: function ( resourceId, req, load, config ) {
var xhr = window.XMLHttpRequest
load: function (resourceId, req, load) {
var xhr = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject( 'Microsoft.XMLHTTP' );
xhr.open( 'GET', req.toUrl( resourceId ), true );
: new ActiveXObject('Microsoft.XMLHTTP');
xhr.open('GET', req.toUrl(resourceId), true);
xhr.onreadystatechange = function () {
if ( xhr.readyState == 4 ) {
if ( xhr.status >= 200 && xhr.status < 300 ) {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
var source = xhr.responseText;
var moduleConfig = module.config();
if ( typeof moduleConfig.autoCompile == 'undefined'
|| moduleConfig.autoCompile
if (moduleConfig.autoCompile
|| moduleConfig.autoCompile == null
) {
etpl.compile( source );
etpl.compile(source);
}
load( source );
load(source);
}
/* jshint -W054 */
xhr.onreadystatechange = new Function();
/* jshint +W054 */
xhr = null;

@@ -39,6 +43,6 @@ }

xhr.send( null );
xhr.send(null);
}
};
}
);
);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc