mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Initial code commit before migration to RaptorJS 3
This commit is contained in:
commit
ff475c6e62
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/work
|
||||
/build
|
||||
/.idea/
|
||||
/npm-debug.log
|
||||
/node_modules
|
||||
/*.sublime-workspace
|
||||
*.orig
|
||||
.DS_Store
|
||||
coverage
|
||||
40
.jshintrc
Normal file
40
.jshintrc
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"predef": [
|
||||
|
||||
],
|
||||
|
||||
"globals": {
|
||||
"define": true,
|
||||
"require": true
|
||||
},
|
||||
|
||||
"node" : true,
|
||||
"es5" : false,
|
||||
"browser" : true,
|
||||
"boss" : false,
|
||||
"curly": false,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": true,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": false,
|
||||
"white": false,
|
||||
"eqeqeq": false,
|
||||
"latedef": true,
|
||||
"unused": "vars",
|
||||
|
||||
/* Relaxing options: */
|
||||
"eqnull": true
|
||||
}
|
||||
11
lib/async-optimizer.json
Normal file
11
lib/async-optimizer.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"package": "raptor/render-context/async"
|
||||
},
|
||||
{
|
||||
"package": "raptor/promises"
|
||||
},
|
||||
"templating_async.js"
|
||||
]
|
||||
}
|
||||
187
lib/compiler/AttributeSplitter.js
Normal file
187
lib/compiler/AttributeSplitter.js
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility class to support sub-attributes in an XML attribute. Each sub-attribute must
|
||||
* be separated by a semicolon. Within each sub-attribute, the name/value pair must
|
||||
* be split using an equal sign. However, the name for the first sub-attribute
|
||||
* is optional and a default name can be provided when reading the sub-attributes.
|
||||
*
|
||||
* <p>
|
||||
* Sub-attribute format:
|
||||
* (<attr-value>)?(<attr-name>=<attr-value>;)*(<attr-name>=<attr-value>)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
define.Class(
|
||||
'raptor/templating/compiler/AttributeSplitter',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings'),
|
||||
Expression = require('raptor/templating/compiler/Expression'),
|
||||
TypeConverter = require('raptor/templating/compiler/TypeConverter'),
|
||||
regExp = /"(?:[^"]|\\")*"|'(?:[^']|\\')*'|==|===|[;=]/g;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
var AttributeSplitter = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the provided string to find the sub-attributes that it contains.
|
||||
* The parsed output can be either returned as an array or a map. By default,
|
||||
* the parsed output is returned as a map where each property corresponds
|
||||
* to a sub-attribute. However, if the order of the sub-attributes is important
|
||||
* then the "ordered" option can be set to "true" and
|
||||
* an array will instead be returned where each element in the array is an object
|
||||
* with a name and value property that corresponds to the matching sub-attribute.
|
||||
*
|
||||
* <p>
|
||||
* Supported options:
|
||||
* <ul>
|
||||
* <li>ordered (boolean, defaults to "false") - If true then an array is returned (see above). Otherwise, an object is returned.
|
||||
* </ul>
|
||||
*
|
||||
* @memberOf raptor/templating/compiler$AttributeSplitter
|
||||
* @param attr {String} The attribute to split
|
||||
* @param types {Object} Type definitions for the possible sub-attributes.
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
AttributeSplitter.parse = function(attr, types, options) {
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
var partStart = 0,
|
||||
ordered = options.ordered === true,
|
||||
defaultName = options.defaultName,
|
||||
removeDashes = options.removeDashes === true,
|
||||
matches,
|
||||
equalIndex = -1,
|
||||
result = ordered ? [] : {},
|
||||
handleError = function(message) {
|
||||
if (options.errorHandler) {
|
||||
options.errorHandler(message);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error(message));
|
||||
}
|
||||
},
|
||||
finishPart = function(endIndex) {
|
||||
if (partStart === endIndex) {
|
||||
//The part is an empty string... ignore it
|
||||
return;
|
||||
}
|
||||
|
||||
var name,
|
||||
value;
|
||||
|
||||
if (equalIndex != -1) {
|
||||
name = strings.trim(attr.substring(partStart, equalIndex));
|
||||
value = attr.substring(equalIndex+1, endIndex);
|
||||
}
|
||||
else {
|
||||
if (defaultName) {
|
||||
name = defaultName;
|
||||
value = attr.substring(partStart, endIndex);
|
||||
if (!strings.trim(value).length) {
|
||||
return; //ignore empty parts
|
||||
}
|
||||
}
|
||||
else {
|
||||
name = attr.substring(partStart, endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (name) {
|
||||
name = strings.trim(name);
|
||||
}
|
||||
|
||||
if (!strings.trim(name).length && !strings.trim(value).length) {
|
||||
equalIndex = -1;
|
||||
return; //ignore empty parts
|
||||
}
|
||||
|
||||
if (types) {
|
||||
var type = types[name] || types['*'];
|
||||
if (type) {
|
||||
if (value != null) {
|
||||
value = TypeConverter.convert(value, type.type, type.allowExpressions !== false);
|
||||
}
|
||||
if (type.name) {
|
||||
name = type.name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleError('Invalid sub-attribute name of "' + name + '"');
|
||||
}
|
||||
}
|
||||
|
||||
if (name && removeDashes) {
|
||||
name = name.replace(/-([a-z])/g, function(match, lower) {
|
||||
return lower.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
if (ordered) {
|
||||
result.push({name: name, value: value});
|
||||
}
|
||||
else {
|
||||
result[name] = value;
|
||||
}
|
||||
|
||||
equalIndex = -1; //Reset the equal index
|
||||
};
|
||||
|
||||
/*
|
||||
* Keep searching the string for the relevant tokens.
|
||||
*
|
||||
* NOTE: The regular expression will also return matches for JavaScript strings,
|
||||
* but they are just ignored. This ensures that semicolons inside strings
|
||||
* are not treated as
|
||||
*/
|
||||
while((matches = regExp.exec(attr))) {
|
||||
//console.error(matches[0]);
|
||||
|
||||
if (matches[0] == ';') {
|
||||
finishPart(matches.index);
|
||||
partStart = matches.index+1;
|
||||
equalIndex = -1;
|
||||
}
|
||||
else if (matches[0] == '=') {
|
||||
if (equalIndex == -1) {
|
||||
equalIndex = matches.index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finishPart(attr.length);
|
||||
|
||||
//console.error("AttributeSplitter - result: ", result);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return AttributeSplitter;
|
||||
});
|
||||
406
lib/compiler/ElementNode.js
Normal file
406
lib/compiler/ElementNode.js
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/ElementNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var forEachEntry = raptor.forEachEntry,
|
||||
forEach = raptor.forEach,
|
||||
objects = require('raptor/objects'),
|
||||
escapeXmlAttr = require('raptor/xml/utils').escapeXmlAttr,
|
||||
XML_URI = 'http://www.w3.org/XML/1998/namespace',
|
||||
XML_URI_ALT = 'http://www.w3.org/XML/1998/namespace',
|
||||
ExpressionParser = require('raptor/templating/compiler/ExpressionParser');
|
||||
|
||||
var ElementNode = function(localName, uri, prefix) {
|
||||
ElementNode.superclass.constructor.call(this, 'element');
|
||||
|
||||
if (!this._elementNode) {
|
||||
this._elementNode = true;
|
||||
|
||||
this.dynamicAttributesExpression = null;
|
||||
this.attributesByNS = {};
|
||||
|
||||
this.prefix = prefix;
|
||||
this.localName = this.tagName = localName;
|
||||
this.uri = uri;
|
||||
|
||||
this.allowSelfClosing = false;
|
||||
this.startTagOnly = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ElementNode.prototype = {
|
||||
|
||||
getQName: function() {
|
||||
return this.localName ? (this.prefix ? this.prefix + ":" : "") + this.localName : null;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param startTagOnly
|
||||
*/
|
||||
setStartTagOnly: function(startTagOnly) {
|
||||
this.startTagOnly = true;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowSelfClosing
|
||||
*/
|
||||
setAllowSelfClosing: function(allowSelfClosing) {
|
||||
this.allowSelfClosing = allowSelfClosing;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isElementNode: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isTextNode: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
getAllAttributes: function() {
|
||||
var allAttrs = [];
|
||||
forEachEntry(this.attributesByNS, function(uri, attrs) {
|
||||
forEachEntry(attrs, function(name, attr) {
|
||||
allAttrs.push(attr);
|
||||
});
|
||||
}, this);
|
||||
|
||||
return allAttrs;
|
||||
},
|
||||
|
||||
forEachAttributeAnyNS: function(callback, thisObj) {
|
||||
forEachEntry(this.attributesByNS, function(uri, attrs) {
|
||||
forEachEntry(attrs, function(name, attr) {
|
||||
callback.call(thisObj, attr);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
forEachAttributeNS: function(uri, callback, thisObj) {
|
||||
var attrs = this.attributesByNS[uri || ''];
|
||||
if (attrs) {
|
||||
forEachEntry(attrs, function(name, attr) {
|
||||
callback.call(thisObj, attr);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
getAttributes: function() {
|
||||
var attributes = [];
|
||||
forEachEntry(this.attributes, function(name, attr) {
|
||||
attributes.push(attr);
|
||||
}, this);
|
||||
return attributes;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
getAttribute: function(name) {
|
||||
return this.getAttributeNS(null, name);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uri
|
||||
* @param localName
|
||||
* @returns
|
||||
*/
|
||||
getAttributeNS: function(uri, localName) {
|
||||
var attrNS = this.attributesByNS[uri || ''];
|
||||
var attr = attrNS ? attrNS[localName] : undefined;
|
||||
return attr ? attr.value : undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param localName
|
||||
* @param value
|
||||
*/
|
||||
setAttribute: function(localName, value, escapeXml) {
|
||||
this.setAttributeNS(null, localName, value, null, escapeXml);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uri
|
||||
* @param localName
|
||||
* @param value
|
||||
* @param prefix
|
||||
*/
|
||||
setAttributeNS: function(uri, localName, value, prefix, escapeXml) {
|
||||
|
||||
var attrNS = this.attributesByNS[uri || ''] || (this.attributesByNS[uri || ''] = {});
|
||||
|
||||
attrNS[localName] = {
|
||||
localName: localName,
|
||||
value: value,
|
||||
prefix: prefix,
|
||||
uri: uri,
|
||||
escapeXml: escapeXml,
|
||||
qName: prefix ? (prefix + ":" + localName) : localName,
|
||||
name: uri ? (uri + ":" + localName) : localName,
|
||||
toString: function() {
|
||||
return this.name;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
setEmptyAttribute: function(name) {
|
||||
this.setAttribute(name, null);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param localName
|
||||
*/
|
||||
removeAttribute: function(localName) {
|
||||
this.removeAttributeNS(null, localName);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uri
|
||||
* @param localName
|
||||
*/
|
||||
removeAttributeNS: function(uri, localName) {
|
||||
var attrNS = this.attributesByNS[uri || ''] || (this.attributesByNS[uri || ''] = {});
|
||||
if (attrNS) {
|
||||
delete attrNS[localName];
|
||||
if (objects.isEmpty(attrNS)) {
|
||||
delete this.attributesByNS[uri || ''];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeAttributesNS: function(uri) {
|
||||
delete this.attributesByNS[uri || ''];
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isPreserveWhitespace: function() {
|
||||
var preserveSpace = ElementNode.superclass.isPreserveWhitespace.call(this);
|
||||
|
||||
if (preserveSpace === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var preserveAttr = this.getAttributeNS(XML_URI, "space") || this.getAttributeNS(XML_URI_ALT, "space") || this.getAttribute("xml:space") === "preserve";
|
||||
if (preserveAttr === 'preserve') {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return preserveSpace;
|
||||
},
|
||||
|
||||
hasAttributesAnyNS: function() {
|
||||
return !objects.isEmpty(this.attributesByNS);
|
||||
},
|
||||
|
||||
hasAttributes: function() {
|
||||
return this.hasAttributesNS('');
|
||||
},
|
||||
|
||||
hasAttributesNS: function(uri) {
|
||||
return this.attributesByNS[uri || ''] !== undefined;
|
||||
},
|
||||
|
||||
hasAttribute: function(localName) {
|
||||
return this.hasAttributeNS('', localName);
|
||||
},
|
||||
|
||||
hasAttributeNS: function(uri, localName) {
|
||||
var attrsNS = this.attributesByNS[uri || ''];
|
||||
return attrsNS ? attrsNS.hasOwnProperty(localName) : false;
|
||||
},
|
||||
|
||||
removePreserveSpaceAttr: function() {
|
||||
this.removeAttributeNS(XML_URI, "space");
|
||||
this.removeAttributeNS(XML_URI_ALT, "space");
|
||||
this.removeAttribute("space");
|
||||
},
|
||||
|
||||
setStripExpression: function(stripExpression) {
|
||||
this.stripExpression = stripExpression;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param template
|
||||
*/
|
||||
doGenerateCode: function(template) {
|
||||
this.generateBeforeCode(template);
|
||||
this.generateCodeForChildren(template);
|
||||
this.generateAfterCode(template);
|
||||
},
|
||||
|
||||
generateBeforeCode: function(template) {
|
||||
var preserveWhitespace = this.preserveWhitespace = this.isPreserveWhitespace();
|
||||
|
||||
var name = this.prefix ? (this.prefix + ":" + this.localName) : this.localName;
|
||||
|
||||
if (preserveWhitespace) {
|
||||
this.removePreserveSpaceAttr();
|
||||
}
|
||||
|
||||
template.text("<" + name);
|
||||
|
||||
this.forEachAttributeAnyNS(function(attr) {
|
||||
var prefix = attr.prefix;
|
||||
if (!prefix && attr.uri) {
|
||||
prefix = this.resolveNamespacePrefix(attr.uri);
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
name = prefix + (attr.localName ? (":" + attr.localName) : "");
|
||||
}
|
||||
else {
|
||||
name = attr.localName;
|
||||
}
|
||||
|
||||
if (attr.value === null || attr.value === undefined) {
|
||||
template.text(' ' + name);
|
||||
}
|
||||
else if (template.isExpression(attr.value)) {
|
||||
|
||||
template.attr(name, attr.value, attr.escapeXml !== false);
|
||||
}
|
||||
else {
|
||||
|
||||
var attrParts = [],
|
||||
hasExpression = false,
|
||||
invalidAttr = false;
|
||||
|
||||
ExpressionParser.parse(
|
||||
attr.value,
|
||||
{
|
||||
text: function(text, escapeXml) {
|
||||
attrParts.push({
|
||||
text: text,
|
||||
escapeXml: escapeXml !== false
|
||||
});
|
||||
},
|
||||
expression: function(expression, escapeXml) {
|
||||
hasExpression = true;
|
||||
|
||||
attrParts.push({
|
||||
expression: expression,
|
||||
escapeXml: escapeXml !== false
|
||||
});
|
||||
},
|
||||
error: function(message) {
|
||||
invalidAttr = true;
|
||||
this.addError('Invalid expression found in attribute "' + name + '". ' + message);
|
||||
}
|
||||
},
|
||||
this);
|
||||
if (invalidAttr) {
|
||||
template.text(name + '="' + escapeXmlAttr(attr.value) + '"');
|
||||
}
|
||||
else {
|
||||
if (hasExpression && attrParts.length === 1) {
|
||||
template.attr(name, attrParts[0].expression, attrParts[0].escapeXml !== false);
|
||||
}
|
||||
else {
|
||||
template.text(' ' + name + '="');
|
||||
forEach(attrParts, function(part) {
|
||||
if (part.text) {
|
||||
template.text(part.escapeXml !== false ? escapeXmlAttr(part.text) : part.text);
|
||||
}
|
||||
else if (part.expression) {
|
||||
template.write(part.expression, {escapeXmlAttr: part.escapeXml !== false});
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error("Illegal state"));
|
||||
}
|
||||
});
|
||||
template.text('"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
if (this.dynamicAttributesExpression) {
|
||||
template.attrs(this.dynamicAttributesExpression);
|
||||
}
|
||||
|
||||
if (this.hasChildren()) {
|
||||
template.text(">");
|
||||
}
|
||||
else {
|
||||
if (this.startTagOnly) {
|
||||
template.text(">");
|
||||
}
|
||||
else if (this.allowSelfClosing) {
|
||||
template.text("/>");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
generateAfterCode: function(template) {
|
||||
var name = this.prefix ? (this.prefix + ":" + this.localName) : this.localName;
|
||||
|
||||
if (this.hasChildren()) {
|
||||
template.text("</" + name + ">");
|
||||
}
|
||||
else {
|
||||
if (!this.startTagOnly && !this.allowSelfClosing) {
|
||||
template.text("></" + name + ">");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return "<" + (this.prefix ? (this.prefix + ":" + this.localName) : this.localName) + ">";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ElementNode;
|
||||
});
|
||||
20
lib/compiler/EscapeXmlContext.js
Normal file
20
lib/compiler/EscapeXmlContext.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Enum(
|
||||
'raptor/templating/compiler/EscapeXmlContext',
|
||||
['Element',
|
||||
'Attribute']);
|
||||
69
lib/compiler/Expression.js
Normal file
69
lib/compiler/Expression.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/Expression',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var operatorsRegExp = /"(?:[^"]|\\")*"|'(?:[^']|\\')*'|\s+(?:and|or|lt|gt|eq|ne|lt|gt|ge|le)\s+/g,
|
||||
strings = require('raptor/strings'),
|
||||
replacements = {
|
||||
"and": " && ",
|
||||
"or": " || ",
|
||||
"eq": " === ",
|
||||
"ne": " !== ",
|
||||
"lt": " < ",
|
||||
"gt": " > ",
|
||||
"ge": " >= ",
|
||||
"le": " <= "
|
||||
},
|
||||
handleBinaryOperators = function(str) {
|
||||
return str.replace(operatorsRegExp, function(match) {
|
||||
return replacements[strings.trim(match)] || match;
|
||||
});
|
||||
};
|
||||
|
||||
var Expression = function(expression, replaceSpecialOperators) {
|
||||
if (expression == null) {
|
||||
throw raptor.createError(new Error("expression argument is required"));
|
||||
}
|
||||
|
||||
if (replaceSpecialOperators !== false && typeof expression === 'string') {
|
||||
expression = handleBinaryOperators(expression);
|
||||
}
|
||||
this.expression = expression;
|
||||
};
|
||||
|
||||
Expression.prototype = {
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
getExpression: function() {
|
||||
return this.expression;
|
||||
},
|
||||
|
||||
/**
|
||||
*/
|
||||
toString: function() {
|
||||
return this.expression.toString();
|
||||
}
|
||||
};
|
||||
|
||||
return Expression;
|
||||
});
|
||||
550
lib/compiler/ExpressionParser.js
Normal file
550
lib/compiler/ExpressionParser.js
Normal file
@ -0,0 +1,550 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/ExpressionParser',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
|
||||
var Expression = require('raptor/templating/compiler/Expression'),
|
||||
strings = require('raptor/strings'),
|
||||
stringify = require('raptor/json/stringify'),
|
||||
regexp = require('raptor/regexp'),
|
||||
endingTokens = {
|
||||
// "{": "}",
|
||||
"${": "}",
|
||||
"$!{": "}",
|
||||
"{%": "%}",
|
||||
"{?": "}",
|
||||
"$": null,
|
||||
"$!": null
|
||||
},
|
||||
createStartRegExpStr = function(starts) {
|
||||
var parts = [];
|
||||
|
||||
raptor.forEach(starts, function(start) {
|
||||
parts.push(regexp.escape("\\\\" + start));
|
||||
parts.push(regexp.escape("\\" + start));
|
||||
parts.push(regexp.escape(start));
|
||||
});
|
||||
|
||||
return parts.join("|");
|
||||
},
|
||||
startRegExpStr = createStartRegExpStr(["{%", "${", "$!{", "$!", "$", "{?"]),
|
||||
createStartRegExp = function() {
|
||||
return new RegExp(startRegExpStr, "g");
|
||||
},
|
||||
getLine = function(str, pos) {
|
||||
var lines = str.split("\n");
|
||||
var index = 0;
|
||||
|
||||
var line;
|
||||
|
||||
while (index < lines.length) {
|
||||
line = lines[index];
|
||||
if (pos - line.length+1 < 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
pos -= line.length+1;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return {
|
||||
str: line,
|
||||
pos: pos
|
||||
};
|
||||
},
|
||||
errorContext = function(str, pos, length) {
|
||||
|
||||
var line = getLine(str, pos);
|
||||
pos = line.pos;
|
||||
str = line.str;
|
||||
|
||||
var start = pos - length,
|
||||
end = pos + length,
|
||||
i;
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (end > str.length) {
|
||||
end = str.length;
|
||||
}
|
||||
|
||||
var prefix = "...";
|
||||
var suffix = "...";
|
||||
|
||||
var context = "\n" + prefix + str.substring(start, end) + suffix + "\n";
|
||||
for (i=0; i<prefix.length; i++) {
|
||||
context += " ";
|
||||
}
|
||||
for (i=start; i<end; i++) {
|
||||
context += i === pos ? "^" : " ";
|
||||
}
|
||||
for (i=0; i<suffix.length; i++) {
|
||||
context += " ";
|
||||
}
|
||||
return context;
|
||||
},
|
||||
getConditionalExpression = function(expression) {
|
||||
var tokensRegExp = /"(?:[^"]|\\")*"|'(?:[^']|\\')*'|\\\\;|\\;|[\{\};]/g,
|
||||
matches,
|
||||
depth = 0;
|
||||
|
||||
var parts = [],
|
||||
partStart = 0;
|
||||
|
||||
while((matches = tokensRegExp.exec(expression))) {
|
||||
if (matches[0] === '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
else if (matches[0] === '}') {
|
||||
if (depth !== 0) {
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (matches[0] === '\\\\;') {
|
||||
/*
|
||||
* 1) Convert \\; --> \;
|
||||
* 2) Start searching again after the single slash
|
||||
*/
|
||||
expression = expression.substring(0, matches.index) + '\\;' + expression.substring(tokensRegExp.lastIndex);
|
||||
tokensRegExp.lastIndex = matches.index + 1;
|
||||
continue;
|
||||
}
|
||||
else if (matches[0] === '\\;') {
|
||||
/*
|
||||
* 1) Convert \; --> ;
|
||||
* 2) Start searching again after the semicolon
|
||||
*/
|
||||
expression = expression.substring(0, matches.index) + ';' + expression.substring(tokensRegExp.lastIndex);
|
||||
tokensRegExp.lastIndex = matches.index + 1;
|
||||
continue;
|
||||
}
|
||||
else if (matches[0] === ';') {
|
||||
if (depth === 0) {
|
||||
parts.push(expression.substring(partStart, matches.index));
|
||||
partStart = tokensRegExp.lastIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partStart < expression.length) {
|
||||
parts.push(expression.substring(partStart));
|
||||
}
|
||||
|
||||
|
||||
var getExpression = function(part) {
|
||||
var expressionParts = [];
|
||||
|
||||
ExpressionParser.parse(part, {
|
||||
text: function(text) {
|
||||
expressionParts.push(stringify(text));
|
||||
},
|
||||
|
||||
expression: function(expression) {
|
||||
expressionParts.push(expression);
|
||||
}
|
||||
});
|
||||
|
||||
return expressionParts.join('+');
|
||||
};
|
||||
|
||||
if (parts.length === 1) {
|
||||
return "(" + parts[0] + " ? " + 'null' + " : '')";
|
||||
}
|
||||
else if (parts.length === 2) {
|
||||
return "(" + parts[0] + " ? " + getExpression(parts[1]) + " : '')";
|
||||
}
|
||||
else if (parts.length === 3) {
|
||||
return "(" + parts[0] + " ? " + getExpression(parts[1]) + " : " + getExpression(parts[2]) + ")";
|
||||
}
|
||||
else {
|
||||
throw new Error('Invalid simple conditional of "' + expression + '". Simple conditionals should be in the form {?<expression>;<true-template>[;<false-template>]}');
|
||||
}
|
||||
|
||||
},
|
||||
processNestedStrings = function(expression, foundStrings) {
|
||||
|
||||
var hasExpression,
|
||||
parts,
|
||||
handleText = function(text) {
|
||||
parts.push(foundString.quote + text + foundString.quote);
|
||||
},
|
||||
handleExpression = function(expression) {
|
||||
hasExpression = true;
|
||||
parts.push(expression);
|
||||
};
|
||||
|
||||
for (var i=foundStrings.length-1, foundString; i>=0; i--) {
|
||||
foundString = foundStrings[i];
|
||||
|
||||
if (!foundString.value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hasExpression = false;
|
||||
parts = [];
|
||||
|
||||
ExpressionParser.parse(foundString.value, {
|
||||
text: handleText,
|
||||
expression: handleExpression
|
||||
});
|
||||
|
||||
if (hasExpression) {
|
||||
expression = expression.substring(0, foundString.start) + "(" + parts.join('+') + ")" + expression.substring(foundString.end);
|
||||
}
|
||||
}
|
||||
|
||||
return expression;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
var ExpressionParserHelper = define.Class(function() {
|
||||
return {
|
||||
init: function(callback, callbackThisObj) {
|
||||
this.callback = callback;
|
||||
this.callbackThisObj = callbackThisObj;
|
||||
|
||||
this.prevText = null;
|
||||
this.prevEscapeXml = null;
|
||||
},
|
||||
|
||||
_invokeCallback: function(name, value, escapeXml) {
|
||||
if (!this.callback[name]) {
|
||||
throw raptor.createError(new Error(name + " not allowed: " + value));
|
||||
}
|
||||
|
||||
this.callback[name].call(this.callbackThisObj, value, escapeXml);
|
||||
},
|
||||
|
||||
_endText: function() {
|
||||
if (this.prevText !== null) {
|
||||
this._invokeCallback("text", this.prevText, this.prevEscapeXml);
|
||||
this.prevText = null;
|
||||
this.prevEscapeXml = null;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param newText
|
||||
* @returns
|
||||
*/
|
||||
addXmlText: function(xmlText) {
|
||||
this.addText(xmlText, false);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param newText
|
||||
* @returns
|
||||
*/
|
||||
addText: function(text, escapeXml) {
|
||||
|
||||
if (this.prevText !== null && this.prevEscapeXml === escapeXml) {
|
||||
this.prevText += text;
|
||||
}
|
||||
else {
|
||||
this._endText();
|
||||
this.prevText = text;
|
||||
this.prevEscapeXml = escapeXml;
|
||||
}
|
||||
},
|
||||
|
||||
addXmlExpression: function(expression, escapeXml) {
|
||||
this.addExpression(expression, false);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param expression
|
||||
* @returns
|
||||
*/
|
||||
addExpression: function(expression, escapeXml) {
|
||||
this._endText();
|
||||
|
||||
if (!(expression instanceof Expression)) {
|
||||
expression = new Expression(expression);
|
||||
}
|
||||
|
||||
this._invokeCallback("expression", expression, escapeXml !== false);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scriptlet
|
||||
* @returns
|
||||
*/
|
||||
addScriptlet: function(scriptlet) {
|
||||
this._endText();
|
||||
|
||||
this._invokeCallback("scriptlet", scriptlet);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var ExpressionParser = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @memberOf raptor/templating/compiler$ExpressionParser
|
||||
*
|
||||
* @param str
|
||||
* @param callback
|
||||
* @param thisObj
|
||||
*/
|
||||
ExpressionParser.parse = function(str, callback, thisObj, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var textStart = 0, //The index of the start of the next text block
|
||||
textEnd, //The index of the current text block
|
||||
startMatches, //The matches found when searching for the possible start tokens
|
||||
endMatches, //The matches found when searching through special expression tokens
|
||||
expressionStart, //The index of the start of the current expression
|
||||
expression, //The current expression string
|
||||
isScriptlet, //If true, then the expression is a scriptlet,
|
||||
isConditional, //If true, then the expression is a conditional (i.e. {?<expression>;<true-template>[;<false-template>]}
|
||||
startToken, //The start token for the current expression
|
||||
custom = options.custom || {},
|
||||
handleError = function(message) {
|
||||
if (callback.error) {
|
||||
callback.error.call(thisObj, message);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error(message));
|
||||
}
|
||||
};
|
||||
|
||||
var startRegExp = createStartRegExp();
|
||||
|
||||
var helper = new ExpressionParserHelper(callback, thisObj);
|
||||
|
||||
startRegExp.lastIndex = 0;
|
||||
|
||||
/*
|
||||
* Look for any of the possible start tokens (including the escaped and double-escaped versions)
|
||||
*/
|
||||
outer:
|
||||
while((startMatches = startRegExp.exec(str))) {
|
||||
|
||||
if (strings.startsWith(startMatches[0], "\\\\")) { // \\${
|
||||
/*
|
||||
* We found a double-escaped start token.
|
||||
*
|
||||
* We found a start token that is preceeded by an escaped backslash...
|
||||
* The start token is a valid start token preceded by an escaped
|
||||
* backslash. Add a single black slash and handle the expression
|
||||
*/
|
||||
textEnd = startMatches.index + 1; //Include everything up to and include the first backslash as part of the text
|
||||
startToken = startMatches[0].substring(2); //Record the start token
|
||||
expressionStart = startMatches.index + startMatches[0].length; //The expression starts after the start token
|
||||
}
|
||||
else if (strings.startsWith(startMatches[0], "\\")) { // \${
|
||||
/*
|
||||
* We found a start token that is escaped. We should
|
||||
* add the unescaped start token to the text output.
|
||||
*/
|
||||
helper.addText(str.substring(textStart, startMatches.index)); //Add everything preceeding the start token
|
||||
helper.addText(startMatches[0].substring(1)); //Add the start token excluding the initial escape character
|
||||
textStart = startRegExp.lastIndex; // The next text block we find will be after this match
|
||||
continue;
|
||||
}
|
||||
else if (endingTokens.hasOwnProperty(startMatches[0])) {
|
||||
/*
|
||||
* We found a valid start token
|
||||
*/
|
||||
startToken = startMatches[0]; //Record the start token
|
||||
textEnd = startMatches.index; //The text ends where the start token begins
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error("Illegal state. Unexpected start token: " + startMatches[0]));
|
||||
}
|
||||
|
||||
expressionStart = startRegExp.lastIndex; //Expression starts where the start token ended
|
||||
|
||||
if (textStart !== textEnd) { //If there was any text between expressions then add it now
|
||||
helper.addText(str.substring(textStart, textEnd));
|
||||
}
|
||||
|
||||
var endToken = endingTokens[startToken]; //Look up the end token
|
||||
if (!endToken) { //Check if the start token has an end token... not all start tokens do. For example: $myVar
|
||||
var variableRegExp = /^([_a-zA-Z]\w*(?:\.[_a-zA-Z]\w*)*)/g;
|
||||
variableRegExp.lastIndex = 0;
|
||||
var variableMatches = variableRegExp.exec(str.substring(expressionStart)); //Find the variable name that follows the starting "$" token
|
||||
|
||||
if (!variableMatches) { //We did not find a valid variable name after the starting "$" token
|
||||
//handleError('Invalid simple variable expression. Location: ' + errorContext(str, expressionStart, 10)); //TODO: Provide a more helpful error message
|
||||
helper.addText(startMatches[0]);
|
||||
startRegExp.lastIndex = textStart = expressionStart;
|
||||
continue outer;
|
||||
}
|
||||
|
||||
var varName = variableMatches[1];
|
||||
if (startToken === '$!') {
|
||||
helper.addXmlExpression(varName); //Add the variable as an expression
|
||||
}
|
||||
else {
|
||||
helper.addExpression(varName); //Add the variable as an expression
|
||||
}
|
||||
|
||||
startRegExp.lastIndex = textStart = expressionStart = expressionStart + varName.length;
|
||||
|
||||
continue outer;
|
||||
}
|
||||
|
||||
|
||||
isScriptlet = startToken === "{%";
|
||||
isConditional = startToken === '{?';
|
||||
|
||||
var endRegExp = /"((?:[^"]|\\")*)"|'((?:[^']|\\')*)'|\%\}|[\{\}]/g;
|
||||
//Now we need to find the ending curly
|
||||
endRegExp.lastIndex = expressionStart; //Start searching from where the expression begins
|
||||
|
||||
var depth = 0;
|
||||
var foundStrings = [];
|
||||
var handler;
|
||||
|
||||
while((endMatches = endRegExp.exec(str))) {
|
||||
if (endMatches[0] === '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
else if (endMatches[0] === '}') {
|
||||
if (isScriptlet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depth !== 0) {
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (endMatches[0] === '%}') {
|
||||
if (!isScriptlet) {
|
||||
handleError('Ending "' + endMatches[0] + '" token was found but matched with starting "' + startToken + '" token. Location: ' + errorContext(str, endMatches.index, 10));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (endMatches[0].charAt(0) === "'" || endMatches[0].charAt(0) === '"') {
|
||||
foundStrings.push({
|
||||
start: endMatches.index - expressionStart,
|
||||
end: endMatches.index + endMatches[0].length - expressionStart,
|
||||
value: endMatches[0].slice(1,-1),
|
||||
json: endMatches[0],
|
||||
quote: endMatches[0].charAt(0)
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//console.log("EXPRESSION: " + str.substring(firstCurly+1, endMatches.index));
|
||||
expression = str.substring(expressionStart, endMatches.index);
|
||||
|
||||
|
||||
handler = null;
|
||||
|
||||
if (startToken === "${") {
|
||||
var firstColon = expression.indexOf(":"),
|
||||
customType;
|
||||
if (firstColon != -1) {
|
||||
customType = expression.substring(0, firstColon);
|
||||
|
||||
handler = custom[customType] || ExpressionParser.custom[customType];
|
||||
if (handler) {
|
||||
handler.call(ExpressionParser, expression.substring(firstColon+1), helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
if (isScriptlet) {
|
||||
helper.addScriptlet(expression);
|
||||
}
|
||||
else if (isConditional) {
|
||||
helper.addExpression(getConditionalExpression(expression));
|
||||
}
|
||||
else {
|
||||
|
||||
if (foundStrings.length > 0) {
|
||||
expression = processNestedStrings(expression, foundStrings);
|
||||
}
|
||||
|
||||
if (startToken === '$!{') {
|
||||
helper.addXmlExpression(expression);
|
||||
}
|
||||
else {
|
||||
helper.addExpression(expression);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
startRegExp.lastIndex = endRegExp.lastIndex; //Start searching from where the end token ended
|
||||
textStart = endRegExp.lastIndex;
|
||||
|
||||
//console.log('Found ending curly. Start index now: ' + searchStart);
|
||||
continue outer;
|
||||
|
||||
}
|
||||
|
||||
handleError('Ending "' + endingTokens[startToken] + '" token not found for "' + startToken + '" token. Location: ' + errorContext(str, startMatches.index, 10) + "\n");
|
||||
}
|
||||
|
||||
if (textStart !== str.length) {
|
||||
helper.addText(str.substring(textStart, str.length));
|
||||
}
|
||||
|
||||
helper._endText();
|
||||
};
|
||||
|
||||
ExpressionParser.custom = {
|
||||
"xml": function(expression, helper) {
|
||||
helper.addXmlExpression(new Expression(expression));
|
||||
},
|
||||
"entity": function(expression, helper) {
|
||||
helper.addXmlText("&" + expression + ";");
|
||||
},
|
||||
"startTag": function(expression, helper) {
|
||||
helper.addXmlText("<" + expression + ">");
|
||||
},
|
||||
"endTag": function(expression, helper) {
|
||||
helper.addXmlText("</" + expression + ">");
|
||||
},
|
||||
"newline": function(expression, helper) {
|
||||
helper.addText("\n");
|
||||
}
|
||||
};
|
||||
|
||||
return ExpressionParser;
|
||||
});
|
||||
738
lib/compiler/Node.js
Normal file
738
lib/compiler/Node.js
Normal file
@ -0,0 +1,738 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var forEachEntry = raptor.forEachEntry,
|
||||
forEach = raptor.forEach,
|
||||
isArray = Array.isArray,
|
||||
isEmpty = require('raptor/objects').isEmpty;
|
||||
|
||||
var Node = function(nodeType) {
|
||||
if (!this.nodeType) {
|
||||
this._isRoot = false;
|
||||
this.preserveWhitespace = null;
|
||||
this.wordWrapEnabled = null;
|
||||
this.escapeXmlBodyText = null;
|
||||
this.escapeXmlContext = null;
|
||||
this.nodeType = nodeType;
|
||||
this.parentNode = null;
|
||||
this.previousSibling = null;
|
||||
this.nextSibling = null;
|
||||
this.firstChild = null;
|
||||
this.lastChild = null;
|
||||
this.namespaceMappings = {};
|
||||
this.prefixMappings = {};
|
||||
this.transformersApplied = {};
|
||||
this.properties = {};
|
||||
}
|
||||
};
|
||||
|
||||
Node.prototype = {
|
||||
setRoot: function(isRoot) {
|
||||
this._isRoot = isRoot;
|
||||
},
|
||||
|
||||
getPosition: function() {
|
||||
var pos = this.pos || this.getProperty("pos") || {
|
||||
toString: function() {
|
||||
return "(unknown position)";
|
||||
}
|
||||
};
|
||||
|
||||
return pos;
|
||||
},
|
||||
|
||||
setPosition: function(pos) {
|
||||
this.pos = pos;
|
||||
},
|
||||
|
||||
addError: function(error) {
|
||||
var compiler = this.compiler,
|
||||
curNode = this;
|
||||
|
||||
while (curNode != null && !compiler) {
|
||||
compiler = curNode.compiler;
|
||||
if (compiler) {
|
||||
break;
|
||||
}
|
||||
curNode = curNode.parentNode;
|
||||
}
|
||||
|
||||
if (!compiler) {
|
||||
throw raptor.createError(new Error("Template compiler not set for node " + this));
|
||||
}
|
||||
var pos = this.getPosition();
|
||||
compiler.addError(error + " (" + this.toString() + ")", pos);
|
||||
},
|
||||
|
||||
setProperty: function(name, value) {
|
||||
this.setPropertyNS(null, name, value);
|
||||
},
|
||||
|
||||
setPropertyNS: function(uri, name, value) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
var namespacedProps = this.properties[uri];
|
||||
if (!namespacedProps) {
|
||||
namespacedProps = this.properties[uri] = {};
|
||||
}
|
||||
namespacedProps[name] = value;
|
||||
},
|
||||
|
||||
setProperties: function(props) {
|
||||
this.setPropertiesNS(null, props);
|
||||
},
|
||||
|
||||
setPropertiesNS: function(uri, props) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
|
||||
forEachEntry(props, function(name, value) {
|
||||
this.setPropertyNS(uri, name, value);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getPropertyNamespaces: function() {
|
||||
return Object.keys(this.properties);
|
||||
},
|
||||
|
||||
getProperties: function(uri) {
|
||||
return this.getPropertiesNS(null);
|
||||
},
|
||||
|
||||
hasProperty: function(name) {
|
||||
return this.hasPropertyNS('', name);
|
||||
},
|
||||
|
||||
hasPropertyNS: function(uri, name) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
var namespaceProps = this.properties[uri];
|
||||
return namespaceProps.hasOwnProperty(name);
|
||||
},
|
||||
|
||||
getPropertiesByNS: function() {
|
||||
return this.properties;
|
||||
},
|
||||
|
||||
getPropertiesNS: function(uri) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
|
||||
return this.properties[uri];
|
||||
},
|
||||
|
||||
forEachProperty: function(callback, thisObj) {
|
||||
forEachEntry(this.properties, function(uri, properties) {
|
||||
forEachEntry(properties, function(name, value) {
|
||||
callback.call(thisObj, uri, name, value);
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getProperty: function(name) {
|
||||
return this.getPropertyNS(null, name);
|
||||
},
|
||||
|
||||
getPropertyNS: function(uri, name) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
|
||||
var namespaceProps = this.properties[uri];
|
||||
return namespaceProps ? namespaceProps[name] : undefined;
|
||||
},
|
||||
|
||||
removeProperty: function(name) {
|
||||
this.removePropertyNS("", name);
|
||||
},
|
||||
|
||||
removePropertyNS: function(uri, name) {
|
||||
if (!uri) {
|
||||
uri = "";
|
||||
}
|
||||
var namespaceProps = this.properties[uri];
|
||||
if (namespaceProps) {
|
||||
delete namespaceProps[name];
|
||||
}
|
||||
|
||||
if (isEmpty(namespaceProps)) {
|
||||
delete this.properties[uri];
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
removePropertiesNS: function(uri) {
|
||||
delete this.properties[uri];
|
||||
},
|
||||
|
||||
forEachPropertyNS: function(uri, callback, thisObj) {
|
||||
if (uri == null) {
|
||||
uri = '';
|
||||
}
|
||||
|
||||
var props = this.properties[uri];
|
||||
if (props) {
|
||||
forEachEntry(props, function(name, value) {
|
||||
callback.call(thisObj, name, value);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
forEachChild: function(callback, thisObj) {
|
||||
if (!this.firstChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the child linked list to an array so that
|
||||
* if the callback code manipulates the child nodes
|
||||
* looping won't break
|
||||
*/
|
||||
var children = [];
|
||||
|
||||
var curChild = this.firstChild;
|
||||
while(curChild) {
|
||||
children.push(curChild);
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
|
||||
for (var i=0, len=children.length; i<len; i++) {
|
||||
curChild = children[i];
|
||||
if (curChild.parentNode === this) {
|
||||
//Make sure the node is still a child of this node
|
||||
if (false === callback.call(thisObj, curChild)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getExpression: function(template, childrenOnly, escapeXml, asFunction) {
|
||||
if (!template) {
|
||||
throw raptor.createError(new Error("template argument is required"));
|
||||
}
|
||||
|
||||
var _this = this;
|
||||
return template.makeExpression({
|
||||
toString: function() {
|
||||
return template.captureCode(function() {
|
||||
if (asFunction) {
|
||||
template.code("function() {\n")
|
||||
.code(template.indentStr(2) + "return context." + (escapeXml !== false ? "captureString" : "c") + "(function() {\n")
|
||||
.indent(3, function() {
|
||||
if (childrenOnly === true) {
|
||||
_this.generateCodeForChildren(template);
|
||||
}
|
||||
else {
|
||||
_this.generateCode(template);
|
||||
}
|
||||
|
||||
})
|
||||
.code(template.indentStr(2) + "});\n")
|
||||
.code(template.indentStr() + "}");
|
||||
}
|
||||
else {
|
||||
template.code("context." + (escapeXml !== false ? "captureString" : "c") + "(function() {\n")
|
||||
.indent(function() {
|
||||
if (childrenOnly === true) {
|
||||
_this.generateCodeForChildren(template);
|
||||
}
|
||||
else {
|
||||
_this.generateCode(template);
|
||||
}
|
||||
|
||||
})
|
||||
.code(template.indentStr() + "})");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getBodyContentExpression: function(template, escapeXml) {
|
||||
return this.getExpression(template, true, escapeXml, false);
|
||||
},
|
||||
|
||||
getBodyContentFunctionExpression: function(template, escapeXml) {
|
||||
return this.getExpression(template, true, escapeXml, true);
|
||||
},
|
||||
|
||||
isTransformerApplied: function(transformer) {
|
||||
return this.transformersApplied[transformer.id] === true;
|
||||
},
|
||||
|
||||
setTransformerApplied: function(transformer) {
|
||||
this.transformersApplied[transformer.id] = true;
|
||||
},
|
||||
|
||||
hasChildren: function() {
|
||||
return this.firstChild != null;
|
||||
},
|
||||
|
||||
appendChild: function(childNode) {
|
||||
|
||||
if (childNode.parentNode) {
|
||||
childNode.parentNode.removeChild(childNode);
|
||||
}
|
||||
|
||||
if (!this.firstChild) {
|
||||
this.firstChild = this.lastChild = childNode;
|
||||
childNode.nextSibling = null;
|
||||
childNode.previousSibling = null;
|
||||
}
|
||||
else {
|
||||
this.lastChild.nextSibling = childNode;
|
||||
childNode.previousSibling = this.lastChild;
|
||||
this.lastChild = childNode;
|
||||
}
|
||||
|
||||
childNode.parentNode = this;
|
||||
},
|
||||
|
||||
appendChildren: function(childNodes) {
|
||||
if (!childNodes) {
|
||||
return;
|
||||
}
|
||||
|
||||
raptor.forEach(childNodes, function(childNode) {
|
||||
this.appendChild(childNode);
|
||||
}, this);
|
||||
},
|
||||
|
||||
isRoot: function() {
|
||||
return this._isRoot === true;
|
||||
},
|
||||
|
||||
detach: function() {
|
||||
if (this.parentNode) {
|
||||
this.parentNode.removeChild(this);
|
||||
}
|
||||
},
|
||||
|
||||
removeChild: function(childNode) {
|
||||
|
||||
if (childNode.parentNode !== this) { //Check if the child node is a child of the parent
|
||||
return null;
|
||||
}
|
||||
|
||||
var previousSibling = childNode.previousSibling,
|
||||
nextSibling = childNode.nextSibling;
|
||||
|
||||
if (this.firstChild === childNode && this.lastChild === childNode) {
|
||||
//The child node is the one and only child node being removed
|
||||
this.firstChild = this.lastChild = null;
|
||||
}
|
||||
else if (this.firstChild === childNode) {
|
||||
//The child node being removed is the first child and there is another child after it
|
||||
this.firstChild = this.firstChild.nextSibling; //Make the next child the first child
|
||||
this.firstChild.previousSibling = null;
|
||||
}
|
||||
else if (this.lastChild === childNode) {
|
||||
//The child node being removed is the last child and there is another child before it
|
||||
this.lastChild = this.lastChild.previousSibling; //Make the previous child the last child
|
||||
this.lastChild.nextSibling = null;
|
||||
}
|
||||
else {
|
||||
previousSibling.nextSibling = nextSibling;
|
||||
nextSibling.previousSibling = previousSibling;
|
||||
}
|
||||
|
||||
//Make sure the removed node is completely detached
|
||||
childNode.parentNode = null;
|
||||
childNode.previousSibling = null;
|
||||
childNode.nextSibling = null;
|
||||
|
||||
return childNode;
|
||||
},
|
||||
|
||||
removeChildren: function() {
|
||||
while (this.firstChild) {
|
||||
this.removeChild(this.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
replaceChild: function(newChild, replacedChild) {
|
||||
|
||||
if (newChild === replacedChild) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!replacedChild) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (replacedChild.parentNode !== this) {
|
||||
return false; //The parent does not have the replacedChild as a child... nothing to do
|
||||
}
|
||||
|
||||
if (this.firstChild === replacedChild && this.lastChild === replacedChild) {
|
||||
this.firstChild = newChild;
|
||||
this.lastChild = newChild;
|
||||
newChild.previousSibling = null;
|
||||
newChild.nextSibling = null;
|
||||
}
|
||||
else if (this.firstChild === replacedChild) {
|
||||
newChild.nextSibling = replacedChild.nextSibling;
|
||||
replacedChild.nextSibling.previousSibling = newChild;
|
||||
this.firstChild = newChild;
|
||||
}
|
||||
else if (this.lastChild === replacedChild) {
|
||||
newChild.previousSibling = replacedChild;
|
||||
replacedChild.previousSibling.nextSibling = newChild;
|
||||
this.lastChild = newChild;
|
||||
}
|
||||
else {
|
||||
replacedChild.nextSibling.previousSibling = newChild;
|
||||
replacedChild.previousSibling.nextSibling = newChild;
|
||||
newChild.nextSibling = replacedChild.nextSibling;
|
||||
newChild.previousSibling = replacedChild.previousSibling;
|
||||
}
|
||||
|
||||
newChild.parentNode = this;
|
||||
|
||||
replacedChild.parentNode = null;
|
||||
replacedChild.previousSibling = null;
|
||||
replacedChild.nextSibling = null;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
insertAfter: function(node, referenceNode) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referenceNode && referenceNode.parentNode !== this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isArray(node)) {
|
||||
raptor.forEach(node, function(node) {
|
||||
this.insertAfter(node, referenceNode);
|
||||
referenceNode = node;
|
||||
}, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node === referenceNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referenceNode === this.lastChild) {
|
||||
this.appendChild(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
if (!referenceNode || referenceNode === this.lastChild) {
|
||||
this.appendChild(node);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
referenceNode.nextSibling.previousSibling = node;
|
||||
node.nextSibling = referenceNode.nextSibling;
|
||||
node.previousSibling = referenceNode;
|
||||
referenceNode.nextSibling = node;
|
||||
}
|
||||
|
||||
node.parentNode = this;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
insertBefore: function(node, referenceNode) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referenceNode && referenceNode.parentNode !== this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isArray(node)) {
|
||||
|
||||
var nodes = node,
|
||||
i;
|
||||
|
||||
for (i=nodes.length-1;i>=0; i--) {
|
||||
this.insertBefore(nodes[i], referenceNode);
|
||||
referenceNode = nodes[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node === referenceNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
if (!referenceNode) {
|
||||
this.appendChild(node);
|
||||
}
|
||||
else if (this.firstChild === referenceNode) {
|
||||
this.firstChild = node;
|
||||
this.firstChild.nextSibling = referenceNode;
|
||||
this.firstChild.previousSibling = null;
|
||||
|
||||
referenceNode.previousSibling = this.firstChild;
|
||||
node.parentNode = this;
|
||||
}
|
||||
else {
|
||||
this.insertAfter(node, referenceNode.previousSibling);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
isTextNode: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
isElementNode: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
setStripExpression: function(stripExpression) {
|
||||
this.stripExpression = stripExpression;
|
||||
},
|
||||
|
||||
generateCode: function(template) {
|
||||
this.compiler = template.compiler;
|
||||
|
||||
var preserveWhitespace = this.isPreserveWhitespace();
|
||||
if (preserveWhitespace == null) {
|
||||
preserveWhitespace = template.options.preserveWhitespace;
|
||||
if (preserveWhitespace === true || (preserveWhitespace && preserveWhitespace["*"])) {
|
||||
this.setPreserveWhitespace(true);
|
||||
}
|
||||
else {
|
||||
this.setPreserveWhitespace(false);
|
||||
}
|
||||
}
|
||||
|
||||
var wordWrapEnabled = this.isWordWrapEnabled();
|
||||
if (wordWrapEnabled == null) {
|
||||
wordWrapEnabled = template.options.wordWrapEnabled;
|
||||
if (wordWrapEnabled !== false) {
|
||||
this.setWordWrapEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isEscapeXmlBodyText() == null) {
|
||||
this.setEscapeXmlBodyText(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!this.stripExpression || this.stripExpression.toString() === 'false') {
|
||||
this.doGenerateCode(template);
|
||||
}
|
||||
else if (this.stripExpression.toString() === 'true') {
|
||||
this.generateCodeForChildren(template);
|
||||
}
|
||||
else {
|
||||
//There is a strip expression
|
||||
if (!this.generateBeforeCode || !this.generateAfterCode) {
|
||||
this.addError("The c:strip directive is not supported for node " + this);
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
var nextStripVarId = template.getAttribute("nextStripVarId");
|
||||
if (nextStripVarId == null) {
|
||||
nextStripVarId = template.setAttribute("nextStripVarId", 0);
|
||||
}
|
||||
var varName = '__strip' + (nextStripVarId++);
|
||||
|
||||
template.statement('var ' + varName + ' = !(' + this.stripExpression + ');');
|
||||
|
||||
template
|
||||
.statement('if (' + varName + ') {')
|
||||
.indent(function() {
|
||||
this.generateBeforeCode(template);
|
||||
}, this)
|
||||
.line("}");
|
||||
|
||||
|
||||
this.generateCodeForChildren(template);
|
||||
|
||||
template
|
||||
.statement('if (' + varName + ') {')
|
||||
.indent(function() {
|
||||
this.generateAfterCode(template);
|
||||
}, this)
|
||||
.line("}");
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError(new Error("Unable to generate code for node " + this + " at position [" + this.getPosition() + "]. Exception: " + e), e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isPreserveWhitespace: function() {
|
||||
return this.preserveWhitespace;
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param preserve
|
||||
*/
|
||||
setPreserveWhitespace: function(preserve) {
|
||||
this.preserveWhitespace = preserve;
|
||||
},
|
||||
|
||||
isWordWrapEnabled: function() {
|
||||
return this.wordWrapEnabled;
|
||||
},
|
||||
|
||||
setWordWrapEnabled: function(enabled) {
|
||||
this.wordWrapEnabled = enabled;
|
||||
},
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
|
||||
this.generateCodeForChildren(template);
|
||||
},
|
||||
|
||||
generateCodeForChildren: function(template, indent) {
|
||||
if (!template) {
|
||||
throw raptor.createError(new Error('The "template" argument is required'));
|
||||
}
|
||||
if (indent === true) {
|
||||
template.incIndent();
|
||||
}
|
||||
|
||||
this.forEachChild(function(childNode) {
|
||||
if (childNode.isPreserveWhitespace() == null) {
|
||||
childNode.setPreserveWhitespace(this.isPreserveWhitespace() === true);
|
||||
}
|
||||
|
||||
if (childNode.isWordWrapEnabled() == null) {
|
||||
childNode.setWordWrapEnabled(this.isWordWrapEnabled() === true);
|
||||
}
|
||||
|
||||
if (childNode.isEscapeXmlBodyText() == null) {
|
||||
childNode.setEscapeXmlBodyText(this.isEscapeXmlBodyText() !== false);
|
||||
}
|
||||
|
||||
if (childNode.getEscapeXmlContext() == null) {
|
||||
childNode.setEscapeXmlContext(this.getEscapeXmlContext() || require('raptor/templating/compiler/EscapeXmlContext').Element);
|
||||
}
|
||||
|
||||
childNode.generateCode(template);
|
||||
}, this);
|
||||
|
||||
if (indent === true) {
|
||||
template.decIndent();
|
||||
}
|
||||
},
|
||||
|
||||
addNamespaceMappings: function(namespaceMappings) {
|
||||
if (!namespaceMappings) {
|
||||
return;
|
||||
}
|
||||
var existingNamespaceMappings = this.namespaceMappings;
|
||||
var prefixMappings = this.prefixMappings;
|
||||
|
||||
forEachEntry(namespaceMappings, function(prefix, uri) {
|
||||
existingNamespaceMappings[prefix] = uri;
|
||||
prefixMappings[uri] = prefix;
|
||||
});
|
||||
},
|
||||
|
||||
hasNamespacePrefix: function(uri) {
|
||||
return this.prefixMappings.hasOwnProperty(uri);
|
||||
},
|
||||
|
||||
resolveNamespacePrefix: function(uri) {
|
||||
var prefix = this.prefixMappings[uri];
|
||||
return (!prefix && this.parentNode) ?
|
||||
this.parentNode.resolveNamespacePrefix() :
|
||||
prefix;
|
||||
},
|
||||
|
||||
forEachNamespace: function(callback, thisObj) {
|
||||
forEachEntry(this.namespaceMappings, callback, thisObj);
|
||||
},
|
||||
|
||||
getNodeClass: function() {
|
||||
return this.nodeClass || this.getClass();
|
||||
},
|
||||
|
||||
setNodeClass: function(nodeClass) {
|
||||
this.nodeClass = nodeClass;
|
||||
},
|
||||
|
||||
prettyPrintTree: function() {
|
||||
var out = [];
|
||||
var printNode = function(node, indent) {
|
||||
out.push(indent + node.toString() + '\n');
|
||||
|
||||
node.forEachChild(function(child) {
|
||||
printNode(child, indent + " ");
|
||||
}, this);
|
||||
};
|
||||
|
||||
printNode(this, "");
|
||||
|
||||
return out.join('');
|
||||
},
|
||||
|
||||
setEscapeXmlBodyText: function(escapeXmlBodyText) {
|
||||
this.escapeXmlBodyText = escapeXmlBodyText;
|
||||
},
|
||||
|
||||
isEscapeXmlBodyText: function() {
|
||||
return this.escapeXmlBodyText;
|
||||
},
|
||||
|
||||
setEscapeXmlContext: function(escapeXmlContext) {
|
||||
this.escapeXmlContext = escapeXmlContext;
|
||||
},
|
||||
|
||||
getEscapeXmlContext: function() {
|
||||
return this.escapeXmlContext;
|
||||
}
|
||||
};
|
||||
|
||||
return Node;
|
||||
});
|
||||
165
lib/compiler/ParseTreeBuilder.js
Normal file
165
lib/compiler/ParseTreeBuilder.js
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/ParseTreeBuilder',
|
||||
['raptor'],
|
||||
function(raptor, require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var logger = module.logger(),
|
||||
sax = require('raptor/xml/sax'),
|
||||
forEach = raptor.forEach,
|
||||
TextNode = require('raptor/templating/compiler/TextNode'),
|
||||
ElementNode = require('raptor/templating/compiler/ElementNode');
|
||||
|
||||
|
||||
var ParseTreeBuilder = function() {
|
||||
};
|
||||
|
||||
ParseTreeBuilder.parse = function(src, filePath, taglibs) {
|
||||
var builder = new ParseTreeBuilder();
|
||||
return builder.parse(src, filePath, taglibs);
|
||||
};
|
||||
|
||||
ParseTreeBuilder.prototype = {
|
||||
/**
|
||||
* @param src {String} The XML source code to parse
|
||||
* @param src {String} The file path (for debugging and error reporting purposes)
|
||||
* @param taglibs {raptor/templating/compiler/TaglibCollection} The taglib collection. Required for resolving taglib URIs when short names are used.
|
||||
*/
|
||||
parse: function(src, filePath, taglibs) {
|
||||
var logger = logger,
|
||||
parentNode = null,
|
||||
rootNode = null,
|
||||
prevTextNode = null,
|
||||
imports,
|
||||
parser = sax.createParser({
|
||||
trim: false,
|
||||
normalize: false,
|
||||
dom: src.documentElement != null
|
||||
}),
|
||||
characters = function(t, isCDATA) {
|
||||
if (!parentNode) {
|
||||
return; //Some bad XML parsers allow text after the ending element...
|
||||
}
|
||||
if (prevTextNode) {
|
||||
prevTextNode.text += t;
|
||||
}
|
||||
else {
|
||||
prevTextNode = new TextNode(t);
|
||||
prevTextNode.pos = parser.getPos();
|
||||
parentNode.appendChild(prevTextNode);
|
||||
}
|
||||
};
|
||||
|
||||
parser.on({
|
||||
error: function(e) {
|
||||
throw raptor.createError(e);
|
||||
},
|
||||
|
||||
characters: function(t) {
|
||||
characters(t, false);
|
||||
},
|
||||
|
||||
cdata: function(t) {
|
||||
characters(t, true);
|
||||
},
|
||||
|
||||
startElement: function(el) {
|
||||
prevTextNode = null;
|
||||
var importsAttr,
|
||||
importedAttr,
|
||||
importedTag;
|
||||
|
||||
var elementNode = new ElementNode(
|
||||
el.getLocalName(),
|
||||
taglibs.resolveURI(el.getNamespaceURI()),
|
||||
el.getPrefix());
|
||||
|
||||
|
||||
elementNode.addNamespaceMappings(el.getNamespaceMappings());
|
||||
|
||||
elementNode.pos = parser.getPos();
|
||||
|
||||
forEach(el.getAttributes(), function(attr) {
|
||||
if (attr.getLocalName() === 'imports' && !attr.getNamespaceURI()) {
|
||||
importsAttr = attr.getValue();
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
|
||||
if (parentNode) {
|
||||
|
||||
parentNode.appendChild(elementNode);
|
||||
}
|
||||
else {
|
||||
rootNode = elementNode;
|
||||
if (importsAttr) {
|
||||
imports = taglibs.getImports(importsAttr);
|
||||
}
|
||||
}
|
||||
|
||||
forEach(el.getAttributes(), function(attr) {
|
||||
var attrURI = taglibs.resolveURI(attr.getNamespaceURI()),
|
||||
attrLocalName = attr.getLocalName(),
|
||||
attrPrefix = attr.getPrefix();
|
||||
|
||||
|
||||
if (!attrURI && imports && (importedAttr = imports.getImportedAttribute(attrLocalName))) {
|
||||
attrURI = importedAttr.uri;
|
||||
attrLocalName = importedAttr.name;
|
||||
attrPrefix = importedAttr.prefix;
|
||||
}
|
||||
|
||||
elementNode.setAttributeNS(
|
||||
attrURI,
|
||||
attrLocalName,
|
||||
attr.getValue(),
|
||||
attrPrefix);
|
||||
}, this);
|
||||
|
||||
if (!elementNode.uri && imports && (importedTag = imports.getImportedTag(elementNode.localName))) {
|
||||
elementNode.uri = importedTag.uri;
|
||||
elementNode.localName = importedTag.name;
|
||||
elementNode.prefix = importedTag.prefix;
|
||||
}
|
||||
|
||||
parentNode = elementNode;
|
||||
},
|
||||
|
||||
endElement: function () {
|
||||
prevTextNode = null;
|
||||
|
||||
parentNode = parentNode.parentNode;
|
||||
}
|
||||
}, this);
|
||||
|
||||
parser.parse(src, filePath);
|
||||
|
||||
rootNode.setRoot(true);
|
||||
|
||||
return rootNode;
|
||||
},
|
||||
|
||||
getRootNode: function() {
|
||||
return this.rootNode;
|
||||
}
|
||||
};
|
||||
|
||||
return ParseTreeBuilder;
|
||||
});
|
||||
389
lib/compiler/Taglib.js
Normal file
389
lib/compiler/Taglib.js
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/Taglib',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var Taglib = function() {
|
||||
this.uri = null;
|
||||
this.shortName = null;
|
||||
this.aliases = [];
|
||||
this.tags = {};
|
||||
this.textTransformers = [];
|
||||
this.attributeMap = {};
|
||||
this.functions = [];
|
||||
this.helperObject = null;
|
||||
|
||||
this.patternAttributes = [];
|
||||
};
|
||||
|
||||
Taglib.prototype = {
|
||||
|
||||
addAttribute: function(attribute) {
|
||||
if (attribute.uri) {
|
||||
throw raptor.createError(new Error('"uri" is not allowed for taglib attributes'));
|
||||
}
|
||||
|
||||
if (attribute.name) {
|
||||
this.attributeMap[attribute.name] = attribute;
|
||||
}
|
||||
else {
|
||||
this.patternAttributes.push(attribute);
|
||||
}
|
||||
},
|
||||
|
||||
getAttribute: function(name) {
|
||||
var attribute = this.attributeMap[name];
|
||||
if (!attribute) {
|
||||
for (var i=0, len=this.patternAttributes.length; i<len; i++) {
|
||||
var patternAttribute = this.patternAttributes[i];
|
||||
if (patternAttribute.pattern.test(name)) {
|
||||
attribute = patternAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
},
|
||||
|
||||
addTag: function(tag) {
|
||||
var key = (tag.uri || '') + ":" + tag.name;
|
||||
tag._taglib = this;
|
||||
this.tags[key] = tag;
|
||||
},
|
||||
|
||||
addTextTransformer: function(transformer) {
|
||||
this.textTransformers.push(transformer);
|
||||
},
|
||||
|
||||
forEachTag: function(callback, thisObj) {
|
||||
raptor.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;
|
||||
},
|
||||
|
||||
addAlias: function(alias) {
|
||||
this.aliases.push(alias);
|
||||
}
|
||||
};
|
||||
|
||||
Taglib.Tag = define.Class(function() {
|
||||
var Tag = function() {
|
||||
// this.handlerClass = null;
|
||||
// this.nodeClass = null;
|
||||
// this.renderer = null;
|
||||
// this.template = null;
|
||||
|
||||
this.attributeMap = {};
|
||||
this.transformers = {};
|
||||
this.nestedVariables = {};
|
||||
this.importedVariables = {};
|
||||
this.nestedTags = {};
|
||||
this.staticProperties = {};
|
||||
this.patternAttributes = [];
|
||||
this._taglib = null;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
raptor.forEachEntry(superTag, function(k, v) {
|
||||
if (subTag[k] === undefined) {
|
||||
subTag[k] = v;
|
||||
}
|
||||
});
|
||||
|
||||
function inheritProps(sub, sup) {
|
||||
raptor.forEachEntry(sup, function(k, v) {
|
||||
if (!sub[k]) {
|
||||
sub[k] = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
['attributeMap',
|
||||
'transformers',
|
||||
'nestedVariables',
|
||||
'importedVariables',
|
||||
'nestedTags',
|
||||
'staticProperties'].forEach(function(propName) {
|
||||
inheritProps(subTag[propName], superTag[propName]);
|
||||
});
|
||||
|
||||
subTag.patternAttributes = superTag.patternAttributes.concat(subTag.patternAttributes);
|
||||
},
|
||||
|
||||
addNestedTag: function(nestedTag) {
|
||||
var uri = nestedTag.uri || '';
|
||||
this.nestedTags[uri + ":" + nestedTag.name] = nestedTag;
|
||||
},
|
||||
|
||||
forEachNestedTag: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.nestedTags, function(key, nestedTag) {
|
||||
callback.call(thisObj, nestedTag);
|
||||
});
|
||||
},
|
||||
|
||||
forEachVariable: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.nestedVariables, function(key, variable) {
|
||||
callback.call(thisObj, variable);
|
||||
});
|
||||
},
|
||||
|
||||
forEachImportedVariable: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.importedVariables, function(key, importedVariable) {
|
||||
callback.call(thisObj, importedVariable);
|
||||
});
|
||||
},
|
||||
|
||||
forEachTransformer: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.transformers, function(key, transformer) {
|
||||
callback.call(thisObj, transformer);
|
||||
});
|
||||
},
|
||||
|
||||
forEachStaticProperty: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.staticProperties, function(key, staticProp) {
|
||||
callback.call(thisObj, staticProp);
|
||||
});
|
||||
},
|
||||
|
||||
hasTransformers: function() {
|
||||
for (var k in this.transformers) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
addAttribute: function(attr) {
|
||||
if (attr.pattern) {
|
||||
this.patternAttributes.push(attr);
|
||||
}
|
||||
else {
|
||||
var uri = attr.uri || '';
|
||||
this.attributeMap[uri + ':' + attr.name] = attr;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getAttribute: function(uri, localName) {
|
||||
if (uri == null) {
|
||||
uri = '';
|
||||
}
|
||||
|
||||
var attr = this.attributeMap[uri + ':' + localName] || this.attributeMap[uri + ':*'] || 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];
|
||||
|
||||
if (patternAttribute.uri !== uri) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patternAttribute.pattern.test(localName)) {
|
||||
attr = patternAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
},
|
||||
|
||||
getTaglibUri: function() {
|
||||
if (!this._taglib) {
|
||||
throw raptor.createError(new Error('Taglib not set for tag. (uri=' + this.uri + ', name=' + this.name + ')'));
|
||||
}
|
||||
return this._taglib.uri;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return "[Tag: <" + this.uri + ":" + this.name + ">]";
|
||||
},
|
||||
|
||||
forEachAttribute: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.attributeMap, function(attrName, attr) {
|
||||
callback.call(thisObj, attr);
|
||||
});
|
||||
},
|
||||
|
||||
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.className;
|
||||
this.transformers[key] = transformer;
|
||||
},
|
||||
|
||||
addStaticProperty: function(prop) {
|
||||
var key = prop.name;
|
||||
this.staticProperties[key] = prop;
|
||||
}
|
||||
};
|
||||
|
||||
return Tag;
|
||||
});
|
||||
|
||||
Taglib.Attribute = define.Class(function() {
|
||||
var Attribute = function() {
|
||||
this.name = null;
|
||||
this.uri = null;
|
||||
this.type = null;
|
||||
this.required = false;
|
||||
this.type = "string";
|
||||
this.allowExpressions = true;
|
||||
};
|
||||
|
||||
Attribute.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return Attribute;
|
||||
});
|
||||
|
||||
Taglib.Property = define.Class(function() {
|
||||
var Property = function() {
|
||||
this.name = null;
|
||||
this.type = "string";
|
||||
this.value = undefined;
|
||||
};
|
||||
|
||||
Property.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return Property;
|
||||
});
|
||||
|
||||
Taglib.NestedVariable = define.Class(function() {
|
||||
var NestedVariable = function() {
|
||||
this.name = null;
|
||||
};
|
||||
|
||||
NestedVariable.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return NestedVariable;
|
||||
});
|
||||
|
||||
Taglib.ImportedVariable = define.Class(function() {
|
||||
var ImportedVariable = function() {
|
||||
this.targetProperty = null;
|
||||
this.expression = null;
|
||||
};
|
||||
|
||||
ImportedVariable.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return ImportedVariable;
|
||||
});
|
||||
|
||||
Taglib.Transformer = define.Class(function() {
|
||||
var uniqueId = 0;
|
||||
|
||||
var Transformer = function() {
|
||||
this.id = uniqueId++;
|
||||
this.tag = null;
|
||||
this.className = null;
|
||||
this.after = null;
|
||||
this.before = null;
|
||||
this.instance = null;
|
||||
this.properties = {};
|
||||
|
||||
};
|
||||
|
||||
Transformer.prototype = {
|
||||
getInstance: function() {
|
||||
if (!this.className) {
|
||||
throw raptor.createError(new Error("Transformer class not defined for tag transformer (tag=" + this.tag + ")"));
|
||||
}
|
||||
|
||||
if (!this.instance) {
|
||||
var Clazz = require(this.className);
|
||||
if (Clazz.process) {
|
||||
return Clazz;
|
||||
}
|
||||
this.instance = new Clazz();
|
||||
this.instance.id = this.id;
|
||||
}
|
||||
return this.instance;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return '[Taglib.Transformer: ' + this.className + ']';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return Transformer;
|
||||
});
|
||||
|
||||
Taglib.Function = define.Class(function() {
|
||||
var Function = function() {
|
||||
this.name = null;
|
||||
this.functionClass = null;
|
||||
this.bindToContext = false;
|
||||
};
|
||||
|
||||
Function.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return Function;
|
||||
});
|
||||
|
||||
Taglib.HelperObject = define.Class(function() {
|
||||
var HelperObject = function() {
|
||||
this.className = null;
|
||||
this.moduleName = null;
|
||||
};
|
||||
|
||||
HelperObject.prototype = {
|
||||
|
||||
};
|
||||
|
||||
return HelperObject;
|
||||
});
|
||||
|
||||
return Taglib;
|
||||
});
|
||||
548
lib/compiler/TaglibCollection.js
Normal file
548
lib/compiler/TaglibCollection.js
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Merges a set of taglibs for ea
|
||||
*/
|
||||
define.Class(
|
||||
'raptor/templating/compiler/TaglibCollection',
|
||||
['raptor'],
|
||||
function(raptor, require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var forEach = raptor.forEach,
|
||||
strings = require('raptor/strings'),
|
||||
ElementNode = require('raptor/templating/compiler/ElementNode'),
|
||||
TextNode = require('raptor/templating/compiler/TextNode'),
|
||||
/*
|
||||
* Probably one of the more amazing regular expressions you will ever see...
|
||||
*
|
||||
* Valid imports:
|
||||
* x, y, z from http://raptorjs.org/templates/core
|
||||
* x, y, z from core
|
||||
* x, y, z from core as my-core
|
||||
* * from core as c
|
||||
* core
|
||||
* core as my-core
|
||||
*/
|
||||
importRegExp = /^(?:(\*|(?:(?:@?[A-Za-z0-9_\-]+\s*,\s*)*@?[A-Za-z0-9_\-]+))\s+from\s+)?([^ ]*)(?:\s+as\s+([A-Za-z0-9_\-]+))?$/,
|
||||
getImported = function(lookup, localName, imports) {
|
||||
if (lookup[localName] != null) {
|
||||
return lookup[localName];
|
||||
}
|
||||
|
||||
var prefixEnd = localName.indexOf('-'),
|
||||
prefix,
|
||||
uri,
|
||||
name;
|
||||
|
||||
|
||||
|
||||
if (prefixEnd != -1) {
|
||||
|
||||
|
||||
prefix = localName.substring(0, prefixEnd);
|
||||
name = localName.substring(prefixEnd+1);
|
||||
uri = imports._prefixes[prefix];
|
||||
|
||||
if (uri) {
|
||||
return {
|
||||
uri: uri,
|
||||
name: name,
|
||||
prefix: prefix
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
var Imports = function(taglibs, importsStr) {
|
||||
this._tagImports = {};
|
||||
this._attrImports = {};
|
||||
this._prefixes = {};
|
||||
|
||||
var parts = strings.trim(importsStr).split(/\s*;\s*/);
|
||||
forEach(parts, function(part) {
|
||||
if (!part) { //Skip empty strings
|
||||
return;
|
||||
}
|
||||
|
||||
var match = part.match(importRegExp),
|
||||
imports,
|
||||
importsLookup = {},
|
||||
from,
|
||||
as;
|
||||
|
||||
if (!match) {
|
||||
throw raptor.createError(new Error('Invalid import: "' + part + '"'));
|
||||
}
|
||||
else {
|
||||
imports = match[1];
|
||||
from = taglibs.resolveURI(match[2]);
|
||||
as = match[3];
|
||||
|
||||
if (!as) {
|
||||
as = taglibs.resolvePrefix(from) || taglibs.resolveShortName(from); //Use either the prefix (preferred) or the short name if provided
|
||||
if (!as) {
|
||||
throw raptor.createError(new Error('Unable to handle imports from "' + from + '". The taglib does not have a prefix or short name defined.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._prefixes[as] = from;
|
||||
|
||||
if (imports) {
|
||||
forEach(imports.split(/\s*,\s*/), function(importedTagName) {
|
||||
importsLookup[importedTagName] = true;
|
||||
});
|
||||
}
|
||||
|
||||
taglibs.forEachTag(from, function(tag, taglib) {
|
||||
|
||||
if (tag.uri === from && (importsLookup['*'] || importsLookup[tag.name])) {
|
||||
/*
|
||||
* Import tags with a URI that matches the taglib URI
|
||||
*/
|
||||
this._tagImports[tag.name] = { uri: from, name: tag.name, prefix: as };
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow imports for attributes that can be assigned to tags with a different URI
|
||||
* e.g. <div c-if="someCondition"></div> --> <div c:if="someCondition"></div>
|
||||
*/
|
||||
tag.forEachAttribute(function(attr) {
|
||||
|
||||
if (tag.uri !== from && (importsLookup['*'] || importsLookup["@" + attr.name])) {
|
||||
this._attrImports[attr.name] = { uri: from, name: attr.name, prefix: as };
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
};
|
||||
|
||||
Imports.prototype = {
|
||||
|
||||
getImportedTag: function(localName) {
|
||||
return getImported(this._tagImports, localName, this);
|
||||
},
|
||||
|
||||
getImportedAttribute: function(localName) {
|
||||
return getImported(this._attrImports, localName, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
var TaglibCollection = function() {
|
||||
this.tagTransformersLookup = {}; //Tag transformers lookup
|
||||
this.tags = {}; //Tag definitions lookup
|
||||
this.textTransformers = {};
|
||||
this.taglibsByURI = {}; //Lookup to track the URIs of taglibs that have been added to this collection
|
||||
this.uriToShortNameMapping = {};
|
||||
this.aliases = {};
|
||||
this.uriToPrefixMapping = {};
|
||||
this.functionsLookup = {};
|
||||
this.attributeLookup = {};
|
||||
this.nestedTags = {};
|
||||
};
|
||||
|
||||
TaglibCollection.prototype = {
|
||||
|
||||
getAttribute: function(tagUri, tagName, attrUri, attrName) {
|
||||
var tags = this.tags;
|
||||
|
||||
tagUri = this.resolveURI(tagUri);
|
||||
|
||||
var attributeLookup = this.attributeLookup;
|
||||
if (attrUri == null) {
|
||||
attrUri = '';
|
||||
}
|
||||
|
||||
var _findAttrForTag = function(tagLookupKey) {
|
||||
var attr = attributeLookup[tagLookupKey + ":" + attrUri + ":" + attrName] ||
|
||||
attributeLookup[tagLookupKey + ":" + attrUri + ":*"] ||
|
||||
attributeLookup[tagLookupKey + ":*:" + attrName] ||
|
||||
attributeLookup[tagLookupKey + ":*:*"];
|
||||
if (!attr) {
|
||||
var tag = tags[tagLookupKey];
|
||||
if (tag) {
|
||||
attr = tag.getAttribute(attrUri, attrName);
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
};
|
||||
|
||||
var attr = _findAttrForTag(tagUri + ":" + tagName) ||
|
||||
_findAttrForTag(tagUri + ":*") ||
|
||||
_findAttrForTag("*:*");
|
||||
|
||||
if (attr && attr.uri && attr.uri !== '*') {
|
||||
//The attribute is being imported
|
||||
|
||||
var taglib = this.taglibsByURI[attr.uri];
|
||||
if (!taglib) {
|
||||
throw raptor.createError(new Error('Taglib with URI "' + attr.uri + '" not found for imported attribute with name "' + attrName + '"'));
|
||||
}
|
||||
attr = taglib.getAttribute(attrName);
|
||||
if (!attr) {
|
||||
throw raptor.createError(new Error('Attribute "' + attrName + '" imported from taglib with URI "' + attr.uri + '" not found in taglib.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!attr) {
|
||||
var attrShortUri = this.resolveShortName(attrUri);
|
||||
if (attrShortUri !== attrUri) {
|
||||
return this.getAttribute(tagUri, tagName, attrShortUri, attrName);
|
||||
}
|
||||
}
|
||||
|
||||
return attr;
|
||||
},
|
||||
|
||||
forEachTag: function(uri, callback, thisObj) {
|
||||
uri = this.resolveURI(uri);
|
||||
|
||||
|
||||
var taglib = this.taglibsByURI[uri];
|
||||
|
||||
if (!taglib) {
|
||||
return;
|
||||
}
|
||||
|
||||
raptor.forEachEntry(taglib.tags, function(key, tag) {
|
||||
|
||||
callback.call(thisObj, tag, taglib);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the provided URI is the URI of a taglib
|
||||
*
|
||||
* @param uri {String} The URI to check
|
||||
* @returns {Boolean} Returns true if the URI is that of a taglib. False, otherwise.
|
||||
*/
|
||||
isTaglib: function(uri) {
|
||||
return this.taglibsByURI[this.resolveURI(uri)] != null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new taglib to the collection
|
||||
*
|
||||
* @param taglib {raptor/templating/compiler/Taglib} The taglib to add
|
||||
*/
|
||||
add: function(taglib) {
|
||||
var targetTaglib = this.taglibsByURI[taglib.uri] || taglib;
|
||||
|
||||
this.taglibsByURI[taglib.uri] = targetTaglib; //Mark the taglib as added
|
||||
|
||||
if (taglib.shortName && taglib.shortName != taglib.uri) {
|
||||
/*
|
||||
* If the taglib has a short name then record that mapping so that we
|
||||
* can map the short name to the full URI
|
||||
*/
|
||||
this.taglibsByURI[taglib.shortName] = taglib; //Mark the short name as being a taglib
|
||||
|
||||
if (taglib.shortName) {
|
||||
targetTaglib.shortName = taglib.shortName;
|
||||
this.addAlias(taglib.uri, taglib.shortName);
|
||||
this.uriToShortNameMapping[taglib.uri] = taglib.shortName; //Add the reverse-mapping
|
||||
}
|
||||
|
||||
if (taglib.prefix) {
|
||||
targetTaglib.prefix = taglib.prefix;
|
||||
|
||||
this.uriToPrefixMapping[taglib.uri] = taglib.prefix;
|
||||
}
|
||||
}
|
||||
|
||||
taglib.aliases.forEach(function(alias) {
|
||||
this.addAlias(taglib.uri, alias);
|
||||
}, this);
|
||||
|
||||
/*
|
||||
* 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 uri = tag.uri == null ? (tag.uri = taglib.uri) : tag.uri, //If not specified, the tag URI should be the same as the taglib URI
|
||||
name = tag.name,
|
||||
key = uri + ":" + name; //The taglib will be registered using the combination of URI and tag name
|
||||
|
||||
this.tags[key] = tag; //Register the tag using the combination of URI and tag name so that it can easily be looked up
|
||||
|
||||
if (tag.hasTransformers()) { //Check if the tag has any transformers that should be applied
|
||||
|
||||
var tagTransformersForTags = this.tagTransformersLookup[key] || (this.tagTransformersLookup[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 raptor.createError(new Error("Transformer is null"));
|
||||
}
|
||||
tagTransformersForTags.push(transformer);
|
||||
}, this);
|
||||
}
|
||||
|
||||
tag.forEachNestedTag(function(nestedTag) {
|
||||
this.nestedTags[tag.uri + ":" + tag.name + ":" + nestedTag.uri + ":" + nestedTag.name] = nestedTag;
|
||||
}, this);
|
||||
|
||||
tag.forEachAttribute(function(attr) {
|
||||
|
||||
this.attributeLookup[tag.uri + ":" + tag.name + ":" + (attr.uri || '') + ":" + attr.name] = attr;
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
/*
|
||||
* Now register all of the text transformers that are part of the provided taglibs
|
||||
*/
|
||||
forEach(taglib.textTransformers, function(textTransformer) {
|
||||
this.textTransformers[textTransformer.className] = textTransformer;
|
||||
}, this);
|
||||
|
||||
|
||||
forEach(taglib.functions, function(func) {
|
||||
if (!func.name) {
|
||||
throw raptor.createError(new Error("Function name not set."));
|
||||
}
|
||||
this.functionsLookup[taglib.uri + ":" + func.name] = func;
|
||||
if (targetTaglib.shortName) {
|
||||
this.functionsLookup[taglib.shortName + ":" + func.name] = func;
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
addAlias: function(uri, alias) {
|
||||
this.aliases[alias] = uri;
|
||||
},
|
||||
|
||||
/**
|
||||
* Invokes a callback for eaching matching transformer that is found for the current node.
|
||||
* If the provided node is an element node then the match is based on the node's
|
||||
* URI and the local name. If the provided node is a text node then all
|
||||
* text transformers will match.
|
||||
*
|
||||
* @param node {raptor/templating/compiler$Node} The node to match transformers to
|
||||
* @param callback {Function} The callback function to invoke for each matching transformer
|
||||
* @param thisObj {Object} The "this" object to use when invoking the callback function
|
||||
*/
|
||||
forEachNodeTransformer: function(node, callback, thisObj) {
|
||||
/*
|
||||
* Based on the type of node we have to choose how to transform it
|
||||
*/
|
||||
if (node instanceof ElementNode) {
|
||||
this.forEachTagTransformer(node.uri, node.localName, callback, thisObj);
|
||||
}
|
||||
else if (node instanceof TextNode) {
|
||||
this.forEachTextTransformer(callback, thisObj);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves a taglib short name to a taglib URI.
|
||||
*
|
||||
* <p>
|
||||
* If the provided short name is not a known short name then it is just returned.
|
||||
*
|
||||
* @param shortName {String} The taglib short name to resolve
|
||||
* @returns {String} The resolved URI or the input string if it is not a known short name
|
||||
*/
|
||||
resolveURI: function(inputURI) {
|
||||
if (!inputURI) {
|
||||
return inputURI;
|
||||
}
|
||||
|
||||
var uri;
|
||||
|
||||
while(true) {
|
||||
uri = this.aliases[inputURI] || inputURI;
|
||||
if (uri === inputURI) {
|
||||
return uri;
|
||||
}
|
||||
inputURI = uri;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves a taglib URI to a taglib short name.
|
||||
*
|
||||
* <p>
|
||||
* If the provided URI is not a known short name then it is just returned.
|
||||
*
|
||||
* @param uri {String} The taglib uri to resolve to a short name
|
||||
* @returns {String} The resolved short name or undefined if the taglib does not have a short name
|
||||
*/
|
||||
resolveShortName: function(uri) {
|
||||
if (!uri) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
uri = this.resolveURI(uri);
|
||||
|
||||
return this.uriToShortNameMapping[uri]; //Otherwise lookup the short name for the long URI
|
||||
},
|
||||
|
||||
resolvePrefix: function(uri) {
|
||||
if (!uri) {
|
||||
return uri;
|
||||
}
|
||||
uri = this.resolveURI(uri); //Resolve the short name to a long URI
|
||||
|
||||
return this.uriToPrefixMapping[uri]; //See if there is a mapping from the long URI to a prefix
|
||||
},
|
||||
|
||||
/**
|
||||
* Invokes a provided callback for each tag transformer that
|
||||
* matches the provided URI and tag name.
|
||||
*
|
||||
* @param uri {String} The tag URI or an empty string if the tag is not namespaced
|
||||
* @param tagName {String} The local name of the tag (e.g. "div")
|
||||
* @param callback {Function} The callback function to invoke
|
||||
* @param thisObj {Object} The "this" object to use when invoking the callback function
|
||||
*/
|
||||
forEachTagTransformer: function(uri, tagName, callback, thisObj) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (uri == null) {
|
||||
uri = '';
|
||||
}
|
||||
|
||||
var matchingTransformersByName = {};
|
||||
var matchingTransformers = [];
|
||||
var handled = {};
|
||||
var before = {};
|
||||
|
||||
/*
|
||||
* Handle all of the transformers in the tag transformers entry
|
||||
*/
|
||||
var _addTransformers = function(transformers) {
|
||||
raptor.forEach(transformers, function(transformer) {
|
||||
if (!transformer) {
|
||||
throw raptor.createError(new Error("Invalid transformer"));
|
||||
}
|
||||
if (!matchingTransformersByName[transformer.className]) {
|
||||
matchingTransformersByName[transformer.className] = transformer;
|
||||
matchingTransformers.push(transformer);
|
||||
|
||||
if (transformer.before) {
|
||||
(before[transformer.before] || (before[transformer.before] = [])).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.tagTransformersLookup["*:*"]); //Wildcard for both URI and tag name (i.e. transformers that apply to every element)
|
||||
_addTransformers(this.tagTransformersLookup[uri + ":*"]); //Wildcard for tag name but matching URI (i.e. transformers that apply to every element with a URI, regadless of tag name)
|
||||
_addTransformers(this.tagTransformersLookup[uri + ":" + tagName]); //All transformers that match the URI and tag name exactly
|
||||
|
||||
var _handleTransformer = function(transformer) {
|
||||
if (!handled[transformer.className]) {
|
||||
handled[transformer.className] = true;
|
||||
|
||||
if (transformer.after) { //Check if this transformer is required to run
|
||||
if (!matchingTransformersByName[transformer.after]) {
|
||||
throw raptor.createError(new Error('After transformers not found for "' + transformer.after + '"'));
|
||||
}
|
||||
_handleTransformer(matchingTransformersByName[transformer.after]); //Handle any transformers that this transformer is supposed to run after
|
||||
}
|
||||
|
||||
raptor.forEach(before[transformer.className], _handleTransformer); //Handle any transformers that are configured to run before this transformer
|
||||
|
||||
callback.call(thisObj, transformer);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
matchingTransformers.forEach(function(transformer) {
|
||||
_handleTransformer(transformer);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invokes a provided callback for each registered text transformer.
|
||||
*
|
||||
* @param callback {Function} The callback function to invoke
|
||||
* @param thisObj {Object} The "this" object to use when invoking the callback function
|
||||
*/
|
||||
forEachTextTransformer: function(callback, thisObj) {
|
||||
raptor.forEachEntry(this.textTransformers, function(className, textTransformer) {
|
||||
var keepGoing = callback.call(thisObj, textTransformer);
|
||||
if (keepGoing === false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the definition of a tag that was loaded from the taglib with the specified
|
||||
* URI and with the matching
|
||||
* @param uri
|
||||
* @param localName
|
||||
* @returns
|
||||
*/
|
||||
getTag: function(uri, localName) {
|
||||
uri = this.resolveURI(uri);
|
||||
|
||||
var tag = this.tags[uri + ":" + localName];
|
||||
if (!tag) {
|
||||
tag = this.tags[uri + ":*"]; //See if there was a wildcard tag definition in the taglib
|
||||
}
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
getNestedTag: function(parentTagUri, parentTagName, nestedTagUri, nestedTagName) {
|
||||
parentTagUri = parentTagUri || '';
|
||||
nestedTagUri = nestedTagUri || '';
|
||||
return this.nestedTags[parentTagUri + ":" + parentTagName + ":" + nestedTagUri + ":" + nestedTagName];
|
||||
},
|
||||
|
||||
getFunction: function(uri, functionName) {
|
||||
return this.functionsLookup[uri + ":" + functionName];
|
||||
},
|
||||
|
||||
getHelperObject: function(uri) {
|
||||
uri = this.resolveURI(uri);
|
||||
|
||||
var taglib = this.taglibsByURI[uri];
|
||||
if (!taglib) {
|
||||
throw new Error("Invalid taglib URI: " + uri);
|
||||
}
|
||||
return taglib.getHelperObject();
|
||||
},
|
||||
|
||||
getImports: function(importsStr) {
|
||||
return new Imports(this, importsStr);
|
||||
}
|
||||
};
|
||||
|
||||
return TaglibCollection;
|
||||
});
|
||||
581
lib/compiler/TaglibXmlLoader.js
Normal file
581
lib/compiler/TaglibXmlLoader.js
Normal file
@ -0,0 +1,581 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/compiler/TaglibXmlLoader",
|
||||
['raptor'],
|
||||
function(raptor, require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var objectMapper = require('raptor/xml/sax/object-mapper'),
|
||||
logger = module.logger(),
|
||||
regexp = require('raptor/regexp'),
|
||||
Taglib = require('raptor/templating/compiler/Taglib'),
|
||||
Tag = Taglib.Tag,
|
||||
Attribute = Taglib.Attribute,
|
||||
Property = Taglib.Property,
|
||||
NestedVariable = Taglib.NestedVariable,
|
||||
ImportedVariable = Taglib.ImportedVariable,
|
||||
Transformer = Taglib.Transformer,
|
||||
Function = Taglib.Function,
|
||||
HelperObject = Taglib.HelperObject,
|
||||
STRING = "string",
|
||||
BOOLEAN = "boolean",
|
||||
OBJECT = "object";
|
||||
|
||||
var TaglibXmlLoader = function(src, resource) {
|
||||
this.src = src;
|
||||
this.filePath = typeof resource === 'string' ? resource : resource.getURL();
|
||||
this.resource = resource;
|
||||
};
|
||||
|
||||
TaglibXmlLoader.load = function(src, resource) {
|
||||
var loader = new TaglibXmlLoader(src, resource);
|
||||
return loader.load();
|
||||
};
|
||||
|
||||
TaglibXmlLoader.prototype = {
|
||||
load: function() {
|
||||
var src = this.src,
|
||||
filePath = this.filePath,
|
||||
taglibResource = this.resource,
|
||||
tagsById = {},
|
||||
handleTagExtends = function(subTag) {
|
||||
var extendsId = subTag['extends'];
|
||||
if (!extendsId) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete subTag['extends'];
|
||||
|
||||
var superTag = tagsById[extendsId];
|
||||
if (!superTag) {
|
||||
throw raptor.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) {
|
||||
if (attr.uri == null) {
|
||||
attr.uri = '';
|
||||
}
|
||||
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"
|
||||
},
|
||||
|
||||
"uri": {
|
||||
_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
|
||||
}
|
||||
};
|
||||
|
||||
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 raptor.createError(new Error('The "target-property" attribute is required for an imported variable'));
|
||||
}
|
||||
if (!importedVariable.expression) {
|
||||
throw raptor.createError(new Error('The "expression" attribute is required for an imported variable'));
|
||||
}
|
||||
tag.addImportedVariable(importedVariable);
|
||||
},
|
||||
|
||||
"name": { // Short-hand for target-property="<name>" and expression="<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 raptor.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();
|
||||
|
||||
if (!taglib) {
|
||||
taglib = newTaglib;
|
||||
}
|
||||
|
||||
|
||||
return newTaglib;
|
||||
},
|
||||
|
||||
"attribute": attributeHandler,
|
||||
|
||||
"tlib-version": {
|
||||
_type: STRING,
|
||||
_targetProp: "version"
|
||||
},
|
||||
|
||||
"short-name": {
|
||||
_type: STRING,
|
||||
_targetProp: "shortName"
|
||||
},
|
||||
"uri": {
|
||||
_type: STRING,
|
||||
_set: function(taglib, name, value, context) {
|
||||
if (!taglib.uri) {
|
||||
taglib.uri = value;
|
||||
}
|
||||
else {
|
||||
taglib.addAlias(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"prefix": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"tag": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Tag();
|
||||
},
|
||||
|
||||
_end: function(tag) {
|
||||
if (tag.uri === undefined) {
|
||||
tag.uri = taglib.uri;
|
||||
}
|
||||
|
||||
taglib.addTag(tag);
|
||||
|
||||
if (tag.id) {
|
||||
tagsById[tag.id] = tag;
|
||||
}
|
||||
},
|
||||
|
||||
"name": {
|
||||
_type: STRING,
|
||||
_targetProp: "name"
|
||||
},
|
||||
"uri": {
|
||||
_type: STRING,
|
||||
_set: function(tag, name, value, context) {
|
||||
tag.uri = 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,
|
||||
_targetProp: "handlerClass"
|
||||
},
|
||||
"renderer": {
|
||||
_type: STRING,
|
||||
_targetProp: "handlerClass"
|
||||
},
|
||||
"template": {
|
||||
_type: STRING,
|
||||
_targetProp: "template"
|
||||
},
|
||||
"node-class": {
|
||||
_type: STRING,
|
||||
_targetProp: "nodeClass"
|
||||
},
|
||||
"dynamic-attributes": {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: "dynamicAttributes"
|
||||
},
|
||||
|
||||
"dynamic-attributes-remove-dashes": {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: "dynamicAttributesRemoveDashes"
|
||||
},
|
||||
|
||||
"<attribute>": attributeHandler,
|
||||
|
||||
"<static-property>": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Property();
|
||||
},
|
||||
|
||||
_end: function(prop, tag) {
|
||||
if (!prop.name) {
|
||||
throw raptor.createError(new Error('The "name" property is required for a <static-property>'));
|
||||
}
|
||||
|
||||
if (!prop.hasOwnProperty('value')) {
|
||||
throw raptor.createError(new Error('The "value" property is required for a <static-property>'));
|
||||
}
|
||||
|
||||
tag.addStaticProperty(prop);
|
||||
},
|
||||
|
||||
"name": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"type": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"value": {
|
||||
_type: STRING
|
||||
}
|
||||
},
|
||||
|
||||
"<nested-tag>": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Tag();
|
||||
},
|
||||
|
||||
_end: function(nestedTag, tag) {
|
||||
if (nestedTag.uri === null || nestedTag.uri === undefined) {
|
||||
nestedTag.uri = taglib.uri;
|
||||
}
|
||||
|
||||
nestedTag.targetProperty = nestedTag.targetProperty || nestedTag.name;
|
||||
|
||||
if (!nestedTag.name) {
|
||||
throw raptor.createError(new Error('The "name" property is required for a <nested-tag>'));
|
||||
}
|
||||
|
||||
tag.addNestedTag(nestedTag);
|
||||
},
|
||||
|
||||
"name": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"type": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"target-property": {
|
||||
_type: STRING,
|
||||
_targetProp: "targetProperty"
|
||||
}
|
||||
},
|
||||
|
||||
"nested-variable": variableHandler, // Issue #28: "nested-variable" deprecated. "variable" should be used instead.
|
||||
"variable": variableHandler,
|
||||
|
||||
"imported-variable": importVariableHandler, // Issue #28: "imported-variable" deprecated. "import-variable" should be used instead.
|
||||
"import-variable": importVariableHandler,
|
||||
|
||||
"transformer-class": {
|
||||
_type: STRING,
|
||||
_set: function(tag, name, value) {
|
||||
var transformer = new Transformer();
|
||||
transformer.className = value;
|
||||
tag.addTransformer(transformer);
|
||||
}
|
||||
},
|
||||
|
||||
"transformer": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Transformer();
|
||||
},
|
||||
|
||||
_end: function(transformer, tag) {
|
||||
tag.addTransformer(transformer);
|
||||
},
|
||||
|
||||
"class-name": {
|
||||
_type: STRING,
|
||||
_targetProp: "className"
|
||||
},
|
||||
|
||||
"after": {
|
||||
_type: STRING,
|
||||
_targetProp: "after"
|
||||
},
|
||||
|
||||
"before": {
|
||||
_type: STRING,
|
||||
_targetProp: "before"
|
||||
},
|
||||
|
||||
"<properties>": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function(parent) {
|
||||
return (parent.properties = {});
|
||||
},
|
||||
|
||||
"<*>": {
|
||||
_type: STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//end "tag"
|
||||
|
||||
"text-transformer": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Transformer();
|
||||
},
|
||||
|
||||
_end: function(textTransformer) {
|
||||
taglib.addTextTransformer(textTransformer);
|
||||
},
|
||||
|
||||
"class-name": {
|
||||
_type: STRING,
|
||||
_targetProp: "className"
|
||||
}
|
||||
},
|
||||
|
||||
"import-taglib": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return {};
|
||||
|
||||
},
|
||||
|
||||
_end: function(importedTaglib) {
|
||||
var path = importedTaglib.path,
|
||||
importedXmlSource,
|
||||
importedTaglibResource;
|
||||
|
||||
if (path.startsWith('/')) {
|
||||
importedTaglibResource = require('raptor/resources').findResource(path);
|
||||
}
|
||||
else {
|
||||
importedTaglibResource = taglibResource.resolve(path);
|
||||
}
|
||||
|
||||
if (!importedTaglibResource.exists()) {
|
||||
throw raptor.createError(new Error('Imported taglib with path "' + path + '" not found in taglib at path "' + filePath + '"'));
|
||||
}
|
||||
|
||||
require('raptor/templating/compiler').recordLoadedTaglib(importedTaglibResource);
|
||||
|
||||
importedXmlSource = importedTaglibResource.readAsString();
|
||||
|
||||
objectMapper.read(
|
||||
importedXmlSource,
|
||||
importedTaglibResource.getURL(),
|
||||
handlers);
|
||||
|
||||
},
|
||||
|
||||
"path": {
|
||||
_type: STRING
|
||||
}
|
||||
},
|
||||
|
||||
"function": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new Function();
|
||||
},
|
||||
|
||||
_end: function(func) {
|
||||
taglib.addFunction(func);
|
||||
},
|
||||
|
||||
"name": {
|
||||
_type: STRING
|
||||
},
|
||||
|
||||
"class": {
|
||||
_type: STRING,
|
||||
_targetProp: "functionClass"
|
||||
},
|
||||
|
||||
"bind-to-context": {
|
||||
_type: BOOLEAN,
|
||||
_targetProp: "bindToContext"
|
||||
}
|
||||
},
|
||||
|
||||
"helper-object": {
|
||||
_type: OBJECT,
|
||||
|
||||
_begin: function() {
|
||||
return new HelperObject();
|
||||
},
|
||||
|
||||
_end: function(helperObject) {
|
||||
taglib.setHelperObject(helperObject);
|
||||
},
|
||||
|
||||
"module-name": {
|
||||
_type: STRING,
|
||||
_targetProp: "moduleName"
|
||||
},
|
||||
|
||||
"class-name": {
|
||||
_type: STRING,
|
||||
_targetProp: "className"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
objectMapper.read(
|
||||
src,
|
||||
filePath,
|
||||
handlers);
|
||||
|
||||
|
||||
taglib.forEachTag(function(tag) {
|
||||
handleTagExtends(tag);
|
||||
});
|
||||
|
||||
return taglib;
|
||||
}
|
||||
};
|
||||
|
||||
return TaglibXmlLoader;
|
||||
|
||||
});
|
||||
620
lib/compiler/TemplateBuilder.js
Normal file
620
lib/compiler/TemplateBuilder.js
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/TemplateBuilder',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var INDENT = " ";
|
||||
|
||||
var stringify = require('raptor/json/stringify'),
|
||||
strings = require('raptor/strings'),
|
||||
Expression = require('raptor/templating/compiler/Expression'),
|
||||
forEach = raptor.forEach;
|
||||
|
||||
|
||||
var CodeWriter = function(indent) {
|
||||
this._indent = indent != null ? indent : INDENT + INDENT;
|
||||
this._code = strings.createStringBuilder();
|
||||
this.firstStatement = true;
|
||||
this._bufferedText = null;
|
||||
this._bufferedContextMethodCalls = null;
|
||||
};
|
||||
|
||||
CodeWriter.prototype = {
|
||||
|
||||
write: function(expression) {
|
||||
this.contextMethodCall("w", expression);
|
||||
},
|
||||
|
||||
text: function(text) {
|
||||
if (this._bufferedText === null) {
|
||||
this._bufferedText = text;
|
||||
}
|
||||
else {
|
||||
this._bufferedText += text;
|
||||
}
|
||||
},
|
||||
|
||||
contextMethodCall: function(methodName, args) {
|
||||
|
||||
this.flushText();
|
||||
|
||||
if (!this._bufferedContextMethodCalls) {
|
||||
this._bufferedContextMethodCalls = [];
|
||||
}
|
||||
|
||||
|
||||
args = require('raptor/arrays').arrayFromArguments(arguments, 1);
|
||||
|
||||
this._bufferedContextMethodCalls.push([methodName, args]);
|
||||
},
|
||||
|
||||
code: function(code) {
|
||||
this.flush();
|
||||
this._code.append(code);
|
||||
},
|
||||
|
||||
statement: function(code) {
|
||||
this.flush();
|
||||
this.code((this.firstStatement ? "" : "\n") + this._indent + code + "\n");
|
||||
this.firstStatement = false;
|
||||
},
|
||||
|
||||
line: function(code) {
|
||||
this.code(this._indent + code + "\n");
|
||||
},
|
||||
|
||||
indentStr: function(delta) {
|
||||
if (arguments.length === 0) {
|
||||
return this._indent;
|
||||
}
|
||||
else {
|
||||
var indent = this._indent;
|
||||
for (var i=0; i<delta; i++) {
|
||||
indent += INDENT;
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
},
|
||||
|
||||
indent: function() {
|
||||
if (arguments.length === 0) {
|
||||
this.code(this._indent);
|
||||
}
|
||||
else if (arguments.length === 1 && typeof arguments[0] === 'number') {
|
||||
this.code(this.indentStr(arguments[0]));
|
||||
}
|
||||
else if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') {
|
||||
var func,
|
||||
thisObj,
|
||||
delta;
|
||||
|
||||
if (typeof arguments[0] === 'function') {
|
||||
delta = 1;
|
||||
func = arguments[0];
|
||||
thisObj = arguments[1];
|
||||
}
|
||||
else {
|
||||
delta = arguments[0];
|
||||
func = arguments[1];
|
||||
thisObj = arguments[2];
|
||||
}
|
||||
|
||||
|
||||
this.incIndent(delta);
|
||||
func.call(thisObj);
|
||||
this.decIndent(delta);
|
||||
}
|
||||
else if (typeof arguments[0] === 'string') {
|
||||
this.code(this._indent + arguments[0]);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
this.flushText();
|
||||
this.flushMethodCalls();
|
||||
},
|
||||
|
||||
flushText: function() {
|
||||
var curText = this._bufferedText;
|
||||
if (curText) {
|
||||
this._bufferedText = null;
|
||||
this.write(stringify(curText, {useSingleQuote: true}));
|
||||
}
|
||||
},
|
||||
|
||||
flushMethodCalls: function() {
|
||||
var _bufferedContextMethodCalls = this._bufferedContextMethodCalls;
|
||||
if (_bufferedContextMethodCalls) {
|
||||
if (!this.firstStatement) {
|
||||
this._code.append("\n");
|
||||
}
|
||||
|
||||
this.firstStatement = false;
|
||||
|
||||
this._bufferedContextMethodCalls = null;
|
||||
forEach(_bufferedContextMethodCalls, function(curWrite, i) {
|
||||
var methodName = curWrite[0],
|
||||
args = curWrite[1];
|
||||
|
||||
if (i === 0)
|
||||
{
|
||||
this._code.append(this.indentStr() + 'context.' + methodName + "(");
|
||||
}
|
||||
else {
|
||||
this.incIndent();
|
||||
this._code.append(this.indentStr() + '.' + methodName + "(");
|
||||
}
|
||||
|
||||
args.forEach(function(arg, i) {
|
||||
if (i !== 0) {
|
||||
this._code.append(", ");
|
||||
}
|
||||
|
||||
if (typeof arg === 'string') {
|
||||
this._code.append(arg);
|
||||
}
|
||||
else if (typeof arg === 'boolean') {
|
||||
this._code.append(arg ? 'true' : 'false');
|
||||
}
|
||||
else if (typeof arg === 'function') {
|
||||
arg();
|
||||
}
|
||||
else if (arg instanceof Expression) {
|
||||
this._code.append(arg.toString());
|
||||
}
|
||||
else if (arg) {
|
||||
this._code.append(arg.toString());
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error('Illegal arg for method call "' +methodName + '": ' + arg.toString() + " (" + i +")"));
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
|
||||
if (i < _bufferedContextMethodCalls.length -1) {
|
||||
this._code.append(")\n");
|
||||
}
|
||||
else {
|
||||
this._code.append(");\n");
|
||||
}
|
||||
if (i !== 0) {
|
||||
this.decIndent();
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
incIndent: function(delta) {
|
||||
if (arguments.length === 0) {
|
||||
delta = 1;
|
||||
}
|
||||
|
||||
this.flush();
|
||||
this._indent = this.indentStr(delta);
|
||||
this.firstStatement = true;
|
||||
},
|
||||
|
||||
decIndent: function(delta) {
|
||||
if (arguments.length === 0) {
|
||||
delta = 1;
|
||||
}
|
||||
|
||||
this.flush();
|
||||
this._indent = this._indent.substring(INDENT.length * delta);
|
||||
this.firstStatement = false;
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
this.flush();
|
||||
return this._code.toString();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var TemplateBuilder = function(compiler, resource, rootNode) {
|
||||
this.resource = resource;
|
||||
this.rootNode = rootNode;
|
||||
this.compiler = compiler;
|
||||
|
||||
if (this.rootNode) {
|
||||
this.rootNode.compiler = compiler;
|
||||
|
||||
this.rootNode.forEachNamespace(function(prefix, uri) {
|
||||
|
||||
if (!this.compiler.taglibs.isTaglib(uri)) {
|
||||
require('raptor/templating/compiler').findAndLoadTaglib(uri);
|
||||
|
||||
if (!this.compiler.taglibs.isTaglib(uri)) {
|
||||
this.rootNode.addError('Taglib with URI "' + uri + '" (prefix: "' + prefix + '") not found.');
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (typeof resource === 'string') {
|
||||
this.resource = null;
|
||||
this.filePath = this.path = resource;
|
||||
}
|
||||
else if (require('raptor/resources').isResource(resource)) {
|
||||
this.filePath = resource.getURL();
|
||||
this.path = resource.getPath();
|
||||
}
|
||||
|
||||
this.options = compiler.options;
|
||||
this.templateName = null;
|
||||
this.attributes = {};
|
||||
|
||||
this.writer = new CodeWriter();
|
||||
|
||||
this.staticVars = [];
|
||||
this.staticVarsLookup = {};
|
||||
this.helperFunctionsAdded = {};
|
||||
|
||||
this.vars = [];
|
||||
this.varsLookup = {};
|
||||
|
||||
this.getStaticHelperFunction("empty", "e");
|
||||
this.getStaticHelperFunction("notEmpty", "ne");
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
TemplateBuilder.prototype = {
|
||||
|
||||
isTaglib: function(uri) {
|
||||
return this.rootNode.hasNamespacePrefix(uri);
|
||||
},
|
||||
|
||||
getTemplateName: function() {
|
||||
var options = this.options || {};
|
||||
return options.templateName || this.templateName || options.defaultTemplateName;
|
||||
},
|
||||
|
||||
_getHelperFunction: function(varName, propName, isStatic) {
|
||||
var key = propName + ":" + (isStatic ? "static" : "context");
|
||||
var added = this.helperFunctionsAdded[key];
|
||||
if (added) {
|
||||
return added;
|
||||
}
|
||||
else {
|
||||
if (isStatic) {
|
||||
this.addStaticVar(varName, "helpers." + propName);
|
||||
}
|
||||
else {
|
||||
this.addVar(varName, "contextHelpers." + propName);
|
||||
}
|
||||
|
||||
this.helperFunctionsAdded[key] = varName;
|
||||
return varName;
|
||||
}
|
||||
},
|
||||
|
||||
getContextHelperFunction: function(varName, propName) {
|
||||
return this._getHelperFunction(varName, propName, false);
|
||||
},
|
||||
|
||||
captureCode: function(func, thisObj) {
|
||||
var oldWriter = this.writer;
|
||||
var newWriter = new CodeWriter(oldWriter.indentStr());
|
||||
|
||||
try
|
||||
{
|
||||
this.writer = newWriter;
|
||||
func.call(thisObj);
|
||||
return newWriter.getOutput();
|
||||
}
|
||||
finally {
|
||||
this.writer = oldWriter;
|
||||
}
|
||||
},
|
||||
|
||||
getStaticHelperFunction: function(varName, propName) {
|
||||
return this._getHelperFunction(varName, propName, true);
|
||||
},
|
||||
|
||||
hasStaticVar: function(name) {
|
||||
return this.staticVarsLookup[name] === true;
|
||||
},
|
||||
|
||||
addStaticVar: function(name, expression) {
|
||||
if (!this.staticVarsLookup[name]) {
|
||||
this.staticVarsLookup[name] = true;
|
||||
this.staticVars.push({name: name, expression: expression});
|
||||
}
|
||||
},
|
||||
|
||||
hasVar: function(name) {
|
||||
return this.vars[name] === true;
|
||||
},
|
||||
|
||||
addVar: function(name, expression) {
|
||||
this.vars[name] = true;
|
||||
this.vars.push({name: name, expression: expression});
|
||||
},
|
||||
|
||||
_writeVars: function(vars, out, indent) {
|
||||
if (!vars.length) {
|
||||
return;
|
||||
}
|
||||
out.append(indent + "var ");
|
||||
var declarations = [];
|
||||
forEach(vars, function(v, i) {
|
||||
declarations.push((i !== 0 ? indent + " " : "" ) + v.name + " = " + v.expression + (i === vars.length-1 ? ";\n" : ",\n"));
|
||||
});
|
||||
out.append(declarations.join(""));
|
||||
},
|
||||
|
||||
text: function(text) {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.text(text);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
attr: function(name, valueExpression, escapeXml) {
|
||||
if (!this.hasErrors()) {
|
||||
if (escapeXml === false) {
|
||||
this.contextMethodCall("a", stringify(name), valueExpression, false);
|
||||
}
|
||||
else {
|
||||
this.contextMethodCall("a", stringify(name), valueExpression);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
attrs: function(attrsExpression) {
|
||||
if (!this.hasErrors()) {
|
||||
this.contextMethodCall("a", attrsExpression);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
include: function(templateName, dataExpression) {
|
||||
if (!this.hasErrors()) {
|
||||
this.contextMethodCall("i", templateName, dataExpression);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
contextMethodCall: function(methodName, args) {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.contextMethodCall.apply(this.writer, arguments);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
addClassNameVar: function(className) {
|
||||
var classVarName = className.replace(/[^a-zA-Z0-9]+/g, '_');
|
||||
if (!this.hasStaticVar(classVarName)) {
|
||||
this.addStaticVar(classVarName, JSON.stringify(className));
|
||||
}
|
||||
return classVarName;
|
||||
},
|
||||
|
||||
addHelperFunction: function(className, functionName, bindToContext, targetVarName) {
|
||||
var classVarName = this.addClassNameVar(className);
|
||||
|
||||
|
||||
if (!targetVarName) {
|
||||
targetVarName = functionName;
|
||||
}
|
||||
|
||||
if (bindToContext === true) {
|
||||
if (this.hasVar(targetVarName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addVar(targetVarName, "context.f(" + classVarName + "," + JSON.stringify(functionName) + ")");
|
||||
}
|
||||
else {
|
||||
if (this.hasStaticVar(targetVarName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addStaticVar(targetVarName, this.getStaticHelperFunction("getHelper", "h") + "(" + classVarName + "," + JSON.stringify(functionName) + ")");
|
||||
}
|
||||
},
|
||||
|
||||
write: function(expression, options) {
|
||||
if (!this.hasErrors()) {
|
||||
|
||||
if (options) {
|
||||
if (options.escapeXml) {
|
||||
expression = this.getStaticHelperFunction("escapeXml", "x") + "(" + expression + ")";
|
||||
}
|
||||
if (options.escapeXmlAttr) {
|
||||
expression = this.getStaticHelperFunction("escapeXmlAttr", "xa") + "(" + expression + ")";
|
||||
}
|
||||
}
|
||||
|
||||
this.writer.write(expression);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
incIndent: function() {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.incIndent.apply(this.writer, arguments);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
decIndent: function() {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.decIndent.apply(this.writer, arguments);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
code: function(code) {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.code(code);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
statement: function(code) {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.statement(code);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
line: function(code) {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.line(code);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
indentStr: function(delta) {
|
||||
return this.writer.indentStr(delta);
|
||||
},
|
||||
|
||||
indent: function() {
|
||||
if (!this.hasErrors()) {
|
||||
this.writer.indent.apply(this.writer, arguments);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getPath: function() {
|
||||
return this.path;
|
||||
},
|
||||
|
||||
getFilePath: function() {
|
||||
return this.filePath;
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
if (this.hasErrors()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var out = strings.createStringBuilder();
|
||||
|
||||
var templateName = this.getTemplateName();
|
||||
if (!templateName) {
|
||||
|
||||
this.addError('Template name not defined in template at path "' + this.getFilePath() + '"');
|
||||
}
|
||||
|
||||
var params = this.params;
|
||||
if (params) {
|
||||
params = ["context"].concat(params);
|
||||
}
|
||||
else {
|
||||
params = ["context"];
|
||||
}
|
||||
|
||||
out.append('$rset("rhtml", ');
|
||||
out.append(stringify(templateName));
|
||||
out.append(', ');
|
||||
out.append('function(helpers, templateInfo) {\n');
|
||||
//Write out the static variables
|
||||
|
||||
this.writer.flush();
|
||||
|
||||
this._writeVars(this.staticVars, out, INDENT);
|
||||
out.append('\n' + INDENT + 'return function(data, context) {\n');
|
||||
|
||||
|
||||
//Write out the render variables
|
||||
if (this.vars && this.vars.length) {
|
||||
this._writeVars(this.vars, out, INDENT + INDENT);
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
out.append(this.writer.getOutput());
|
||||
|
||||
out.append(INDENT + '}\n});');
|
||||
return out.toString();
|
||||
},
|
||||
|
||||
setTemplateName: function(templateName) {
|
||||
this.templateName = templateName;
|
||||
},
|
||||
|
||||
makeExpression: function(expression) {
|
||||
if (expression instanceof Expression) {
|
||||
return expression;
|
||||
}
|
||||
else {
|
||||
return new Expression(expression);
|
||||
}
|
||||
},
|
||||
|
||||
isExpression: function(expression) {
|
||||
return expression instanceof Expression;
|
||||
},
|
||||
|
||||
getAttribute: function(name) {
|
||||
return this.attributes[name];
|
||||
},
|
||||
|
||||
setAttribute: function(name, value) {
|
||||
this.attributes[name] = value;
|
||||
return value;
|
||||
},
|
||||
|
||||
hasErrors: function() {
|
||||
return this.compiler.hasErrors();
|
||||
},
|
||||
|
||||
addError: function(message, pos) {
|
||||
this.compiler.addError(message, pos);
|
||||
},
|
||||
|
||||
getErrors: function() {
|
||||
return this.compiler.getErrors();
|
||||
},
|
||||
|
||||
getNodeClass: function(uri, localName) {
|
||||
return this.compiler.getNodeClass(uri, localName);
|
||||
},
|
||||
|
||||
transformTree: function(node) {
|
||||
this.compiler.transformTree(node, this);
|
||||
},
|
||||
|
||||
INDENT: INDENT
|
||||
|
||||
};
|
||||
|
||||
return TemplateBuilder;
|
||||
});
|
||||
299
lib/compiler/TemplateCompiler.js
Normal file
299
lib/compiler/TemplateCompiler.js
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/TemplateCompiler',
|
||||
['raptor'],
|
||||
function(raptor, require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var TemplateBuilder = require('raptor/templating/compiler/TemplateBuilder'),
|
||||
ParseTreeBuilder = require('raptor/templating/compiler/ParseTreeBuilder'),
|
||||
Expression = require('raptor/templating/compiler/Expression'),
|
||||
minifier = require.exists("raptor/js-minifier") ? require("raptor/js-minifier") : null,
|
||||
TypeConverter = require('raptor/templating/compiler/TypeConverter'),
|
||||
logger = module.logger();
|
||||
|
||||
/**
|
||||
* @param taglibs {raptor/templating/compiler/TaglibCollection} The collection of taglibs that are available to the compiler
|
||||
* @param options {object} The options for the compiler.
|
||||
*/
|
||||
var TemplateCompiler = function(taglibs, options) {
|
||||
this.taglibs = taglibs;
|
||||
this.options = options || {};
|
||||
this.workDir = this.options.workDir || require('raptor/templating/compiler').workDir || require('raptor/temp').workDir;
|
||||
this.errors = [];
|
||||
};
|
||||
|
||||
TemplateCompiler.prototype = {
|
||||
|
||||
/**
|
||||
* This method processes every node in the tree using a pre-order traversal.
|
||||
* That is, the parent node is transformed before its child nodes are
|
||||
* transformed.
|
||||
*
|
||||
* <p>
|
||||
* NOTE:
|
||||
* This method is repeatedly called until there are no more nodes in the tree
|
||||
* that need to be transformed. This is because transformers might add
|
||||
* new nodes to the tree in a position that has already been passed and
|
||||
* we want to make sure that all new nodes added to the tree are transformed
|
||||
* as necessary.
|
||||
*
|
||||
* @param node {raptor/templating/compiler/Node} The root node to transform
|
||||
* @param templateBuilder {raptor/templating/compiler/TemplateBuilder} The template builder object that is used to control how the compiled code is generated
|
||||
*/
|
||||
transformTree: function(rootNode, templateBuilder) {
|
||||
if (!templateBuilder) {
|
||||
throw raptor.createError(new Error("The templateBuilder argument is required"));
|
||||
}
|
||||
|
||||
var transformTreeHelper = function(node) {
|
||||
try
|
||||
{
|
||||
this.taglibs.forEachNodeTransformer( //Handle all of the transformers that are appropriate for this node
|
||||
node, //The node being transformed
|
||||
function(transformer) {
|
||||
if (!node.isTransformerApplied(transformer)) { //Check to make sure a transformer of a certain type is only applied once to a node
|
||||
node.setTransformerApplied(transformer); //Mark the node as have been transformed by the current transformer
|
||||
this._transformerApplied = true; //Set the flag to indicate that a node was transformed
|
||||
node.compiler = this;
|
||||
transformer.getInstance().process(node, this, templateBuilder); //Have the transformer process the node (NOTE: Just because a node is being processed by the transformer doesn't mean that it has to modify the parse tree)
|
||||
}
|
||||
},
|
||||
this);
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError(new Error('Unable to compile template at path "' + templateBuilder.filePath + ". Error: " + e.message), e);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Now process the child nodes by looping over the child nodes
|
||||
* and transforming the subtree recursively
|
||||
*
|
||||
* NOTE: The length of the childNodes array might change as the tree is being performed.
|
||||
* The checks to prevent transformers from being applied multiple times makes
|
||||
* sure that this is not a problem.
|
||||
*/
|
||||
|
||||
node.forEachChild(function(childNode) {
|
||||
if (!childNode.parentNode) {
|
||||
return; //The child node might have been removed from the tree
|
||||
}
|
||||
transformTreeHelper.call(this, childNode);
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The tree is continuously transformed until we go through an entire pass where
|
||||
* there were no new nodes that needed to be transformed. This loop makes sure that
|
||||
* nodes added by transformers are also transformed.
|
||||
*/
|
||||
do
|
||||
{
|
||||
this._transformerApplied = false; //Reset the flag to indicate that no transforms were yet applied to any of the nodes for this pass
|
||||
transformTreeHelper.call(this, rootNode); //Run the transforms on the tree
|
||||
}
|
||||
while (this._transformerApplied);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compiles the XML source code for a template and returns the resulting compiled JavaScript code.
|
||||
*
|
||||
* <p>
|
||||
* When the returned code is evaluated by a JavaScript engine it will register the function
|
||||
* to render the template. The function is registered with the name found as the "name" attribute
|
||||
* of the root <c:template> element unless a template name is passed in as a compiler option.
|
||||
*
|
||||
*
|
||||
* @param xmlSrc {String} The XML source code for the template
|
||||
* @param filePath {String} The path to the input template for debugging/error reporting only
|
||||
* @returns {String} The JavaScript code for the compiled template
|
||||
*/
|
||||
compile: function(xmlSrc, resource, callback, thisObj) {
|
||||
var _this = this,
|
||||
rootNode,
|
||||
templateBuilder,
|
||||
handleErrors = function() {
|
||||
var message = "Errors in template:\n",
|
||||
errors = _this.getErrors();
|
||||
|
||||
for (var i=0, len=errors.length; i<len; i++) {
|
||||
message += (i+1) + ") " + (errors[i].pos ? "[" + errors[i].pos + "] " : "") + errors[i].message + "\n";
|
||||
}
|
||||
|
||||
var error = new Error(message);
|
||||
error.errors = _this.getErrors();
|
||||
throw error;
|
||||
};
|
||||
|
||||
var filePath;
|
||||
|
||||
if (require('raptor/resources').isResource(resource)) {
|
||||
filePath = resource.getURL();
|
||||
}
|
||||
else if (typeof resource === 'string'){
|
||||
filePath = resource;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
/*
|
||||
* First build the parse tree for the tempate
|
||||
*/
|
||||
rootNode = ParseTreeBuilder.parse(xmlSrc, filePath, this.taglibs); //Build a parse tree from the input XML
|
||||
templateBuilder = new TemplateBuilder(this, resource, rootNode); //The templateBuilder object is need to manage the compiled JavaScript output
|
||||
this.transformTree(rootNode, templateBuilder);
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError(new Error('An error occurred while trying to compile template at path "' + filePath + '". Exception: ' + e), e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
/*
|
||||
* The tree has been transformed and we can now generate
|
||||
*/
|
||||
rootNode.generateCode(templateBuilder); //Generate the code and have all output be managed by the TemplateBuilder
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError(new Error('An error occurred while trying to compile template at path "' + filePath + '". Exception: ' + e), e);
|
||||
}
|
||||
|
||||
if (this.hasErrors()) {
|
||||
handleErrors();
|
||||
}
|
||||
|
||||
var output = templateBuilder.getOutput(); //Get the compiled output from the template builder
|
||||
//console.error('COMPILED TEMPLATE (' + filePath + ')\n', '\n' + output, '\n------------------');
|
||||
|
||||
if (minifier && this.options.minify === true) {
|
||||
output = minifier.minify(output);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback.call(thisObj, {
|
||||
source: output,
|
||||
templateName: templateBuilder.getTemplateName()
|
||||
});
|
||||
}
|
||||
|
||||
var options = this.options;
|
||||
|
||||
if (options && options.nameCallback) {
|
||||
options.nameCallback(templateBuilder.getTemplateName());
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param xmlSrc {String} The XML source code for the template
|
||||
* @param filePath {String} The path to the input template for debugging/error reporting only
|
||||
* @return {void}
|
||||
*/
|
||||
compileAndLoad: function(xmlSrc, resource) {
|
||||
var compiledSrc = this.compile(xmlSrc, resource, function(result) { //Get the compiled output for the template
|
||||
require('raptor/templating').unload(result.templateName); //Unload any existing template with the same name
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
//console.log('compileAndLoad: ' + (resource ? resource.getURL() : '(no resource') + ': ' + compiledSrc);
|
||||
this._eval(compiledSrc, resource); //Evaluate the compiled code and register the template
|
||||
}
|
||||
catch(e) {
|
||||
var filePath;
|
||||
if (typeof resource === 'string') {
|
||||
filePath = resource;
|
||||
}
|
||||
else if (require('raptor/resources').isResource(resource)) {
|
||||
filePath = resource.getURL();
|
||||
}
|
||||
logger.error("Unable to load compiled template: " + compiledSrc, e);
|
||||
throw raptor.createError(new Error('Unable to load template at path "' + filePath + '". Exception: ' + e.message), e);
|
||||
}
|
||||
},
|
||||
|
||||
_eval: function(compiledSrc, resource) {
|
||||
eval(compiledSrc);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the provided object is an Expression object, false otherwise
|
||||
* @param expression {Object} The object to test
|
||||
* @returns {Boolean} True if the provided object is an Expression object, false otherwise
|
||||
*/
|
||||
isExpression: function(expression) {
|
||||
return expression instanceof Expression;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uri
|
||||
* @param localName
|
||||
* @returns {TagHandlerNode}
|
||||
*/
|
||||
createTagHandlerNode: function(uri, localName) {
|
||||
var TagHandlerNode = require("raptor/templating/taglibs/core/TagHandlerNode");
|
||||
var tag = this.taglibs.getTag(uri, localName);
|
||||
var tagHandlerNode = new TagHandlerNode(tag);
|
||||
return tagHandlerNode;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
* @param type
|
||||
* @param allowExpressions
|
||||
* @returns
|
||||
*/
|
||||
convertType: function(value, type, allowExpressions) {
|
||||
return TypeConverter.convert(value, type, allowExpressions);
|
||||
},
|
||||
|
||||
addError: function(message, pos) {
|
||||
this.errors.push({message: message, pos: pos});
|
||||
},
|
||||
|
||||
hasErrors: function() {
|
||||
return this.errors.length !== 0;
|
||||
},
|
||||
|
||||
getErrors: function() {
|
||||
return this.errors;
|
||||
},
|
||||
|
||||
getNodeClass: function(uri, localName) {
|
||||
var tag = this.taglibs.getTag(uri, localName);
|
||||
if (tag && tag.nodeClass) {
|
||||
return require(tag.nodeClass);
|
||||
}
|
||||
throw raptor.createError(new Error('Node class not found for uri "' + uri + '" and localName "' + localName + '"'));
|
||||
},
|
||||
|
||||
createTag: function() {
|
||||
var Taglib = require('raptor/templating/compiler/Taglib');
|
||||
return new Taglib.Tag();
|
||||
}
|
||||
};
|
||||
|
||||
return TemplateCompiler;
|
||||
});
|
||||
38
lib/compiler/TemplateCompiler_node.js
Normal file
38
lib/compiler/TemplateCompiler_node.js
Normal file
@ -0,0 +1,38 @@
|
||||
var nodeRequire = require;
|
||||
|
||||
define.extend('raptor/templating/compiler/TemplateCompiler', function(require, target) {
|
||||
"use strict";
|
||||
|
||||
var vm = require('vm'),
|
||||
raptor = require('raptor');
|
||||
|
||||
return {
|
||||
_eval: function(compiledSrc, resource) {
|
||||
|
||||
var filePath;
|
||||
if (resource) {
|
||||
if (typeof resource === 'string') {
|
||||
filePath = resource;
|
||||
}
|
||||
else if (require('raptor/resources').isResource(resource)) {
|
||||
filePath = resource.isFileResource() ? resource.getFilePath() : resource.getURL();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (filePath && require('raptor/files').exists(filePath)) { //This is a hack, but line numbers for SyntaxErrors are getting lost if we don't use require
|
||||
delete nodeRequire.cache[filePath];
|
||||
nodeRequire(filePath);
|
||||
}
|
||||
else {
|
||||
vm.runInThisContext(compiledSrc, filePath || null);
|
||||
}
|
||||
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError(new Error('Unable to load compile templated at path "' + filePath + '". Exception: ' + e), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
19
lib/compiler/TemplateCompiler_rhino.js
Normal file
19
lib/compiler/TemplateCompiler_rhino.js
Normal file
@ -0,0 +1,19 @@
|
||||
define.extend('raptor/templating/compiler/TemplateCompiler', function(require, target) {
|
||||
"use strict";
|
||||
|
||||
return {
|
||||
_eval: function(compiledSrc, resource) {
|
||||
var filePath;
|
||||
if (resource) {
|
||||
if (typeof resource === 'string') {
|
||||
filePath = resource;
|
||||
}
|
||||
else if (require('raptor/resources').isResource(resource)) {
|
||||
filePath = resource.getURL();
|
||||
}
|
||||
}
|
||||
|
||||
__rhinoHelpers.runtime.evaluateString(compiledSrc, filePath || null);
|
||||
}
|
||||
};
|
||||
});
|
||||
127
lib/compiler/TemplateCompiler_server.js
Normal file
127
lib/compiler/TemplateCompiler_server.js
Normal file
@ -0,0 +1,127 @@
|
||||
define.extend('raptor/templating/compiler/TemplateCompiler', ['raptor'], function(raptor, require, target) {
|
||||
"use strict";
|
||||
|
||||
var File = require('raptor/files/File'),
|
||||
resources = require('raptor/resources'),
|
||||
optionsKey = function(options) {
|
||||
|
||||
var key = JSON.stringify(options);
|
||||
|
||||
var crypto = require('crypto');
|
||||
|
||||
var shasum = crypto.createHash('sha1');
|
||||
shasum.update(key);
|
||||
var checksum = shasum.digest('hex');
|
||||
if (checksum.length > 5) {
|
||||
checksum = checksum.substring(0, 5);
|
||||
}
|
||||
return checksum;
|
||||
},
|
||||
logger = require('raptor/logging').logger('raptor/templating/compiler/TemplateCompiler_server');
|
||||
|
||||
return {
|
||||
_getOptionsKey: function() {
|
||||
if (!this._optionsKey) {
|
||||
this._optionsKey = optionsKey(this.options);
|
||||
}
|
||||
|
||||
return this._optionsKey;
|
||||
},
|
||||
|
||||
_getWorkFile: function(resource) {
|
||||
if (!this.workDir || !resource || !resource.isFileResource()) {
|
||||
return null;
|
||||
}
|
||||
//var optionsKey = this._getOptionsKey();
|
||||
var path = typeof resource === 'string' ? resource : resource.getPath();
|
||||
var workFile = new File(this.workDir, path + '.js');
|
||||
return workFile;
|
||||
},
|
||||
|
||||
/**
|
||||
* [_compileResource description]
|
||||
* @param {[type]} resource [description]
|
||||
* @return {[type]} [description]
|
||||
* @private
|
||||
*/
|
||||
_compileResource: function(path) {
|
||||
var resource = resources.findResource(path);
|
||||
if (!resource.exists()) {
|
||||
throw raptor.createError(new Error('Unable to compile template with resource path "' + path + '". Resource not found'));
|
||||
}
|
||||
|
||||
var compiledSource,
|
||||
outputPath,
|
||||
workFile;
|
||||
|
||||
if (this.workDir) {
|
||||
workFile = this._getWorkFile(resource);
|
||||
}
|
||||
|
||||
if (!workFile || !workFile.exists() || resource.lastModified() > workFile.lastModified()) {
|
||||
// The work file does not exist or it is out-of-date so we need to read the resource and compile
|
||||
var xmlSource = resource.readAsString();
|
||||
compiledSource = this.compile(xmlSource, resource);
|
||||
|
||||
if (workFile) { // If there is a work file then write out the compiled source so that we don't have to recompile again until the input resource is modified
|
||||
workFile.writeAsString(compiledSource);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// The work file exists and it is up-to-date so we can use that to return the compiled source
|
||||
compiledSource = workFile.readAsString();
|
||||
}
|
||||
|
||||
if (workFile) {
|
||||
outputPath = workFile.getAbsolutePath();
|
||||
}
|
||||
else {
|
||||
outputPath = resource.getURL() + ".js";
|
||||
}
|
||||
|
||||
return {
|
||||
templateResource: resource,
|
||||
compiledSource: compiledSource,
|
||||
outputPath: outputPath,
|
||||
outputFile: workFile
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
compileResource: function(path) {
|
||||
var compiledInfo = this._compileResource(path);
|
||||
return compiledInfo.compiledSource;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
compileAndLoadResource: function(path) {
|
||||
var compiledInfo = this._compileResource(path);
|
||||
this._eval(compiledInfo.compiledSource, compiledInfo.outputPath);
|
||||
var resource = compiledInfo.templateResource;
|
||||
|
||||
if (require('raptor/templating/compiler').isWatchingEnabled()) {
|
||||
resource.watch(
|
||||
function() {
|
||||
console.log('Template modified at path "' + resource.getURL() + '". Reloading template...');
|
||||
try
|
||||
{
|
||||
this.compileAndLoadResource(resource);
|
||||
}
|
||||
catch(e) {
|
||||
logger.warn('Unable to re-compile modified template at path "' + resource.getURL() + '". Exception: ' + e, e);
|
||||
}
|
||||
},
|
||||
this);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
215
lib/compiler/TextNode.js
Normal file
215
lib/compiler/TextNode.js
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/TextNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var escapeXml = require('raptor/xml/utils').escapeXml,
|
||||
attrReplace = /[&<>\"\']/g,
|
||||
replacements = {
|
||||
'<': "<",
|
||||
'>': ">",
|
||||
'&': "&",
|
||||
'"': """,
|
||||
"'": "'"
|
||||
},
|
||||
escapeXmlAttr = function(str) {
|
||||
return str.replace(attrReplace, function(match) {
|
||||
return replacements[match];
|
||||
});
|
||||
},
|
||||
EscapeXmlContext = require('raptor/templating/compiler/EscapeXmlContext');
|
||||
|
||||
var TextNode = function(text, escapeXml) {
|
||||
TextNode.superclass.constructor.call(this, 'text');
|
||||
if (text != null && typeof text !== 'string') {
|
||||
throw raptor.createError('Invalid text: ' + text);
|
||||
}
|
||||
this.text = text;
|
||||
this.escapeXml = escapeXml !== false;
|
||||
};
|
||||
|
||||
TextNode.prototype = {
|
||||
|
||||
normalizeText: function() {
|
||||
var normalizedText = this.getEscapedText(),
|
||||
curChild = this.nextSibling,
|
||||
nodeToRemove;
|
||||
|
||||
while(curChild && curChild.isTextNode()) {
|
||||
normalizedText += curChild.getEscapedText();
|
||||
nodeToRemove = curChild;
|
||||
curChild = curChild.nextSibling;
|
||||
nodeToRemove.detach();
|
||||
}
|
||||
|
||||
this.setText(normalizedText);
|
||||
this.escapeXml = false; //Make sure the text is not re-escaped
|
||||
},
|
||||
|
||||
getEscapedText: function() {
|
||||
var text = this.getText();
|
||||
var parentNode = this.parentNode;
|
||||
var shouldEscapeXml = this.escapeXml !== false && parentNode && parentNode.isEscapeXmlBodyText() !== false;
|
||||
if (shouldEscapeXml) {
|
||||
if (this.getEscapeXmlContext() === EscapeXmlContext.Attribute) {
|
||||
return escapeXmlAttr(text);
|
||||
}
|
||||
else {
|
||||
return escapeXml(text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
|
||||
trim: function() {
|
||||
var text = this.getText();
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentNode = this.parentNode;
|
||||
|
||||
if (parentNode &&
|
||||
parentNode.trimBodyIndent) {
|
||||
|
||||
var initialSpaceMatches = /^\s+/.exec(text);
|
||||
|
||||
|
||||
if (initialSpaceMatches) {
|
||||
//console.error(JSON.stringify(initialSpaceMatches[0]));
|
||||
|
||||
var indentMatches = /\n[^\n]*$/.exec(initialSpaceMatches[0]);
|
||||
if (indentMatches) {
|
||||
//console.error(JSON.stringify(indentMatches[0]));
|
||||
var indentRegExp = new RegExp(indentMatches[0].replace(/\n/g, "\\n"), "g");
|
||||
text = text.replace(indentRegExp, '\n');
|
||||
}
|
||||
|
||||
text = text.replace(/^\s*/, '').replace(/\s*$/, '');
|
||||
|
||||
this.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isPreserveWhitespace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.previousSibling) {
|
||||
//First child
|
||||
text = text.replace(/^\n\s*/g, "");
|
||||
}
|
||||
if (!this.nextSibling) {
|
||||
//Last child
|
||||
text = text.replace(/\n\s*$/g, "");
|
||||
}
|
||||
|
||||
if (/^\n\s*$/.test(text)) { //Whitespace between elements
|
||||
text = '';
|
||||
}
|
||||
|
||||
text = text.replace(/\s+/g, " ");
|
||||
|
||||
if (this.isWordWrapEnabled() && text.length > 80) {
|
||||
var start=0,
|
||||
end;
|
||||
|
||||
while (start < text.length) {
|
||||
end = Math.min(start+80, text.length);
|
||||
|
||||
var lastSpace = text.substring(start, end).lastIndexOf(' ');
|
||||
if (lastSpace != -1) {
|
||||
lastSpace = lastSpace + start; //Adjust offset into original string
|
||||
}
|
||||
else {
|
||||
lastSpace = text.indexOf(' ', end); //No space before the 80 column mark... search for the first space after to break on
|
||||
}
|
||||
|
||||
if (lastSpace != -1) {
|
||||
text = text.substring(0, lastSpace) + "\n" + text.substring(lastSpace+1);
|
||||
start = lastSpace + 1;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
this.setText(text);
|
||||
},
|
||||
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
/*
|
||||
* After all of the transformation of the tree we
|
||||
* might have ended up with multiple text nodes
|
||||
* as siblings. We want to normalize adjacent
|
||||
* text nodes so that whitespace removal rules
|
||||
* will be correct
|
||||
*/
|
||||
this.normalizeText();
|
||||
this.trim();
|
||||
|
||||
var text = this.getText();
|
||||
if (text) {
|
||||
template.text(text);
|
||||
}
|
||||
},
|
||||
|
||||
getText: function() {
|
||||
return this.text;
|
||||
},
|
||||
|
||||
setText: function(text) {
|
||||
this.text = text;
|
||||
},
|
||||
|
||||
isTextNode: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
isElementNode: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
setEscapeXml: function(escapeXml) {
|
||||
this.escapeXml = escapeXml;
|
||||
},
|
||||
|
||||
isEscapeXml: function() {
|
||||
return this.escapeXml;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
var text = this.text && this.text.length > 25 ? this.text.substring(0, 25) + '...' : this.text;
|
||||
text = text.replace(/[\n]/g, '\\n');
|
||||
return "[text: " + text + "]";
|
||||
}
|
||||
};
|
||||
|
||||
return TextNode;
|
||||
});
|
||||
107
lib/compiler/TypeConverter.js
Normal file
107
lib/compiler/TypeConverter.js
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/compiler/TypeConverter',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var ExpressionParser = require('raptor/templating/compiler/ExpressionParser'),
|
||||
stringify = require('raptor/json/stringify'),
|
||||
Expression = require('raptor/templating/compiler/Expression');
|
||||
|
||||
var TypeConverter = function() {
|
||||
|
||||
};
|
||||
|
||||
TypeConverter.convert = function(value, targetType, allowExpressions) {
|
||||
|
||||
|
||||
var hasExpression = false,
|
||||
expressionParts = [];
|
||||
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (targetType === 'custom' || targetType === 'identifier') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (targetType === 'expression') {
|
||||
if (value === '') {
|
||||
value = 'null';
|
||||
}
|
||||
|
||||
return new Expression(value);
|
||||
}
|
||||
|
||||
var processedText = '';
|
||||
|
||||
if (allowExpressions) {
|
||||
ExpressionParser.parse(value, {
|
||||
text: function(text) {
|
||||
processedText += text;
|
||||
expressionParts.push(stringify(text));
|
||||
},
|
||||
|
||||
expression: function(expression) {
|
||||
expressionParts.push(expression);
|
||||
hasExpression = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasExpression) {
|
||||
return new Expression(expressionParts.join("+"));
|
||||
}
|
||||
value = processedText;
|
||||
}
|
||||
|
||||
if (targetType === 'string') {
|
||||
return allowExpressions ? new Expression(value ? stringify(value) : "null") : value;
|
||||
}
|
||||
else if (targetType === 'boolean') {
|
||||
if (allowExpressions) {
|
||||
return new Expression(value);
|
||||
}
|
||||
else {
|
||||
value = value.toLowerCase();
|
||||
value = value === 'true' || value === 'yes'; //convert it to a boolean
|
||||
return value;
|
||||
}
|
||||
}
|
||||
else if (targetType === 'float' || targetType === 'double' || targetType === 'number' || targetType === 'integer' || targetType === 'int') {
|
||||
if (allowExpressions) {
|
||||
return new Expression(value);
|
||||
}
|
||||
else {
|
||||
if (targetType === 'integer') {
|
||||
value = parseInt(value, 10);
|
||||
}
|
||||
else {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw raptor.createError(new Error("Unsupported attribute type: " + targetType));
|
||||
}
|
||||
};
|
||||
|
||||
return TypeConverter;
|
||||
});
|
||||
222
lib/compiler/compiler.js
Normal file
222
lib/compiler/compiler.js
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define(
|
||||
"raptor/templating/compiler",
|
||||
function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var TaglibCollection = require('raptor/templating/compiler/TaglibCollection'),
|
||||
taglibs = new TaglibCollection(),
|
||||
extend = require('raptor').extend,
|
||||
ExpressionParser = require('raptor/templating/compiler/ExpressionParser'),
|
||||
defaultOptions = {
|
||||
minify: false,
|
||||
preserveWhitespace: {
|
||||
'pre': true,
|
||||
'textarea': true
|
||||
},
|
||||
allowSelfClosing: { //Conditionally enable self-closing tags for specific elements. Self-closing tag: <div /> Not self-closing tag: <div></div>
|
||||
//'pre': true
|
||||
},
|
||||
startTagOnly: {
|
||||
'img': true,
|
||||
'br': true,
|
||||
'input': true,
|
||||
'meta': true,
|
||||
'link': true,
|
||||
'hr': true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Creates a new object that can be used to compile templates with the
|
||||
* provided options.
|
||||
*
|
||||
* <p>
|
||||
* Allowed options:
|
||||
* <ul>
|
||||
* <li>
|
||||
* <b>preserveWhitespace</b> (object|boolean): An object that defines which elements should
|
||||
* have their whitespace preserved. While most whitespace gets normalized
|
||||
* in HTML documents, some HTML elements make use of their whitespace (e.g. PRE and TEXTAREA tags).
|
||||
* If this option is set to "true" then all whitespace is preserved.
|
||||
*
|
||||
* <p>
|
||||
* Default value:
|
||||
<pre>
|
||||
{
|
||||
'pre': true,
|
||||
'textarea': true
|
||||
}
|
||||
</pre>
|
||||
* </li>
|
||||
* <li>
|
||||
* <b>allowSelfClosing</b> (object): An object that defines which elements are allowed
|
||||
* to be self-closing. By default, all elements are allowed to be self-closing.
|
||||
* Some browsers do not handle certain HTML elements that are self-closing
|
||||
* and require a separate ending tag.
|
||||
*
|
||||
* <p>
|
||||
* Default value:
|
||||
<pre>
|
||||
allowSelfClosing: {
|
||||
'script': false,
|
||||
'div': false
|
||||
}
|
||||
</pre>
|
||||
* </li>
|
||||
* <li>
|
||||
* <b>startTagOnly</b> (object): An object that defines which elements should only be
|
||||
* written out with the opening tag and not the closing tags. For HTML5
|
||||
* output that is not well-formed XML it is acceptable to write
|
||||
* certain elements with the opening tag only.
|
||||
*
|
||||
* <p>
|
||||
* Default value:
|
||||
<pre>
|
||||
startTagOnly: {
|
||||
'img': true,
|
||||
'br': true
|
||||
}
|
||||
</pre>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param options Compiler options (see above)
|
||||
* @returns {raptor/templating/compiler$TemplateCompiler} The newly created compiler
|
||||
*/
|
||||
createCompiler: function(options) {
|
||||
if (this.discoverTaglibs) { //Only discover taglibs if that method is implemented
|
||||
this.discoverTaglibs(); //The discoverTaglibs method is implemented on the server so execute it now
|
||||
}
|
||||
|
||||
var TemplateCompiler = require("raptor/templating/compiler/TemplateCompiler"); //Get a reference to the TemplateCompiler class
|
||||
if (options) {
|
||||
/*
|
||||
* If options were provided then they should override the default options.
|
||||
* NOTE: Only top-level properties are overridden
|
||||
*/
|
||||
options = extend(
|
||||
extend({}, defaultOptions), //Create a clone of the default options that can be extended
|
||||
options);
|
||||
}
|
||||
else {
|
||||
options = defaultOptions; //Otherwise, no options were provided so use the default options
|
||||
}
|
||||
|
||||
return new TemplateCompiler(taglibs, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compiles an XML template by creating a new compiler using the provided options and
|
||||
* then passing along the XML source code for the template to be compiled.
|
||||
*
|
||||
* For a list of options see {@link raptor/templating/compiler/createCompiler}
|
||||
*
|
||||
* @param xmlSource {String} The XML source code for the template to compile
|
||||
* @param path {String} The path to the template (for debugging/error reporting purposes only)
|
||||
* @returns {String} The JavaScript code for the compiled template.
|
||||
*/
|
||||
compile: function(xmlSource, path, options) {
|
||||
return this.createCompiler(options).compile(xmlSource, path);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param xmlSource {String} The XML source code for the template to compile
|
||||
* @param path {String} The path to the template (for debugging/error reporting purposes only)
|
||||
* @returns {void}
|
||||
*/
|
||||
compileAndLoad: function(xmlSource, path, options) {
|
||||
this.createCompiler(options).compileAndLoad(xmlSource, path);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param taglibXml
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
loadTaglibXml: function(taglibXml, path) {
|
||||
var TaglibXmlLoader = require("raptor/templating/compiler/TaglibXmlLoader");
|
||||
var taglib = TaglibXmlLoader.load(taglibXml, path);
|
||||
this.addTaglib(taglib);
|
||||
return taglib;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a {@link raptor/templating/compiler/Taglib} instance to the internal {@link raptor/templating/compiler/TaglibCollection} so
|
||||
* that the taglib is available to all compilers.
|
||||
*
|
||||
* @param taglib {raptor/templating/compiler/Taglib} The taglib to add
|
||||
* @returns {void}
|
||||
*/
|
||||
addTaglib: function(taglib) {
|
||||
taglibs.add(taglib);
|
||||
},
|
||||
|
||||
addTaglibAlias: function(uri, alias) {
|
||||
taglibs.addAlias(uri, alias);
|
||||
},
|
||||
|
||||
clearTaglibs: function() {
|
||||
this.taglibs = taglibs = new TaglibCollection();
|
||||
},
|
||||
|
||||
hasTaglib: function(uri) {
|
||||
return taglibs.isTaglib(uri);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Registers a custom expression handler with the given name and handler function.
|
||||
*
|
||||
* <p>
|
||||
* Custom expression handlers are functions that can be used to control the compiled output
|
||||
* of certain expressions. Custom expression handlers are of the following form:
|
||||
* ${<handler-name>:<custom-expression>}
|
||||
*
|
||||
* <p>
|
||||
* When a custom expression handler is used in a template then the provided handler
|
||||
* function will be invoked with two arguments:
|
||||
* <ul>
|
||||
* <li><b>customExpression</b> (String) The custom expression provided in the template the (the part after the colon)</li>
|
||||
* <li><b>helper</b> (ExpressionParserHelper) The helper that can be used to control the compiled output</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param name
|
||||
* @param func
|
||||
* @returns
|
||||
*/
|
||||
registerCustomExpressionHandler: function(name, func) {
|
||||
ExpressionParser.custom[name] = func;
|
||||
},
|
||||
|
||||
recordLoadedTaglib: function(taglibResource) {
|
||||
// No-op by default
|
||||
},
|
||||
|
||||
defaultOptions: defaultOptions,
|
||||
|
||||
taglibs: taglibs
|
||||
};
|
||||
});
|
||||
52
lib/compiler/compiler_browser.js
Normal file
52
lib/compiler/compiler_browser.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extension Server
|
||||
*/
|
||||
define.extend(
|
||||
"raptor/templating/compiler",
|
||||
function(require, compiler) {
|
||||
"use strict";
|
||||
|
||||
var raptor = require('raptor'),
|
||||
discoveryComplete = false;
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
discoverTaglibs: function() {
|
||||
if (discoveryComplete) {
|
||||
return;
|
||||
}
|
||||
discoveryComplete = true;
|
||||
|
||||
var taglibPaths = $rget("rtld");
|
||||
|
||||
raptor.forEach(taglibPaths, function(path) {
|
||||
var resource = require('raptor/resources').findResource(path);
|
||||
if (resource && resource.exists()) {
|
||||
this.loadTaglibXml(resource.readAsString(), resource.getPath());
|
||||
}
|
||||
|
||||
}, this);
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
65
lib/compiler/compiler_rhino.js
Normal file
65
lib/compiler/compiler_rhino.js
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extension Rhino
|
||||
*/
|
||||
define.extend(
|
||||
"raptor/templating/compiler",
|
||||
function(require, compiler) {
|
||||
"use strict";
|
||||
|
||||
var java = require("raptor/java");
|
||||
|
||||
var convertJavaOptions = function(javaOptions) {
|
||||
var options = {};
|
||||
options.templateName = javaOptions.templateName;
|
||||
return options;
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param src
|
||||
* @param path
|
||||
* @param javaOptions
|
||||
* @returns
|
||||
*/
|
||||
rhinoCompile: function(src, path, javaOptions) {
|
||||
return this.compile(src, path, convertJavaOptions(javaOptions));
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @param javaOptions
|
||||
* @returns
|
||||
*/
|
||||
rhinoCompileResource: function(path, javaOptions) {
|
||||
return this.compileResource(path, convertJavaOptions(javaOptions));
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @param javaOptions
|
||||
* @returns
|
||||
*/
|
||||
rhinoCompileAndLoadResource: function(path, javaOptions) {
|
||||
return this.compileAndLoadResource(path, convertJavaOptions(javaOptions));
|
||||
}
|
||||
};
|
||||
});
|
||||
302
lib/compiler/compiler_server.js
Normal file
302
lib/compiler/compiler_server.js
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extension Server
|
||||
*/
|
||||
define.extend(
|
||||
"raptor/templating/compiler",
|
||||
function(require, compiler) {
|
||||
"use strict";
|
||||
|
||||
var logger = require('raptor/logging').logger("raptor/templating/compiler"),
|
||||
raptor = require('raptor'),
|
||||
resources = require('raptor/resources'),
|
||||
packaging = require('raptor/packaging'),
|
||||
discoveryComplete = false,
|
||||
searchPathListenerHandler = null,
|
||||
watchingEnabled = false,
|
||||
loadedTaglibPaths = {},
|
||||
taglibsLastModified = null;
|
||||
|
||||
Object.defineProperty(compiler, "workDir", {
|
||||
get: function() {
|
||||
return this._workDir;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
this.setWorkDir(val);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
|
||||
enableWatching: function() {
|
||||
watchingEnabled = true;
|
||||
},
|
||||
|
||||
disableWatching: function() {
|
||||
watchingEnabled = false;
|
||||
},
|
||||
|
||||
isWatchingEnabled: function() {
|
||||
return watchingEnabled;
|
||||
},
|
||||
|
||||
setWatchingEnabled: function(enabled) {
|
||||
watchingEnabled = enabled;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
compileAndLoadResource: function(path, options) {
|
||||
this.createCompiler(options).compileAndLoadResource(path);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
compileResource: function(path, options) {
|
||||
return this.createCompiler(options).compileResource(path);
|
||||
},
|
||||
|
||||
loadModuleTaglibs: function(moduleName) {
|
||||
var manifest = require('raptor/packaging').getModuleManifest(moduleName);
|
||||
if (manifest) {
|
||||
this.loadPackageTaglibs(manifest);
|
||||
}
|
||||
},
|
||||
|
||||
loadPackageTaglibs: function(manifest) {
|
||||
var taglibs = manifest.getRaptorProp('taglibs');
|
||||
if (taglibs) {
|
||||
raptor.forEach(taglibs, function(rtldPath) {
|
||||
var key = manifest.getURL() + ':' + rtldPath;
|
||||
if (!loadedTaglibPaths[key]) {
|
||||
loadedTaglibPaths[key] = true;
|
||||
|
||||
var rtldResource = manifest.resolveResource(rtldPath);
|
||||
if (!rtldResource || !rtldResource.exists()) {
|
||||
throw raptor.createError(new Error('Raptor TLD "' + rtldPath + '" not found for manifest "' + manifest.getURL() + '"'));
|
||||
}
|
||||
this.loadTaglib(rtldResource);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
findAndLoadTaglib: function(uri) {
|
||||
|
||||
var pathBuilders = [
|
||||
function(uri) {
|
||||
var path = uri;
|
||||
if (!path.endsWith('.rtld')) {
|
||||
path += '.rtld';
|
||||
}
|
||||
|
||||
if (!path.startsWith('/')) {
|
||||
path = '/' + path;
|
||||
}
|
||||
return path;
|
||||
},
|
||||
function(uri) {
|
||||
var lastSlash = uri.lastIndexOf('/');
|
||||
var shortName = lastSlash === -1 ? uri : uri.substring(lastSlash+1);
|
||||
path = uri + '/' + shortName;
|
||||
if (!path.endsWith('.rtld')) {
|
||||
path += '.rtld';
|
||||
}
|
||||
|
||||
if (!path.startsWith('/')) {
|
||||
path = '/' + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
];
|
||||
|
||||
for (var i=0, len=pathBuilders.length; i<len; i++) {
|
||||
var pathBuilder = pathBuilders[i];
|
||||
var path = pathBuilder(uri);
|
||||
|
||||
var taglibResource = require('raptor/resources').findResource(path);
|
||||
if (taglibResource && taglibResource.exists()) {
|
||||
var taglib = require('raptor/templating/compiler').loadTaglib(taglibResource);
|
||||
this.addTaglibAlias(taglib.uri, uri);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: see if the URI is associated with a module that registers
|
||||
// the taglibs...
|
||||
require('raptor/templating/compiler').loadModuleTaglibs(uri);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
discoverTaglibs: function(force) {
|
||||
if (discoveryComplete && force !== true) {
|
||||
return;
|
||||
}
|
||||
discoveryComplete = true;
|
||||
this.clearTaglibs();
|
||||
loadedTaglibPaths = {};
|
||||
|
||||
packaging.forEachTopLevelPackageManifest(this.loadPackageTaglibs, this);
|
||||
|
||||
resources.forEach("/rtlds", function(rtldsResource) {
|
||||
if (rtldsResource.isDirectory()) {
|
||||
rtldsResource.forEachChild(function(rtldResource) {
|
||||
if (rtldResource.isFile() && rtldResource.getName().endsWith(".rtld")) {
|
||||
this.loadTaglib(rtldResource);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!searchPathListenerHandler) {
|
||||
searchPathListenerHandler = require('raptor/resources').getSearchPath().subscribe("modified", function() {
|
||||
discoveryComplete = false;
|
||||
this.discoverTaglibs(); //If the search path is modified then rediscover the taglibs
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
loadTaglib: function(taglibResource) {
|
||||
|
||||
this.recordLoadedTaglib(taglibResource);
|
||||
var xml = taglibResource.readAsString();
|
||||
return this.loadTaglibXml(xml, taglibResource);
|
||||
},
|
||||
|
||||
recordLoadedTaglib: function(taglibResource) {
|
||||
|
||||
var workDir = this.getWorkDir();
|
||||
if (workDir) {
|
||||
|
||||
var taglibsLastModified = this._readTaglibsLastModified(workDir);
|
||||
taglibsLastModified.lastModified = Math.max(taglibResource.lastModified(), taglibsLastModified.lastModified || 0);
|
||||
taglibsLastModified.urls[taglibResource.getURL()] = true;
|
||||
var newStr = taglibsLastModified.lastModified + '\n' + Object.keys(taglibsLastModified.urls).join('\n');
|
||||
if (newStr != taglibsLastModified.written) {
|
||||
taglibsLastModified.written = newStr;
|
||||
this._taglibsWorkFile.writeAsString(newStr);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_readTaglibsLastModified: function(workDir) {
|
||||
var taglibsWorkFile = this._taglibsWorkFile;
|
||||
|
||||
if (taglibsLastModified == null) {
|
||||
taglibsLastModified = {
|
||||
lastModified: null,
|
||||
urls: {},
|
||||
written: null
|
||||
};
|
||||
|
||||
if (taglibsWorkFile.exists()) {
|
||||
try {
|
||||
taglibsLastModified.str = taglibsWorkFile.readAsString();
|
||||
var lastModifiedEnd = taglibsLastModified.str.indexOf('\n');
|
||||
taglibsLastModified.lastModified = parseInt(taglibsLastModified.str.substring(0, lastModifiedEnd), 10);
|
||||
|
||||
taglibsLastModified.str.substring(lastModifiedEnd+1).split('\n').forEach(function(url) {
|
||||
taglibsLastModified.urls[url] = true;
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
logger.warn('Unable to read "' + taglibsWorkFile.getAbsolutePath() + '". Exception: ' + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return taglibsLastModified;
|
||||
},
|
||||
|
||||
_validateWorkDir: function(workDir) {
|
||||
if (!workDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug('Validating work directory at path "' + workDir + '"...');
|
||||
|
||||
var taglibsLastModified = this._readTaglibsLastModified(workDir);
|
||||
|
||||
|
||||
function isUpToDate() {
|
||||
var lastModified = taglibsLastModified.lastModified;
|
||||
|
||||
if (lastModified == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var files = require('raptor/files');
|
||||
|
||||
var urls = Object.keys(taglibsLastModified.urls);
|
||||
for (var i=0, len=urls.length; i<len; i++) {
|
||||
var url = urls[i];
|
||||
if (url.startsWith('file://')) { // Only supporting file URLs for now...
|
||||
var taglibFile = files.fromFileUrl(url);
|
||||
if (!taglibFile.exists() || taglibFile.lastModified() > lastModified) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!isUpToDate()) {
|
||||
console.log('One ore more taglibs modified. Removing compiled templates work directory at path "' + workDir.getAbsolutePath() + '"...');
|
||||
workDir.remove();
|
||||
}
|
||||
},
|
||||
|
||||
setWorkDir: function(workDir) {
|
||||
if (workDir) {
|
||||
logger.debug('Setting work directory to "' + workDir + '"...');
|
||||
var File = require('raptor/files/File');
|
||||
|
||||
if (typeof workDir === 'string') {
|
||||
workDir = new File(workDir);
|
||||
}
|
||||
|
||||
this._workDir = workDir;
|
||||
this._taglibsWorkFile = new File(this._workDir, 'taglibs.txt');
|
||||
this._validateWorkDir(workDir);
|
||||
}
|
||||
else {
|
||||
this._workDir = null;
|
||||
this._taglibsWorkFile = null;
|
||||
}
|
||||
},
|
||||
|
||||
getWorkDir: function() {
|
||||
return this._workDir;
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
70
lib/compiler/optimizer.json
Normal file
70
lib/compiler/optimizer.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"package": "raptor/resources"
|
||||
},
|
||||
{
|
||||
"package": "raptor/regexp"
|
||||
},
|
||||
{
|
||||
"package": "raptor/strings"
|
||||
},
|
||||
{
|
||||
"package": "raptor/objects"
|
||||
},
|
||||
{
|
||||
"package": "raptor/listeners"
|
||||
},
|
||||
{
|
||||
"package": "raptor/json/stringify"
|
||||
},
|
||||
{
|
||||
"package": "raptor/xml/utils"
|
||||
},
|
||||
{
|
||||
"package": "raptor/xml/sax/object-mapper"
|
||||
},
|
||||
{
|
||||
"package": "raptor/temp"
|
||||
},
|
||||
"Expression.js",
|
||||
"ExpressionParser.js",
|
||||
"TypeConverter.js",
|
||||
"AttributeSplitter.js",
|
||||
"TemplateBuilder.js",
|
||||
"TemplateCompiler.js",
|
||||
"Taglib.js",
|
||||
"TaglibCollection.js",
|
||||
"compiler.js",
|
||||
"Node.js",
|
||||
"ElementNode.js",
|
||||
"TextNode.js",
|
||||
"ParseTreeBuilder.js",
|
||||
"TaglibXmlLoader.js",
|
||||
"EscapeXmlContext.js",
|
||||
{
|
||||
"path": "compiler_server.js",
|
||||
"if-extension": "server"
|
||||
},
|
||||
{
|
||||
"path": "TemplateCompiler_server.js",
|
||||
"if-extension": "server"
|
||||
},
|
||||
{
|
||||
"path": "compiler_rhino.js",
|
||||
"if-extension": "rhino"
|
||||
},
|
||||
{
|
||||
"path": "TemplateCompiler_rhino.js",
|
||||
"if-extension": "rhino"
|
||||
},
|
||||
{
|
||||
"path": "TemplateCompiler_node.js",
|
||||
"if-extension": "node"
|
||||
},
|
||||
{
|
||||
"path": "compiler_browser.js",
|
||||
"if-extension": "browser"
|
||||
}
|
||||
]
|
||||
}
|
||||
350
lib/index.js
Normal file
350
lib/index.js
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This module provides the runtime for rendering compiled templates.
|
||||
*
|
||||
*
|
||||
* <p>The code for the Raptor Templates compiler is kept separately
|
||||
* in the {@link raptor/templating/compiler} module.
|
||||
*/
|
||||
define('raptor/templating', ['raptor'], function(raptor, require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var getRegisteredTemplate = function(name) {
|
||||
return $rget('rhtml', name);
|
||||
},
|
||||
loadedTemplates = {},
|
||||
isArray = Array.isArray,
|
||||
createError = raptor.createError,
|
||||
StringBuilder = require('raptor/strings/StringBuilder'),
|
||||
escapeXml = require('raptor/xml/utils').escapeXml,
|
||||
escapeXmlAttr = require('raptor/xml/utils').escapeXmlAttr,
|
||||
renderContext = require('raptor/render-context'),
|
||||
Context = renderContext.Context,
|
||||
_getFunction = Context.classFunc,
|
||||
templating,
|
||||
/**
|
||||
* Helper function to return the singleton instance of a tag handler
|
||||
*
|
||||
* @param name {String} The class name of the tag handler
|
||||
* @returns {Object} The tag handler singleton instance.
|
||||
*/
|
||||
_getHandler = function(name) {
|
||||
var Handler = require(name), //Load the handler class
|
||||
instance;
|
||||
|
||||
if (Handler.process || Handler.render) {
|
||||
instance = Handler;
|
||||
}
|
||||
else if (!(instance = Handler.instance)) { //See if an instance has already been created
|
||||
instance = Handler.instance = new Handler(); //If not, create and store a new instance
|
||||
}
|
||||
|
||||
return instance; //Return the handler instance
|
||||
},
|
||||
/**
|
||||
* Helper function to check if an object is "empty". Different types of objects are handled differently:
|
||||
* 1) null/undefined: Null and undefined objects are considered empty
|
||||
* 2) String: The string is trimmed (starting and trailing whitespace is removed) and if the resulting string is an empty string then it is considered empty
|
||||
* 3) Array: If the length of the array is 0 then the array is considred empty
|
||||
*
|
||||
*/
|
||||
notEmpty = function(o) {
|
||||
if (Array.isArray(o) === true) {
|
||||
return o.length !== 0;
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
helpers = {
|
||||
|
||||
/**
|
||||
* Helper function to return a static helper function
|
||||
*
|
||||
* @function
|
||||
* @param uri
|
||||
* @param name
|
||||
* @returns {Function} The corresponding helper function. An exception is thrown if the helper function is not found
|
||||
*/
|
||||
h: _getFunction,
|
||||
|
||||
t: _getHandler,
|
||||
|
||||
/**
|
||||
* forEach helper function
|
||||
*
|
||||
* @param array {Array} The array to iterate over
|
||||
* @param callback {Function} The callback function to invoke for each iteration
|
||||
* @returns {void}
|
||||
*/
|
||||
fv: function(array, callback) {
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!array.forEach) {
|
||||
array = [array];
|
||||
}
|
||||
|
||||
var i=0,
|
||||
len=array.length, //Cache the array size
|
||||
loopStatus = { //The "loop status" object is provided as the second argument to the callback function used for each iteration
|
||||
/**
|
||||
* Returns the length of the array that is being iterated over
|
||||
* @returns {int} The length of the array
|
||||
*/
|
||||
getLength: function() {
|
||||
return len;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the current iteration is the last iteration
|
||||
* @returns {Boolean} True if the current iteration is the last iteration. False, otherwse.
|
||||
*/
|
||||
isLast: function() {
|
||||
return i === len-1;
|
||||
},
|
||||
isFirst: function() {
|
||||
return i === 0;
|
||||
},
|
||||
getIndex: function() {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
for (; i<len; i++) { //Loop over the elements in the array
|
||||
var o = array[i];
|
||||
callback(o || '', loopStatus);
|
||||
}
|
||||
},
|
||||
|
||||
f: raptor.forEach,
|
||||
|
||||
fl: function(array, func) {
|
||||
if (array != null) {
|
||||
if (!isArray(array)) {
|
||||
array = [array];
|
||||
}
|
||||
func(array, 0, array.length);
|
||||
}
|
||||
},
|
||||
|
||||
fp: function(o, func) {
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
for (var k in o)
|
||||
{
|
||||
if (o.hasOwnProperty(k))
|
||||
{
|
||||
func(k, o[k]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
e: function(o) {
|
||||
return !notEmpty(o);
|
||||
},
|
||||
|
||||
ne: notEmpty,
|
||||
|
||||
/**
|
||||
* escapeXml helper function
|
||||
*
|
||||
* @param str
|
||||
* @returns
|
||||
*/
|
||||
x: escapeXml,
|
||||
xa: escapeXmlAttr,
|
||||
|
||||
nx: function(str) {
|
||||
return {
|
||||
toString: function() {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
templating = {
|
||||
|
||||
/**
|
||||
* Returns a function that can be used to render the template with the specified name.
|
||||
*
|
||||
* The template function should always be invoked with two arguments:
|
||||
* <ol>
|
||||
* <li><b>data</b> {Object}: The template data object</li>
|
||||
* <li><b>context</b> {@link raptor/templating/Context}: The template context object</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param {String} templateName The name of the template
|
||||
* @return {Function} The function that can be used to render the specified template.
|
||||
*/
|
||||
templateFunc: function(templateName) {
|
||||
|
||||
/*
|
||||
* We first need to find the template rendering function. It's possible
|
||||
* that the factory function for the template rendering function has been
|
||||
* registered but that the template rendering function has not already
|
||||
* been created.
|
||||
*
|
||||
* The template rendering functions are lazily initialized.
|
||||
*/
|
||||
var templateFunc = loadedTemplates[templateName]; //Look for the template function in the loaded templates lookup
|
||||
if (!templateFunc) { //See if the template has already been loaded
|
||||
/*
|
||||
* If we didn't find the template function in the loaded template lookup
|
||||
* then it means that the template has not been fully loaded and initialized.
|
||||
* Therefore, check if the template has been registerd with the name provided
|
||||
*/
|
||||
templateFunc = getRegisteredTemplate(templateName);
|
||||
|
||||
if (!templateFunc && this.findTemplate) {
|
||||
this.findTemplate(templateName);
|
||||
templateFunc = getRegisteredTemplate(templateName);
|
||||
}
|
||||
|
||||
if (templateFunc) { //Check the registered templates lookup to see if a factory function has been register
|
||||
/*
|
||||
* We found that template has been registered so we need to fully initialize it.
|
||||
* To create the template rendering function we must invoke the template factory
|
||||
* function which expects a reference to the static helpers object.
|
||||
*
|
||||
* NOTE: The factory function allows static private variables to be created once
|
||||
* and are then made available to the rendering function as part of the
|
||||
* closure for the rendering function
|
||||
*/
|
||||
var templateInfo = this.getTemplateInfo(templateName);
|
||||
templateFunc = templateFunc(helpers, templateInfo); //Invoke the factory function to get back the rendering function
|
||||
}
|
||||
|
||||
if (!templateFunc) {
|
||||
throw createError(new Error('Template not found with name "' + templateName + '"'));
|
||||
}
|
||||
loadedTemplates[templateName] = templateFunc; //Store the template rendering function in the lookup
|
||||
}
|
||||
|
||||
return templateFunc;
|
||||
},
|
||||
|
||||
getTemplateInfo: function(templateName) {
|
||||
return {
|
||||
name: templateName
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders a template to the provided context.
|
||||
*
|
||||
* <p>
|
||||
* The template specified by the templateName parameter must already have been loaded. The data object
|
||||
* is passed to the compiled rendering function of the template. All output is written to the provided
|
||||
* context using the "writer" associated with the context.
|
||||
*
|
||||
* @param templateName The name of the template to render. The template must have been previously rendered
|
||||
* @param data The data object to pass to the template rendering function
|
||||
* @param context The context to use for all rendered output (required)
|
||||
*/
|
||||
render: function(templateName, data, context) {
|
||||
if (!context) {
|
||||
throw createError(new Error("Context is required"));
|
||||
}
|
||||
|
||||
var templateFunc = this.templateFunc(templateName);
|
||||
|
||||
try
|
||||
{
|
||||
templateFunc(data || {}, context); //Invoke the template rendering function with the required arguments
|
||||
}
|
||||
catch(e) {
|
||||
throw createError(new Error('Unable to render template with name "' + templateName + '". Exception: ' + e), e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders a template and captures the output as a String
|
||||
*
|
||||
* @param templateName {String}The name of the template to render. NOTE: The template must have already been loaded.
|
||||
* @param data {Object} The data object to provide to the template rendering function
|
||||
* @param context {raptor/templating/Context} The context object to use (optional). If a context is provided then the writer will be
|
||||
* temporarily swapped with a StringBuilder to capture the output of rendering. If a context
|
||||
* is not provided then one will be created using the "createContext" method.
|
||||
* @returns {String} The string output of the template
|
||||
*/
|
||||
renderToString: function(templateName, data, context) {
|
||||
var sb = new StringBuilder(); //Create a StringBuilder object to serve as a buffer for the output
|
||||
|
||||
|
||||
if (context === undefined) {
|
||||
/*
|
||||
* If a context object is not provided then we need to create a new context object and use the StringBuilder as the writer
|
||||
*/
|
||||
this.render(templateName, data, new Context(sb));
|
||||
}
|
||||
else {
|
||||
var _this = this;
|
||||
/*
|
||||
* If a context is provided then we need to temporarily swap out the writer for the StringBuilder
|
||||
*/
|
||||
context.swapWriter(sb, function() {
|
||||
_this.render(templateName, data, context);
|
||||
}); //Swap in the writer, render the template and then restore the original writer
|
||||
}
|
||||
|
||||
return sb.toString(); //Return the final string associated with the StringBuilder
|
||||
},
|
||||
|
||||
/**
|
||||
* Unloads a template so that it can be reloaded.
|
||||
*
|
||||
* @param templateName
|
||||
*/
|
||||
unload: function(templateName) {
|
||||
delete loadedTemplates[templateName];
|
||||
$rset('rhtml', templateName, undefined);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to return a helper function
|
||||
*
|
||||
* @function
|
||||
* @param uri
|
||||
* @param name
|
||||
* @returns {Function} The corresponding helper function. An exception is thrown if the helper function is not found
|
||||
*/
|
||||
getFunction: _getFunction,
|
||||
|
||||
/**
|
||||
* Creates a new context object that can be used as the context for
|
||||
* template rendering.
|
||||
*
|
||||
* @param writer {Object} An object that supports a "write" and a "toString" method.
|
||||
* @returns {templating$Context} The newly created context object
|
||||
*/
|
||||
createContext: renderContext.createContext,
|
||||
|
||||
getHandler: _getHandler,
|
||||
|
||||
/**
|
||||
* Helper functions (with short names for minification reasons)
|
||||
*/
|
||||
helpers: helpers
|
||||
};
|
||||
|
||||
return templating;
|
||||
|
||||
});
|
||||
74
lib/index_async.js
Normal file
74
lib/index_async.js
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @extension Async
|
||||
*
|
||||
*/
|
||||
define.extend('raptor/templating', function(require) {
|
||||
"use strict";
|
||||
|
||||
var promises = require('raptor/promises');
|
||||
|
||||
return {
|
||||
|
||||
renderToStringAsync: function(templateName, data, context) {
|
||||
if (!context) {
|
||||
context = this.createContext();
|
||||
}
|
||||
|
||||
var promise = this.renderAsync(templateName, data, context);
|
||||
var deferred = promises.defer();
|
||||
promise
|
||||
.then(function() {
|
||||
deferred.resolve(context.getOutput());
|
||||
})
|
||||
.fail(function(e) {
|
||||
deferred.reject(e);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
renderAsync: function(templateName, data, context) {
|
||||
if (!context) {
|
||||
throw new Error("context is required");
|
||||
}
|
||||
|
||||
var deferred = promises.defer();
|
||||
|
||||
var attributes = context.attributes;
|
||||
var asyncAttributes = attributes.async || (attributes.async = {});
|
||||
asyncAttributes.remaining = 0;
|
||||
asyncAttributes.deferred = deferred;
|
||||
|
||||
try {
|
||||
this.render(templateName, data, context);
|
||||
}
|
||||
catch(e) {
|
||||
deferred.reject(e);
|
||||
}
|
||||
|
||||
asyncAttributes.firstPassComplete = true;
|
||||
|
||||
if (asyncAttributes.remaining === 0) {
|
||||
deferred.resolve(context);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
});
|
||||
66
lib/index_rhino.js
Normal file
66
lib/index_rhino.js
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @extension Rhino
|
||||
*
|
||||
*/
|
||||
define.extend('raptor/templating', function(require) {
|
||||
"use strict";
|
||||
|
||||
var raptor = require('raptor');
|
||||
|
||||
var WrappedWriter = function(javaWriter) {
|
||||
this.javaWriter = javaWriter;
|
||||
this.write = function(o) {
|
||||
if (o != null) {
|
||||
this.javaWriter.write(o.toString());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Provides a Rhino-compatible render function that bridges the gap between
|
||||
* the Java world and the JavaScript world.
|
||||
*
|
||||
* @param templateName {java.lang.String} The name of template to render
|
||||
* @param data {String|java.lang.Object} The data object to pass to the template rendering function
|
||||
* @param javaWriter {java.io.Writer} The Java Writer object to use for output
|
||||
* @returns {void}
|
||||
*/
|
||||
rhinoRender: function(templateName, data, context) {
|
||||
if (data && typeof data === 'string') {
|
||||
|
||||
try
|
||||
{
|
||||
data = eval("(" + data + ")"); //Convert the JSON string to a native JavaScript object
|
||||
}
|
||||
catch(e) {
|
||||
throw raptor.createError('Invalid JSON data passed to "' + templateName + '". Exception: ' + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
this.render('' + templateName, data, context);
|
||||
},
|
||||
|
||||
rhinoCreateContext: function(javaWriter) {
|
||||
var context = this.createContext(new WrappedWriter(javaWriter)); //Wrap the Java writer with a JavaScript object
|
||||
return context;
|
||||
}
|
||||
};
|
||||
});
|
||||
134
lib/index_server.js
Normal file
134
lib/index_server.js
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @extension Server
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
define.extend('raptor/templating', function(require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings'),
|
||||
resources = require('raptor/resources'),
|
||||
files = require('raptor/files'),
|
||||
File = require('raptor/files/File'),
|
||||
templateInfoByName = {},
|
||||
logger = require('raptor/logging').logger('raptor/templating_server');
|
||||
|
||||
return {
|
||||
/**
|
||||
* This method gets invoked if a template with a particular name has not been loaded/registered
|
||||
* and will then try to find it and load it.
|
||||
*
|
||||
* This method will attempt to convert the template name to various
|
||||
* resource paths to try and find the template on disk. It assumes
|
||||
* that templates are named with a particular convention. In addition to
|
||||
* trying the template as a file path, the following
|
||||
* conventions are supported:
|
||||
*
|
||||
* <ol>
|
||||
* <li>my/template --> /my/template.rhtml
|
||||
* <li>my/template --> /my/template/template.rhtml
|
||||
* </ol>
|
||||
*
|
||||
* @param name {String} The template name
|
||||
* @returns {void}
|
||||
*/
|
||||
findTemplate: function(name) {
|
||||
var path = name,
|
||||
resource,
|
||||
templatePath,
|
||||
templateFile = name instanceof File ? name : new File(name);
|
||||
|
||||
|
||||
if (templateFile.exists() && templateFile.isFile()) {
|
||||
resource = resources.createFileResource(templateFile.getAbsolutePath());
|
||||
name = templateFile.getAbsolutePath();
|
||||
}
|
||||
else if (name instanceof File) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
templatePath = path;
|
||||
if (!strings.startsWith(templatePath, '/')) {
|
||||
templatePath = '/' + templatePath;
|
||||
}
|
||||
|
||||
if (!strings.endsWith(templatePath, '.rhtml')) {
|
||||
templatePath += '.rhtml';
|
||||
}
|
||||
|
||||
resource = resources.findResource(templatePath);
|
||||
}
|
||||
|
||||
|
||||
if (!resource || !resource.exists()) {
|
||||
templatePath = path;
|
||||
|
||||
if (!strings.startsWith(templatePath, '/')) {
|
||||
templatePath = '/' + templatePath;
|
||||
}
|
||||
|
||||
var lastSlash = templatePath.lastIndexOf('/');
|
||||
if (lastSlash != -1) {
|
||||
resource = resources.findResource(templatePath + templatePath.substring(lastSlash) + ".rhtml");
|
||||
}
|
||||
}
|
||||
|
||||
if (resource && resource.exists()) {
|
||||
templateInfoByName[name] = {
|
||||
resource: resource,
|
||||
filePath: resource.isFileResource() ? resource.getFilePath() : null,
|
||||
name: name
|
||||
};
|
||||
|
||||
require('raptor/templating/compiler').compileAndLoadResource(resource, {templateName: name});
|
||||
}
|
||||
},
|
||||
|
||||
unloadFile: function(path) {
|
||||
for (var curName in templateInfoByName) {
|
||||
if (templateInfoByName.hasOwnProperty(curName)) {
|
||||
var templateInfo = templateInfoByName[curName];
|
||||
if (templateInfo.filePath === path) {
|
||||
this.unload(curName);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getTemplateInfo: function(name) {
|
||||
return templateInfoByName[name] || {templateName: name};
|
||||
},
|
||||
|
||||
renderToFile: function(templateName, data, outputFile, context) {
|
||||
if (typeof outputFile === 'string') {
|
||||
outputFile = new File(outputFile);
|
||||
}
|
||||
|
||||
return this.renderToStringAsync(templateName, data, context)
|
||||
.then(
|
||||
function(html) {
|
||||
outputFile.writeAsString(html);
|
||||
},
|
||||
function(e) {
|
||||
logger.error(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
14
lib/optimizer.json
Normal file
14
lib/optimizer.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"package": "raptor/render-context"
|
||||
},
|
||||
{
|
||||
"package": "raptor/strings"
|
||||
},
|
||||
{
|
||||
"package": "raptor/xml/utils"
|
||||
},
|
||||
"templating.js"
|
||||
]
|
||||
}
|
||||
68
lib/taglibs/async/AsyncFragmentTag.js
Normal file
68
lib/taglibs/async/AsyncFragmentTag.js
Normal file
@ -0,0 +1,68 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/async/AsyncFragmentTag',
|
||||
function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var logger = require('raptor-logging').logger(module);
|
||||
|
||||
var promises = require('raptor/promises');
|
||||
|
||||
return {
|
||||
render: function(input, context) {
|
||||
var dataProvider = input.dataProvider,
|
||||
arg = input.arg || {};
|
||||
|
||||
arg.context = context;
|
||||
|
||||
context.beginAsyncFragment(function(asyncContext, asyncFragment) {
|
||||
function onError(e) {
|
||||
asyncFragment.end(e);
|
||||
}
|
||||
|
||||
function renderBody(data) {
|
||||
try {
|
||||
if (input.invokeBody) {
|
||||
input.invokeBody(asyncContext, data);
|
||||
}
|
||||
asyncFragment.end();
|
||||
}
|
||||
catch(e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
var promise;
|
||||
if (typeof dataProvider === 'function') {
|
||||
// The data provider is a function we can call to
|
||||
// get the data or a promise
|
||||
promise = dataProvider(arg);
|
||||
}
|
||||
else {
|
||||
promise = promises.isPromise(dataProvider) ?
|
||||
dataProvider : // Data provider is already a promise
|
||||
context.requestData(dataProvider, arg); // Otherwise request the data using a data provider
|
||||
// associated with the context
|
||||
}
|
||||
|
||||
if (promise.resolvedValue) { // Legacy "resolvedValue" support
|
||||
renderBody(promise.resolvedValue);
|
||||
}
|
||||
else {
|
||||
promises.immediateThen(promise,
|
||||
function(data) {
|
||||
renderBody(data);
|
||||
},
|
||||
onError)
|
||||
.fail(onError);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
onError(e);
|
||||
}
|
||||
}, input.timeout);
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
60
lib/taglibs/async/AsyncFragmentTagTransformer.js
Normal file
60
lib/taglibs/async/AsyncFragmentTagTransformer.js
Normal file
@ -0,0 +1,60 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/async/AsyncFragmentTagTransformer',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
||||
|
||||
return {
|
||||
process: function(node, compiler, template) {
|
||||
|
||||
var varName = node.getAttribute('var') ||
|
||||
node.getAttribute('data-provider') ||
|
||||
node.getAttribute('dependency');
|
||||
|
||||
if (varName) {
|
||||
if (!varNameRegExp.test(varName)) {
|
||||
node.addError('Invalid variable name of "' + varName + '"');
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.addError('Either "var" or "data-provider" is required');
|
||||
return;
|
||||
}
|
||||
|
||||
var argProps = [];
|
||||
|
||||
var propsToRemove = [];
|
||||
|
||||
node.forEachProperty(function(uri, name, value) {
|
||||
if (uri === '' && name.startsWith('arg-')) {
|
||||
var argName = name.substring('arg-'.length);
|
||||
argProps.push(JSON.stringify(argName) + ": " + value);
|
||||
propsToRemove.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
propsToRemove.forEach(function(propName) {
|
||||
node.removeProperty(propName);
|
||||
});
|
||||
|
||||
var argString;
|
||||
if (argProps.length) {
|
||||
argString = "{" + argProps.join(", ") + "}";
|
||||
}
|
||||
|
||||
var arg = node.getProperty('arg');
|
||||
if (arg) {
|
||||
argString = 'require("raptor").extend(' + arg + ', ' + argString +')';
|
||||
}
|
||||
|
||||
if (argString) {
|
||||
node.setProperty("arg", template.makeExpression(argString));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
32
lib/taglibs/async/async.rtld
Normal file
32
lib/taglibs/async/async.rtld
Normal file
@ -0,0 +1,32 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<uri>http://raptorjs.org/templates/async</uri>
|
||||
<short-name>async</short-name>
|
||||
<prefix>async</prefix>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>fragment</name>
|
||||
|
||||
<handler-class>raptor/templating/taglibs/async/AsyncFragmentTag</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>
|
||||
<class-name>raptor/templating/taglibs/async/AsyncFragmentTagTransformer</class-name>
|
||||
<after>raptor/templating/taglibs/core/CoreTagTransformer</after>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
8
lib/taglibs/async/optimizer.json
Normal file
8
lib/taglibs/async/optimizer.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"package": "raptor/render-context/async"
|
||||
},
|
||||
"AsyncFragmentTag.js"
|
||||
]
|
||||
}
|
||||
37
lib/taglibs/caching/CachedFragmentTag.js
Normal file
37
lib/taglibs/caching/CachedFragmentTag.js
Normal file
@ -0,0 +1,37 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/caching/CachedFragmentTag',
|
||||
function(require) {
|
||||
"use strict";
|
||||
|
||||
return {
|
||||
render: function(input, context) {
|
||||
var attributes = context.attributes,
|
||||
cacheProvider = attributes.cacheProvider,
|
||||
cache,
|
||||
cachedHtml,
|
||||
cacheKey = input.cacheKey;
|
||||
|
||||
if (!cacheKey) {
|
||||
throw require('raptor').createError(new Error('cache-key is required for <caching:cached-fragment>'));
|
||||
}
|
||||
|
||||
if (!cacheProvider) {
|
||||
cacheProvider = require('raptor/caching').getDefaultProvider();
|
||||
}
|
||||
|
||||
|
||||
|
||||
cache = cacheProvider.getCache(input.cacheName);
|
||||
cachedHtml = cache.get(cacheKey, function() {
|
||||
return context.captureString(function() {
|
||||
if (input.invokeBody) {
|
||||
input.invokeBody();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
context.write(cachedHtml);
|
||||
}
|
||||
};
|
||||
});
|
||||
20
lib/taglibs/caching/caching.rtld
Normal file
20
lib/taglibs/caching/caching.rtld
Normal file
@ -0,0 +1,20 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<uri>http://raptorjs.org/templates/caching</uri>
|
||||
<short-name>caching</short-name>
|
||||
<prefix>caching</prefix>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>cached-fragment</name>
|
||||
|
||||
<handler-class>raptor/templating/taglibs/caching/CachedFragmentTag</handler-class>
|
||||
|
||||
<attribute name="cache-key"/>
|
||||
<attribute name="cache-name"/>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
8
lib/taglibs/caching/optimizer.json
Normal file
8
lib/taglibs/caching/optimizer.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"package": "raptor/caching"
|
||||
},
|
||||
"CachedFragmentTag.js"
|
||||
]
|
||||
}
|
||||
61
lib/taglibs/core/AssignNode.js
Normal file
61
lib/taglibs/core/AssignNode.js
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/AssignNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
||||
|
||||
var AssignNode = function(props) {
|
||||
AssignNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
AssignNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var varName = this.getProperty("var"),
|
||||
value = this.getProperty("value");
|
||||
|
||||
if (!varName) {
|
||||
this.addError('"var" attribute is required');
|
||||
}
|
||||
else if (!varNameRegExp.test(varName)) {
|
||||
this.addError('Invalid variable name of "' + varName + '"');
|
||||
varName = null;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
this.addError('"value" attribute is required');
|
||||
}
|
||||
|
||||
|
||||
if (varName) {
|
||||
template.statement(varName + "=" + value + ";");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return AssignNode;
|
||||
});
|
||||
89
lib/taglibs/core/ChooseNode.js
Normal file
89
lib/taglibs/core/ChooseNode.js
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/ChooseNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings'),
|
||||
WhenNode = require('raptor/templating/taglibs/core/WhenNode'),
|
||||
OtherwiseNode = require('raptor/templating/taglibs/core/OtherwiseNode');
|
||||
|
||||
var ChooseNode = function(props) {
|
||||
ChooseNode.superclass.constructor.call(this);
|
||||
};
|
||||
|
||||
ChooseNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var otherwiseNode = null,
|
||||
foundWhenNode = false;
|
||||
|
||||
|
||||
var allowedNodes = [];
|
||||
|
||||
this.forEachChild(function(child) {
|
||||
|
||||
if (child.isTextNode()) {
|
||||
if (!strings.isEmpty(child.getText())) {
|
||||
this.addError('Static text "' + strings.trim(child.getText()) + '" is not allowed in ' + this.toString() + " tag.");
|
||||
}
|
||||
}
|
||||
else if (child.getNodeClass() === WhenNode){
|
||||
if (otherwiseNode) {
|
||||
this.addError(otherwiseNode + ' tag must be last child of tag ' + this + '.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!foundWhenNode) {
|
||||
foundWhenNode = true;
|
||||
child.firstWhen = true;
|
||||
}
|
||||
|
||||
allowedNodes.push(child);
|
||||
}
|
||||
else if (child.getNodeClass() === OtherwiseNode) {
|
||||
if (otherwiseNode) {
|
||||
this.addError('More than one ' + otherwiseNode + ' tag is not allowed as child of tag ' + this + '.');
|
||||
return;
|
||||
}
|
||||
|
||||
otherwiseNode = child;
|
||||
allowedNodes.push(otherwiseNode);
|
||||
}
|
||||
else {
|
||||
this.addError(child + ' tag is not allowed as child of tag ' + this + '.');
|
||||
child.generateCode(template); //Generate the code for the children so that we can still show errors to the user for nested nodes
|
||||
}
|
||||
}, this);
|
||||
|
||||
allowedNodes.forEach(function(child, i) {
|
||||
child.hasElse = i < allowedNodes.length - 1;
|
||||
child.generateCode(template);
|
||||
});
|
||||
|
||||
if (!foundWhenNode) {
|
||||
this.addError('' + otherwiseNode + ' tag is required to have at least one sibling <c:when> tag.');
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ChooseNode;
|
||||
});
|
||||
451
lib/taglibs/core/CoreTagTransformer.js
Normal file
451
lib/taglibs/core/CoreTagTransformer.js
Normal file
@ -0,0 +1,451 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function removeDashes(str) {
|
||||
return str.replace(/-([a-z])/g, function(match, lower) {
|
||||
return lower.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/core/CoreTagTransformer",
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var extend = raptor.extend,
|
||||
coreNS = "http://raptorjs.org/templates/core",
|
||||
WriteNode = require('raptor/templating/taglibs/core/WriteNode'),
|
||||
ForNode = require("raptor/templating/taglibs/core/ForNode"),
|
||||
IfNode = require("raptor/templating/taglibs/core/IfNode"),
|
||||
ElseIfNode = require("raptor/templating/taglibs/core/ElseIfNode"),
|
||||
ElseNode = require("raptor/templating/taglibs/core/ElseNode"),
|
||||
WithNode = require("raptor/templating/taglibs/core/WithNode"),
|
||||
WhenNode = require("raptor/templating/taglibs/core/WhenNode"),
|
||||
OtherwiseNode = require("raptor/templating/taglibs/core/OtherwiseNode"),
|
||||
TagHandlerNode = require("raptor/templating/taglibs/core/TagHandlerNode"),
|
||||
IncludeNode = require('raptor/templating/taglibs/core/IncludeNode'),
|
||||
Expression = require('raptor/templating/compiler/Expression'),
|
||||
AttributeSplitter = require('raptor/templating/compiler/AttributeSplitter'),
|
||||
TypeConverter = require('raptor/templating/compiler/TypeConverter'),
|
||||
getPropValue = function(value, type, allowExpressions) {
|
||||
return TypeConverter.convert(value, type, allowExpressions);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
process: function(node, compiler, template) {
|
||||
//Find and handle nested <c:attrs> elements
|
||||
this.findNestedAttrs(node, compiler, template);
|
||||
|
||||
var forEachAttr,
|
||||
ifAttr,
|
||||
elseIfAttr,
|
||||
attrsAttr,
|
||||
whenAttr,
|
||||
withAttr,
|
||||
escapeXmlAttr,
|
||||
parseBodyTextAttr,
|
||||
stripAttr,
|
||||
contentAttr,
|
||||
replaceAttr,
|
||||
inputAttr,
|
||||
forEachNode,
|
||||
uri,
|
||||
tag,
|
||||
nestedTag,
|
||||
forEachProp = function(callback, thisObj) {
|
||||
node.forEachAttributeAnyNS(function(attr) {
|
||||
|
||||
if (attr.uri=== 'http://www.w3.org/2000/xmlns/' || attr.uri === 'http://www.w3.org/XML/1998/namespace' || attr.prefix == 'xmlns') {
|
||||
return; //Skip xmlns attributes
|
||||
}
|
||||
var prefix = attr.prefix;
|
||||
|
||||
|
||||
var attrUri = compiler.taglibs.resolveURI(attr.uri);
|
||||
|
||||
attrUri = attr.prefix && (attrUri != tag.getTaglibUri()) ? attr.uri : null;
|
||||
var attrDef = compiler.taglibs.getAttribute(uri, node.localName, attrUri, attr.localName);
|
||||
|
||||
var type = attrDef ? (attrDef.type || 'string') : 'string',
|
||||
value;
|
||||
|
||||
if (attrUri === tag.getTaglibUri()) {
|
||||
prefix = '';
|
||||
}
|
||||
|
||||
var isTaglibUri = template.isTaglib(attrUri);
|
||||
|
||||
if (!attrDef && (isTaglibUri || !tag.dynamicAttributes)) {
|
||||
|
||||
//Tag doesn't allow dynamic attributes
|
||||
node.addError('The tag "' + tag.name + '" in taglib "' + tag.getTaglibUri() + '" does not support attribute "' + attr + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.value instanceof Expression) {
|
||||
value = attr.value;
|
||||
}
|
||||
else {
|
||||
try
|
||||
{
|
||||
value = getPropValue(attr.value, type, attrDef ? attrDef.allowExpressions !== false : true);
|
||||
}
|
||||
catch(e) {
|
||||
node.addError('Invalid attribute value of "' + attr.value + '" for attribute "' + attr.name + '": ' + e.message);
|
||||
value = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
var propName;
|
||||
|
||||
if (attrDef) {
|
||||
if (attrDef.targetProperty) {
|
||||
propName = attrDef.targetProperty;
|
||||
}
|
||||
else if (attrDef.preserveName) {
|
||||
propName = attr.localName;
|
||||
}
|
||||
else {
|
||||
propName = removeDashes(attr.localName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
propName = attr.localName;
|
||||
}
|
||||
|
||||
callback.call(thisObj, attrUri, propName, value, prefix, attrDef);
|
||||
}, this);
|
||||
|
||||
tag.forEachStaticProperty(function(staticProp) {
|
||||
var value = getPropValue(staticProp.value, staticProp.type, false);
|
||||
callback.call(thisObj, '', staticProp.name, value, '', staticProp);
|
||||
});
|
||||
};
|
||||
|
||||
uri = node.uri;
|
||||
|
||||
if (!uri && node.isRoot() && node.localName === 'template') {
|
||||
uri = coreNS;
|
||||
}
|
||||
|
||||
if (node.parentNode) {
|
||||
var parentUri = node.parentNode.uri;
|
||||
var parentName = node.parentNode.localName;
|
||||
nestedTag = compiler.taglibs.getNestedTag(parentUri, parentName, uri, node.localName);
|
||||
|
||||
if (nestedTag) {
|
||||
node.setWordWrapEnabled(false);
|
||||
node.parentNode.setProperty(nestedTag.targetProperty, node.getBodyContentExpression(template));
|
||||
node.detach();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tag = node.tag || compiler.taglibs.getTag(uri, node.localName);
|
||||
|
||||
if (node.getAttributeNS(coreNS, "space") === "preserve" || node.getAttributeNS(coreNS, "whitespace") === "preserve") {
|
||||
node.setPreserveWhitespace(true);
|
||||
}
|
||||
node.removeAttributeNS(coreNS, "space");
|
||||
node.removeAttributeNS(coreNS, "whitespace");
|
||||
|
||||
if ((escapeXmlAttr = node.getAttributeNS(coreNS, "escape-xml")) != null) {
|
||||
node.removeAttributeNS(coreNS, "escape-xml");
|
||||
node.setEscapeXmlBodyText(escapeXmlAttr !== "false");
|
||||
}
|
||||
|
||||
if ((parseBodyTextAttr = node.getAttributeNS(coreNS, "parse-body-text")) != null) {
|
||||
node.removeAttributeNS(coreNS, "parse-body-text");
|
||||
node.parseBodyText = parseBodyTextAttr !== "false";
|
||||
}
|
||||
if ((whenAttr = node.getAttributeNS(coreNS, "when")) != null) {
|
||||
node.removeAttributeNS(coreNS, "when");
|
||||
|
||||
var whenNode = new WhenNode({test: new Expression(whenAttr), pos: node.getPosition()});
|
||||
node.parentNode.replaceChild(whenNode, node);
|
||||
whenNode.appendChild(node);
|
||||
}
|
||||
|
||||
if (node.getAttributeNS(coreNS, "otherwise") != null) {
|
||||
node.removeAttributeNS(coreNS, "otherwise");
|
||||
|
||||
var otherwiseNode = new OtherwiseNode({pos: node.getPosition()});
|
||||
node.parentNode.replaceChild(otherwiseNode, node);
|
||||
otherwiseNode.appendChild(node);
|
||||
}
|
||||
|
||||
if ((attrsAttr = node.getAttributeNS(coreNS, "attrs")) != null) {
|
||||
node.removeAttributeNS(coreNS, "attrs");
|
||||
node.dynamicAttributesExpression = attrsAttr;
|
||||
}
|
||||
|
||||
if (((forEachAttr = node.getAttributeNS(coreNS, "for")) || (forEachAttr = node.getAttributeNS(coreNS, "for-each"))) != null) {
|
||||
node.removeAttributeNS(coreNS, "for");
|
||||
node.removeAttributeNS(coreNS, "for-each");
|
||||
var forEachProps = AttributeSplitter.parse(
|
||||
forEachAttr,
|
||||
{
|
||||
each: {
|
||||
type: "custom"
|
||||
},
|
||||
separator: {
|
||||
type: "expression"
|
||||
},
|
||||
"status-var": {
|
||||
type: "identifier"
|
||||
},
|
||||
"for-loop": {
|
||||
type: "boolean",
|
||||
allowExpressions: false
|
||||
}
|
||||
},
|
||||
{
|
||||
removeDashes: true,
|
||||
defaultName: "each",
|
||||
errorHandler: function(message) {
|
||||
node.addError('Invalid c:for attribute of "' + forEachAttr + '". Error: ' + message);
|
||||
}
|
||||
});
|
||||
|
||||
forEachProps.pos = node.getPosition(); //Copy the position property
|
||||
forEachNode = new ForNode(forEachProps);
|
||||
|
||||
//Surround the existing node with an "forEach" node by replacing the current
|
||||
//node with the new "forEach" node and then adding the current node as a child
|
||||
node.parentNode.replaceChild(forEachNode, node);
|
||||
forEachNode.appendChild(node);
|
||||
}
|
||||
|
||||
if ((ifAttr = node.getAttributeNS(coreNS, "if")) != null) {
|
||||
node.removeAttributeNS(coreNS, "if");
|
||||
|
||||
var ifNode = new IfNode({
|
||||
test: new Expression(ifAttr),
|
||||
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(ifNode, node);
|
||||
ifNode.appendChild(node);
|
||||
}
|
||||
|
||||
if ((elseIfAttr = node.getAttributeNS(coreNS, "else-if")) != null) {
|
||||
node.removeAttributeNS(coreNS, "else-if");
|
||||
|
||||
var elseIfNode = new ElseIfNode({
|
||||
test: new Expression(elseIfAttr),
|
||||
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(elseIfNode, node);
|
||||
elseIfNode.appendChild(node);
|
||||
}
|
||||
|
||||
if ((node.getAttributeNS(coreNS, "else")) != null) {
|
||||
node.removeAttributeNS(coreNS, "else");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if ((withAttr = node.getAttributeNS(coreNS, "with")) != null) {
|
||||
node.removeAttributeNS(coreNS, "with");
|
||||
|
||||
var withNode = new WithNode({
|
||||
vars: withAttr,
|
||||
pos: node.getPosition()
|
||||
});
|
||||
|
||||
node.parentNode.replaceChild(withNode, node);
|
||||
withNode.appendChild(node);
|
||||
}
|
||||
|
||||
if ((contentAttr = (node.getAttributeNS(coreNS, "bodyContent") || node.getAttributeNS(coreNS, "content"))) != null) {
|
||||
node.removeAttributeNS(coreNS, "bodyContent");
|
||||
node.removeAttributeNS(coreNS, "content");
|
||||
|
||||
var newChild = new WriteNode({expression: contentAttr, pos: node.getPosition()});
|
||||
node.removeChildren();
|
||||
node.appendChild(newChild);
|
||||
}
|
||||
|
||||
if (node.getAttributeNS(coreNS, "trim-body-indent") === 'true') {
|
||||
node.removeAttributeNS(coreNS, "trim-body-indent");
|
||||
node.trimBodyIndent = true;
|
||||
}
|
||||
|
||||
if (node.getAttributeNS && (stripAttr = node.getAttributeNS(coreNS, "strip")) != null) {
|
||||
node.removeAttributeNS(coreNS, "strip");
|
||||
if (!node.setStripExpression) {
|
||||
node.addError("The c:strip directive is not allowed for target node");
|
||||
}
|
||||
node.setStripExpression(stripAttr);
|
||||
}
|
||||
|
||||
if (node.getAttributeNS && (replaceAttr = node.getAttributeNS(coreNS, "replace")) != null) {
|
||||
node.removeAttributeNS(coreNS, "replace");
|
||||
|
||||
var replaceWriteNode = new WriteNode({expression: replaceAttr, pos: node.getPosition()});
|
||||
//Replace the existing node with an node that only has children
|
||||
|
||||
node.parentNode.replaceChild(replaceWriteNode, node);
|
||||
|
||||
node = replaceWriteNode;
|
||||
}
|
||||
|
||||
if (tag) {
|
||||
if (tag.preserveWhitespace) {
|
||||
node.setPreserveWhitespace(true);
|
||||
}
|
||||
|
||||
if (tag.handlerClass || tag.template)
|
||||
{
|
||||
if (tag.handlerClass) {
|
||||
//Instead of compiling as a static XML element, we'll
|
||||
//make the node render as a tag handler node so that
|
||||
//writes code that invokes the handler
|
||||
TagHandlerNode.convertNode(
|
||||
node,
|
||||
tag);
|
||||
|
||||
if ((inputAttr = node.getAttributeNS(coreNS, "input")) != null) {
|
||||
node.removeAttributeNS(coreNS, "input");
|
||||
node.setInputExpression(template.makeExpression(inputAttr));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The tag is mapped to a template that will be used to perform
|
||||
// the rendering so convert the node into a "IncludeNode" that can
|
||||
// be used to include the output of rendering a template
|
||||
IncludeNode.convertNode(
|
||||
node,
|
||||
tag.template);
|
||||
}
|
||||
|
||||
forEachProp(function(uri, name, value, prefix, attrDef) {
|
||||
if (attrDef) {
|
||||
node.setPropertyNS(uri, name, value);
|
||||
}
|
||||
else {
|
||||
if (tag.dynamicAttributesRemoveDashes === true) {
|
||||
name = removeDashes(name);
|
||||
}
|
||||
|
||||
node.addDynamicAttribute(prefix ? prefix + ':' + name : name, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (tag.nodeClass){
|
||||
|
||||
var NodeCompilerClass = require(tag.nodeClass);
|
||||
extend(node, NodeCompilerClass.prototype);
|
||||
NodeCompilerClass.call(node);
|
||||
|
||||
node.setNodeClass(NodeCompilerClass);
|
||||
|
||||
forEachProp(function(uri, name, value) {
|
||||
node.setPropertyNS(uri, name, value);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else if (uri && template.isTaglib(uri)) {
|
||||
node.addError('Tag ' + node.toString() + ' is not allowed for taglib "' + uri + '"');
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
findNestedAttrs: function(node, compiler, template) {
|
||||
node.forEachChild(function(child) {
|
||||
if (child.uri === coreNS && child.localName === 'attr') {
|
||||
this.handleAttr(child, compiler, template);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
handleAttr: function(node, compiler, template) {
|
||||
|
||||
|
||||
var parentNode = node.parentNode;
|
||||
if (!parentNode.isElementNode()) {
|
||||
node.addError(this.toString() + ' tag is not nested within an element tag.');
|
||||
return;
|
||||
}
|
||||
|
||||
var hasValue = node.hasAttribute("value");
|
||||
|
||||
var attrName = node.getAttribute("name");
|
||||
var attrValue = node.getAttribute("value");
|
||||
var attrUri = node.getAttribute("uri") || '';
|
||||
var attrPrefix = node.getAttribute("prefix") || '';
|
||||
|
||||
if (parentNode.hasAttributeNS(attrUri, attrName)) {
|
||||
node.addError(node.toString() + ' tag adds duplicate attribute with name "' + attrName + '"' + (attrUri ? ' and URI "' + attrUri + '"' : ''));
|
||||
return;
|
||||
}
|
||||
|
||||
node.removeAttribute("name");
|
||||
node.removeAttribute("value");
|
||||
node.removeAttribute("uri");
|
||||
node.removeAttribute("prefix");
|
||||
|
||||
if (node.hasAttributesAnyNS()) {
|
||||
//There shouldn't be any other attributes...
|
||||
var invalidAttrs = node.getAllAttributes().map(function(attr) {
|
||||
return attr.qName;
|
||||
});
|
||||
node.addError("Invalid attributes for tag " + node.toString() + ': ' + invalidAttrs.join(", "));
|
||||
return;
|
||||
}
|
||||
//Cleanup whitespace between <c:attr> tags
|
||||
if (node.previousSibling && node.previousSibling.isTextNode() && node.previousSibling.getText().trim() === '') {
|
||||
node.previousSibling.detach();
|
||||
}
|
||||
|
||||
if (node.nextSibling && node.nextSibling.isTextNode() && node.nextSibling.getText().trim() === '') {
|
||||
node.nextSibling.detach();
|
||||
}
|
||||
|
||||
if (node.nextSibling && node.nextSibling.isTextNode()) {
|
||||
node.nextSibling.setText(node.nextSibling.getText().replace(/^\n\s*/, ""));
|
||||
}
|
||||
|
||||
node.detach(); //Remove the node out of the tree
|
||||
|
||||
compiler.transformTree(node, template);
|
||||
|
||||
if (hasValue) {
|
||||
parentNode.setAttributeNS(attrUri, attrName, attrValue, attrPrefix);
|
||||
}
|
||||
else {
|
||||
node.setEscapeXmlContext(require('raptor/templating/compiler/EscapeXmlContext').Attribute); //Escape body text and expressions as attributes
|
||||
parentNode.setAttributeNS(attrUri, attrName, node.getBodyContentExpression(template), attrPrefix, false /* Don't escape...pre-escaped*/);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
102
lib/taglibs/core/CoreTextTransformer.js
Normal file
102
lib/taglibs/core/CoreTextTransformer.js
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/core/CoreTextTransformer",
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var ExpressionParser = require('raptor/templating/compiler/ExpressionParser'),
|
||||
TextNode = require('raptor/templating/compiler/TextNode'),
|
||||
WriteNode = require('raptor/templating/taglibs/core/WriteNode'),
|
||||
ScriptletNode = require('raptor/templating/taglibs/core/ScriptletNode');
|
||||
|
||||
return {
|
||||
|
||||
process: function(node, compiler) {
|
||||
|
||||
if (node.parentNode && node.parentNode.parseBodyText === false) {
|
||||
return; //Don't try to parse expressions
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
ExpressionParser.parse(
|
||||
node.text,
|
||||
{
|
||||
text: function(text, escapeXml) {
|
||||
parts.push({text: text, escapeXml: escapeXml});
|
||||
},
|
||||
|
||||
expression: function(expression, escapeXml) {
|
||||
parts.push({expression: expression, escapeXml: escapeXml});
|
||||
},
|
||||
|
||||
scriptlet: function(scriptlet) {
|
||||
parts.push({scriptlet: scriptlet});
|
||||
},
|
||||
error: function(message) {
|
||||
node.addError(message);
|
||||
}
|
||||
},
|
||||
this);
|
||||
|
||||
if (parts.length > 0) {
|
||||
var startIndex = 0;
|
||||
|
||||
if (parts[0].text) {
|
||||
node.setText(parts[0].text); //Update this text node to match first text part and we'll add the remaining
|
||||
node.setEscapeXml(parts[0].escapeXml !== false);
|
||||
startIndex = 1;
|
||||
}
|
||||
else {
|
||||
node.text = ''; //The first part is an expression so we'll just zero out this text node
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
var newNodes = [];
|
||||
|
||||
for (var i=startIndex, part, newNode; i<parts.length; i++) {
|
||||
part = parts[i];
|
||||
newNode = null;
|
||||
|
||||
|
||||
if (part.hasOwnProperty('text')) {
|
||||
newNode = new TextNode(part.text, part.escapeXml !== false);
|
||||
newNode.setTransformerApplied(this); //We shouldn't reprocess the new text node
|
||||
}
|
||||
else if (part.hasOwnProperty('expression')) {
|
||||
newNode = new WriteNode({expression: part.expression, escapeXml: part.escapeXml !== false});
|
||||
}
|
||||
else if (part.hasOwnProperty('scriptlet')) {
|
||||
newNode = new ScriptletNode(part.scriptlet);
|
||||
}
|
||||
|
||||
if (newNode) {
|
||||
newNode.setPosition(node.getPosition());
|
||||
newNodes.push(newNode);
|
||||
}
|
||||
}
|
||||
if (newNodes.length) {
|
||||
node.parentNode.insertAfter(newNodes, node);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
85
lib/taglibs/core/DefNode.js
Normal file
85
lib/taglibs/core/DefNode.js
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/DefNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings'),
|
||||
funcDefRegExp = /^([A-Za-z_][A-Za-z0-9_]*)\(((?:[A-Za-z_][A-Za-z0-9_]*,\s*)*[A-Za-z_][A-Za-z0-9_]*)?\)$/;
|
||||
|
||||
var DefNode = function(props) {
|
||||
DefNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
DefNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
|
||||
var func = this.getProperty("function");
|
||||
|
||||
if (!func) {
|
||||
this.addError('"function" attribute is required');
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
func = strings.trim(func);
|
||||
|
||||
var matches = funcDefRegExp.exec(func);
|
||||
if (matches) {
|
||||
var name = matches[1];
|
||||
var params = matches[2] ? matches[2].split(/\s*,\s*/) : [];
|
||||
|
||||
var definedFunctions = template.getAttribute("core:definedFunctions");
|
||||
if (!definedFunctions) {
|
||||
definedFunctions = template.setAttribute("core:definedFunctions", {});
|
||||
}
|
||||
|
||||
definedFunctions[name] = {
|
||||
params: params,
|
||||
bodyParam: this.getProperty("bodyParam")
|
||||
};
|
||||
}
|
||||
else {
|
||||
this.addError('Invalid function name of "' + func + '"');
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
template
|
||||
.statement('function ' + func + ' {')
|
||||
.indent(function() {
|
||||
template
|
||||
.line('return context.c(function() {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('});');
|
||||
}, this)
|
||||
.line('}');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return DefNode;
|
||||
});
|
||||
52
lib/taglibs/core/ElseIfNode.js
Normal file
52
lib/taglibs/core/ElseIfNode.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/ElseIfNode',
|
||||
'raptor/templating/compiler/ElementNode',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var ElseIfNode = function(props) {
|
||||
ElseIfNode.superclass.constructor.call(this, "http://raptorjs.org/templates/core", "else-if", "c");
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
ElseIfNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
var test = this.getProperty("test");
|
||||
|
||||
if (!test) {
|
||||
this.addError('"test" attribute is required');
|
||||
return;
|
||||
}
|
||||
|
||||
template
|
||||
.line('else if (' + test + ') {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ElseIfNode;
|
||||
});
|
||||
52
lib/taglibs/core/ElseNode.js
Normal file
52
lib/taglibs/core/ElseNode.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/ElseNode',
|
||||
'raptor/templating/compiler/ElementNode',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings');
|
||||
|
||||
var ElseNode = function(props) {
|
||||
ElseNode.superclass.constructor.call(this, "else", "http://raptorjs.org/templates/core", "c");
|
||||
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
ElseNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
if (this.valid == null) {
|
||||
return; //Don't generate code for an invalid else
|
||||
}
|
||||
|
||||
template
|
||||
.line('else {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ElseNode;
|
||||
});
|
||||
68
lib/taglibs/core/ElseTagTransformer.js
Normal file
68
lib/taglibs/core/ElseTagTransformer.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/core/ElseTagTransformer",
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings');
|
||||
|
||||
return {
|
||||
|
||||
process: function(node, compiler) {
|
||||
var curNode = node.previousSibling,
|
||||
matchingNode,
|
||||
IfNode = compiler.getNodeClass('http://raptorjs.org/templates/core', 'if'),
|
||||
ElseIfNode = compiler.getNodeClass('http://raptorjs.org/templates/core', 'else-if'),
|
||||
whitespaceNodes = [];
|
||||
|
||||
while (curNode) {
|
||||
if (curNode.getNodeClass() === ElseIfNode || curNode.getNodeClass() === IfNode) {
|
||||
matchingNode = curNode;
|
||||
break;
|
||||
}
|
||||
else if (curNode.isTextNode()) {
|
||||
if (!strings.isEmpty(curNode.getText())) {
|
||||
node.addError('Static text "' + strings.trim(curNode.getText()) + '" is not allowed before ' + node.toString() + " tag.");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
whitespaceNodes.push(curNode);
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.addError(curNode + ' is not allowed before ' + node.toString() + " tag.");
|
||||
return;
|
||||
}
|
||||
curNode = curNode.previousSibling;
|
||||
}
|
||||
|
||||
if (!matchingNode) {
|
||||
node.addError('<c:if> or <c:else-if> node not found immediately before ' + node.toString() + " tag.");
|
||||
return;
|
||||
}
|
||||
|
||||
whitespaceNodes.forEach(function(whitespaceNode) {
|
||||
whitespaceNode.parentNode.removeChild(whitespaceNode);
|
||||
});
|
||||
|
||||
matchingNode.hasElse = true;
|
||||
node.valid = true;
|
||||
}
|
||||
};
|
||||
});
|
||||
171
lib/taglibs/core/ForNode.js
Normal file
171
lib/taglibs/core/ForNode.js
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/ForNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var forEachRegEx = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(.+)$/,
|
||||
forEachPropRegEx = /^\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*,\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s+in\s+(.+)$/,
|
||||
stringify = require("raptor/json/stringify").stringify,
|
||||
parseForEach = function(value) {
|
||||
var match = value.match(forEachRegEx);
|
||||
if (match) {
|
||||
return {
|
||||
"var": match[1],
|
||||
"in": match[2]
|
||||
};
|
||||
}
|
||||
else {
|
||||
match = value.match(forEachPropRegEx);
|
||||
if (!match) {
|
||||
throw new Error('Invalid each attribute of "' + value + '"');
|
||||
}
|
||||
|
||||
return {
|
||||
"nameVar": match[1],
|
||||
"valueVar": match[2],
|
||||
"in": match[3]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
var ForNode = function(props) {
|
||||
ForNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
ForNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
var each = this.getProperty("each"),
|
||||
eachProp = false,
|
||||
separator = this.getProperty("separator"),
|
||||
statusVar = this.getProperty("statusVar") || this.getProperty("varStatus");
|
||||
|
||||
if (!each) {
|
||||
this.addError('"each" attribute is required');
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
var parts;
|
||||
try
|
||||
{
|
||||
parts = parseForEach(each);
|
||||
}
|
||||
catch(e) {
|
||||
this.addError(e.message);
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
var items = template.makeExpression(parts["in"]),
|
||||
varName = parts["var"],
|
||||
nameVarName = parts.nameVar,
|
||||
valueVarName = parts.valueVar;
|
||||
|
||||
if (nameVarName) {
|
||||
if (separator) {
|
||||
this.addError('Separator is not supported when looping over properties');
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusVar) {
|
||||
this.addError('Loop status variable not supported when looping over properties');
|
||||
this.generateCodeForChildren(template);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (separator && !statusVar) {
|
||||
statusVar = "__loop";
|
||||
}
|
||||
|
||||
|
||||
var funcName;
|
||||
|
||||
var forEachParams;
|
||||
if (statusVar) {
|
||||
forEachParams = [varName, statusVar];
|
||||
|
||||
funcName = template.getStaticHelperFunction("forEachWithStatusVar", "fv");
|
||||
|
||||
template
|
||||
.statement(funcName + '(' + items + ', function(' + forEachParams.join(",") + ') {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
if (separator) {
|
||||
template
|
||||
.statement("if (!" + statusVar + ".isLast()) {")
|
||||
.indent(function() {
|
||||
template.write(template.isExpression(separator) ? separator.getExpression() : stringify(separator));
|
||||
}, this)
|
||||
.line('}');
|
||||
}
|
||||
}, this)
|
||||
.line('});');
|
||||
}
|
||||
else {
|
||||
if (this.getProperty('forLoop') === true) {
|
||||
forEachParams = ["__array", "__index", "__length", varName];
|
||||
|
||||
template
|
||||
.statement(template.getStaticHelperFunction("forLoop", "fl") + '(' + items + ', function(' + forEachParams.join(",") + ') {')
|
||||
.indent(function() {
|
||||
template
|
||||
.statement('for (;__index<__length;__index++) {')
|
||||
.indent(function() {
|
||||
template.statement(varName + '=__array[__index];');
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('});');
|
||||
}
|
||||
else {
|
||||
forEachParams = nameVarName ? [nameVarName, valueVarName] : [varName];
|
||||
funcName = nameVarName ? template.getStaticHelperFunction("forEachProp", "fp") :
|
||||
template.getStaticHelperFunction("forEach", "f");
|
||||
|
||||
template
|
||||
.statement(funcName + '(' + items + ', function(' + forEachParams.join(",") + ') {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('});');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ForNode;
|
||||
});
|
||||
50
lib/taglibs/core/IfNode.js
Normal file
50
lib/taglibs/core/IfNode.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/IfNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var IfNode = function(props) {
|
||||
IfNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
IfNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
var test = this.getProperty("test");
|
||||
|
||||
if (!test) {
|
||||
this.addError('"test" attribute is required');
|
||||
}
|
||||
|
||||
template
|
||||
.statement('if (' + test + ') {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return IfNode;
|
||||
});
|
||||
103
lib/taglibs/core/IncludeNode.js
Normal file
103
lib/taglibs/core/IncludeNode.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/IncludeNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var stringify = require('raptor/json/stringify'),
|
||||
extend = raptor.extend;
|
||||
|
||||
var IncludeNode = function(props) {
|
||||
IncludeNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
IncludeNode.convertNode = function(node, template) {
|
||||
extend(node, IncludeNode.prototype);
|
||||
IncludeNode.call(node);
|
||||
|
||||
node.setProperty("template", stringify(template));
|
||||
};
|
||||
|
||||
IncludeNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
var templateName = this.getProperty("template"),
|
||||
templateData = this.getProperty("templateData") || this.getProperty("template-data"),
|
||||
resourcePath,
|
||||
_this = this;
|
||||
|
||||
if (templateName) {
|
||||
this.removeProperty("template");
|
||||
var dataExpression;
|
||||
|
||||
if (templateData) {
|
||||
dataExpression = templateData;
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
dataExpression = {
|
||||
toString: function() {
|
||||
var propParts = [];
|
||||
|
||||
_this.forEachPropertyNS('', 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(", ") + "}";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template.include(templateName, dataExpression);
|
||||
|
||||
}
|
||||
else if ((resourcePath = this.getAttribute("resource"))) {
|
||||
var isStatic = this.getProperty("static") !== false;
|
||||
if (isStatic) {
|
||||
var resource = require('raptor/resources').findResource(resourcePath);
|
||||
if (!resource.exists()) {
|
||||
this.addError('"each" attribute is required');
|
||||
return;
|
||||
}
|
||||
|
||||
template.write(stringify(resource.readAsString()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.addError('"template" or "resource" attribute is required');
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return IncludeNode;
|
||||
});
|
||||
133
lib/taglibs/core/InvokeNode.js
Normal file
133
lib/taglibs/core/InvokeNode.js
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/InvokeNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var forEach = raptor.forEach;
|
||||
|
||||
var InvokeNode = function(props) {
|
||||
InvokeNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
InvokeNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
|
||||
var func = this.getProperty("function"),
|
||||
funcDef,
|
||||
bodyParam,
|
||||
definedFunctions = template.getAttribute("core:definedFunctions");
|
||||
|
||||
if (!func) {
|
||||
this.addError('"function" attribute is required');
|
||||
return;
|
||||
}
|
||||
|
||||
if (func.indexOf('(') === -1) {
|
||||
|
||||
funcDef = definedFunctions ? definedFunctions[func] : null;
|
||||
// if (!funcDef) {
|
||||
// this.addError('Function with name "' + func + '" not defined using <c:define>.');
|
||||
// }
|
||||
|
||||
var argParts = [];
|
||||
var validParamsLookup = {};
|
||||
var params = [];
|
||||
|
||||
if (funcDef) {
|
||||
params = funcDef.params || [];
|
||||
bodyParam = funcDef.bodyParam;
|
||||
/*
|
||||
* Loop over the defined parameters to figure out the names of allowed parameters and add them to a lookup
|
||||
*/
|
||||
forEach(params, function(param) {
|
||||
validParamsLookup[param] = true;
|
||||
}, this);
|
||||
|
||||
}
|
||||
|
||||
var bodyArg = null;
|
||||
|
||||
if (this.hasChildren()) {
|
||||
if (!funcDef || !funcDef.bodyParam) {
|
||||
this.addError('Nested content provided when invoking macro "' + func + '" but defined macro does not support nested content.');
|
||||
}
|
||||
else {
|
||||
bodyArg = this.getBodyContentExpression(template, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* VALIDATION:
|
||||
* Loop over all of the provided attributes and make sure they are allowed
|
||||
*/
|
||||
this.forEachPropertyNS('', function(name, value) {
|
||||
if (name === 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validParamsLookup[name]) {
|
||||
this.addError('Parameter with name "' + name + '" not supported for function with name "' + func + '". Allowed parameters: ' + params.join(", "));
|
||||
}
|
||||
}, this);
|
||||
|
||||
/*
|
||||
* One more pass to build the argument list
|
||||
*/
|
||||
forEach(params, function(param) {
|
||||
validParamsLookup[param] = true;
|
||||
if (param === bodyParam) {
|
||||
argParts.push(bodyArg ? bodyArg : "undefined");
|
||||
}
|
||||
else {
|
||||
var arg = this.getAttribute(param);
|
||||
if (arg == null) {
|
||||
argParts.push("undefined");
|
||||
}
|
||||
else {
|
||||
argParts.push(this.getProperty(param));
|
||||
}
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
template.write(func + "(" + argParts.join(",") + ")");
|
||||
}
|
||||
else {
|
||||
var funcName = func.substring(0, func.indexOf('('));
|
||||
funcDef = definedFunctions ? definedFunctions[funcName] : null;
|
||||
if (funcDef) {
|
||||
template.write(func);
|
||||
}
|
||||
else {
|
||||
template.statement(func + ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return InvokeNode;
|
||||
});
|
||||
51
lib/taglibs/core/OtherwiseNode.js
Normal file
51
lib/taglibs/core/OtherwiseNode.js
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/OtherwiseNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var strings = require('raptor/strings');
|
||||
|
||||
var OtherwiseNode = function(props) {
|
||||
OtherwiseNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
OtherwiseNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
template
|
||||
.line('else {')
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return "<c:otherwise>";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return OtherwiseNode;
|
||||
});
|
||||
43
lib/taglibs/core/ScriptletNode.js
Normal file
43
lib/taglibs/core/ScriptletNode.js
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/ScriptletNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var ScriptletNode = function(code) {
|
||||
ScriptletNode.superclass.constructor.call(this, 'scriptlet');
|
||||
this.code = code;
|
||||
};
|
||||
|
||||
ScriptletNode.prototype = {
|
||||
doGenerateCode: function(template) {
|
||||
if (this.code) {
|
||||
template.code(this.code);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
toString: function() {
|
||||
return '{%' + this.code + '%}';
|
||||
}
|
||||
};
|
||||
|
||||
return ScriptletNode;
|
||||
});
|
||||
288
lib/taglibs/core/TagHandlerNode.js
Normal file
288
lib/taglibs/core/TagHandlerNode.js
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/core/TagHandlerNode",
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var extend = raptor.extend,
|
||||
objects = require('raptor/objects'),
|
||||
forEach = raptor.forEach,
|
||||
forEachEntry = raptor.forEachEntry,
|
||||
stringify = require('raptor/json/stringify'),
|
||||
Expression = require('raptor/templating/compiler/Expression'),
|
||||
addHandlerVar = function(template, handlerClass) {
|
||||
var handlerVars = template._handlerVars || (template._handlerVars = {});
|
||||
|
||||
var handlerVar = handlerVars[handlerClass];
|
||||
if (!handlerVar) {
|
||||
handlerVars[handlerClass] = handlerVar = handlerClass.replace(/[.\-\/]/g, '_');
|
||||
template.addStaticVar(handlerVar, template.getStaticHelperFunction("getTagHandler", "t") + "(" + stringify(handlerClass) + ")");
|
||||
}
|
||||
|
||||
return handlerVar;
|
||||
|
||||
},
|
||||
getPropsStr = function(props, template) {
|
||||
var propsArray = [];
|
||||
|
||||
if (props) {
|
||||
forEachEntry(props, function(name, value) {
|
||||
if (value instanceof Expression) {
|
||||
var expressionStr;
|
||||
template.indent(function() {
|
||||
expressionStr = value.expression.toString();
|
||||
});
|
||||
|
||||
propsArray.push(template.indentStr(1) + stringify(name) + ": " + expressionStr);
|
||||
}
|
||||
else if (typeof value === 'string' || typeof value === 'object') {
|
||||
propsArray.push(template.indentStr(1) + stringify(name) + ": " + stringify(value));
|
||||
}
|
||||
else {
|
||||
propsArray.push(template.indentStr(1) + stringify(name) + ": " + value);
|
||||
}
|
||||
});
|
||||
|
||||
if (propsArray.length) {
|
||||
return "{\n" + propsArray.join(',\n') + "\n" + template.indentStr() + "}";
|
||||
}
|
||||
else {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "{}";
|
||||
}
|
||||
};
|
||||
|
||||
var TagHandlerNode = function(tag) {
|
||||
if (!this.nodeType) {
|
||||
TagHandlerNode.superclass.constructor.call(this);
|
||||
}
|
||||
this.tag = tag;
|
||||
this.dynamicAttributes = null;
|
||||
this.preInvokeCode = [];
|
||||
this.postInvokeCode = [];
|
||||
this.inputExpression = null;
|
||||
};
|
||||
|
||||
TagHandlerNode.convertNode = function(node, tag) {
|
||||
extend(node, TagHandlerNode.prototype);
|
||||
TagHandlerNode.call(node, tag);
|
||||
};
|
||||
|
||||
TagHandlerNode.prototype = {
|
||||
|
||||
addDynamicAttribute: function(name, value) {
|
||||
if (!this.dynamicAttributes) {
|
||||
this.dynamicAttributes = {};
|
||||
}
|
||||
|
||||
this.dynamicAttributes[name] = value;
|
||||
},
|
||||
|
||||
addPreInvokeCode: function(code) {
|
||||
this.preInvokeCode.push(code);
|
||||
},
|
||||
|
||||
addPostInvokeCode: function(code) {
|
||||
this.postInvokeCode.push(code);
|
||||
},
|
||||
|
||||
setInputExpression: function(expression) {
|
||||
this.inputExpression = expression;
|
||||
},
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
|
||||
/*
|
||||
context.t(
|
||||
handler,
|
||||
props,
|
||||
bodyFunc,
|
||||
dynamicAttributes,
|
||||
namespacedProps)
|
||||
*/
|
||||
|
||||
///////////////////
|
||||
|
||||
|
||||
var handlerVar = addHandlerVar(template, this.tag.handlerClass);
|
||||
|
||||
this.tag.forEachImportedVariable(function(importedVariable) {
|
||||
this.setProperty(importedVariable.targetProperty, new Expression(importedVariable.expression));
|
||||
}, this);
|
||||
|
||||
|
||||
var namespacedProps = raptor.extend({}, this.getPropertiesByNS());
|
||||
delete namespacedProps[''];
|
||||
var hasNamespacedProps = !objects.isEmpty(namespacedProps);
|
||||
|
||||
///////////
|
||||
|
||||
var _this = this;
|
||||
|
||||
var variableNames = [];
|
||||
|
||||
_this.tag.forEachVariable(function(v) {
|
||||
var varName;
|
||||
|
||||
if (v.nameFromAttribute) {
|
||||
|
||||
|
||||
var possibleNameAttributes = v.nameFromAttribute.split(/\s+or\s+|\s*,\s*/i);
|
||||
for (var i=0,len=possibleNameAttributes.length; i<len; i++) {
|
||||
var attrName = possibleNameAttributes[i],
|
||||
keep = false;
|
||||
if (attrName.endsWith('|keep')) {
|
||||
keep = true;
|
||||
attrName = attrName.slice(0, 0-'|keep'.length);
|
||||
possibleNameAttributes[i] = attrName;
|
||||
}
|
||||
|
||||
varName = this.getAttribute(attrName);
|
||||
if (varName) {
|
||||
if (!keep) {
|
||||
this.removeProperty(attrName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!varName) {
|
||||
this.addError('Attribute ' + possibleNameAttributes.join(" or ") + ' is required');
|
||||
varName = "_var"; // Let it continue with errors
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
varName = v.name;
|
||||
if (!varName) {
|
||||
this.addError('Variable name is required');
|
||||
varName = "_var"; // Let it continue with errors
|
||||
}
|
||||
}
|
||||
|
||||
variableNames.push(varName);
|
||||
|
||||
}, this);
|
||||
|
||||
if (_this.preInvokeCode.length) {
|
||||
_this.preInvokeCode.forEach(function(code) {
|
||||
template
|
||||
.indent()
|
||||
.code(code)
|
||||
.code("\n");
|
||||
});
|
||||
}
|
||||
|
||||
template.contextMethodCall("t", function() {
|
||||
template
|
||||
.code("\n")
|
||||
.indent(function() {
|
||||
template
|
||||
.line(handlerVar + ',')
|
||||
.indent();
|
||||
|
||||
if (_this.inputExpression) {
|
||||
template.code(_this.inputExpression);
|
||||
}
|
||||
else {
|
||||
template.code(getPropsStr(_this.getProperties(), template));
|
||||
}
|
||||
|
||||
|
||||
if (_this.hasChildren()) {
|
||||
var bodyParams = [];
|
||||
|
||||
forEach(variableNames, function(varName) {
|
||||
bodyParams.push(varName);
|
||||
});
|
||||
|
||||
template
|
||||
.code(',\n')
|
||||
.line("function(" + bodyParams.join(",") + ") {")
|
||||
.indent(function() {
|
||||
_this.generateCodeForChildren(template);
|
||||
})
|
||||
.indent()
|
||||
.code('}');
|
||||
}
|
||||
else {
|
||||
if (hasNamespacedProps || _this.dynamicAttributes) {
|
||||
template
|
||||
.code(",\n")
|
||||
.indent().code("null");
|
||||
}
|
||||
}
|
||||
|
||||
if (_this.dynamicAttributes) {
|
||||
template
|
||||
.code(",\n")
|
||||
.indent().code(getPropsStr(_this.dynamicAttributes, template));
|
||||
}
|
||||
else {
|
||||
if (hasNamespacedProps) {
|
||||
template
|
||||
.code(",\n")
|
||||
.indent().code("null");
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNamespacedProps) {
|
||||
template
|
||||
.code(",\n")
|
||||
.line("{")
|
||||
.indent(function() {
|
||||
var first = true;
|
||||
forEachEntry(namespacedProps, function(uri, props) {
|
||||
if (!first) {
|
||||
template.code(',\n');
|
||||
}
|
||||
template.code(template.indentStr() + '"' + uri + '": ' + getPropsStr(props, template));
|
||||
first = false;
|
||||
|
||||
});
|
||||
})
|
||||
.indent()
|
||||
.code("}");
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
if (_this.postInvokeCode.length) {
|
||||
_this.postInvokeCode.forEach(function(code) {
|
||||
template
|
||||
.indent()
|
||||
.code(code)
|
||||
.code("\n");
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return TagHandlerNode;
|
||||
});
|
||||
106
lib/taglibs/core/TemplateNode.js
Normal file
106
lib/taglibs/core/TemplateNode.js
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/core/TemplateNode",
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var forEach = raptor.forEach;
|
||||
|
||||
var TemplateNode = function(props) {
|
||||
TemplateNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
TemplateNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var name = this.getProperty("name"),
|
||||
params = this.getProperty("params");
|
||||
|
||||
if (params) {
|
||||
params = params.split(/(?:\s*,\s*)|(?:\s+)/g);
|
||||
|
||||
forEach(params, function(param) {
|
||||
param = param.trim();
|
||||
if (param.length) {
|
||||
template.addVar(param, "data." + param);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
else {
|
||||
params = null;
|
||||
}
|
||||
|
||||
this.forEachProperty(function(uri, name, value) {
|
||||
if (!uri) {
|
||||
uri = this.uri;
|
||||
}
|
||||
|
||||
if (name === 'functions' || name === 'importFunctions' || name === 'importHelperFunctions') {
|
||||
forEach(value.split(/\s*[,;]\s*/g), function(funcName) {
|
||||
var func = template.compiler.taglibs.getFunction(uri, funcName);
|
||||
if (!func) {
|
||||
this.addError('Function with name "' + funcName + '" not found in taglib "' + uri + '"');
|
||||
}
|
||||
else {
|
||||
template.addHelperFunction(func.functionClass, funcName, func.bindToContext === true);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
else if (name === 'importHelperObject') {
|
||||
var varName = value;
|
||||
if (!template.compiler.taglibs.isTaglib(uri)) {
|
||||
this.addError('Helper object not found for taglib "' + uri + '". Taglib with URI "' + uri + '" not found.');
|
||||
}
|
||||
else {
|
||||
var helperObject = template.compiler.taglibs.getHelperObject(uri);
|
||||
if (!helperObject) {
|
||||
this.addError('Helper object not found for taglib "' + uri + '"');
|
||||
}
|
||||
else {
|
||||
if (helperObject.className) {
|
||||
template.addVar(varName, "context.o(" + JSON.stringify(helperObject.className) + ")");
|
||||
}
|
||||
else if (helperObject.moduleName) {
|
||||
template.addStaticVar(varName, "require(" + JSON.stringify(helperObject.moduleName) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (name) {
|
||||
template.setTemplateName(name);
|
||||
}
|
||||
else if (!template.getTemplateName()) {
|
||||
this.addError('The "name" attribute is required for the ' + this.toString() + ' tag or it must be passed in as a compiler option.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.generateCodeForChildren(template);
|
||||
}
|
||||
};
|
||||
|
||||
return TemplateNode;
|
||||
});
|
||||
58
lib/taglibs/core/VarNode.js
Normal file
58
lib/taglibs/core/VarNode.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/VarNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
||||
|
||||
var VarNode = function(props) {
|
||||
VarNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
VarNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var varName = this.getProperty("name"),
|
||||
value = this.getProperty("value") || this.getProperty("string-value");
|
||||
|
||||
if (!varName) {
|
||||
this.addError('"name" attribute is required');
|
||||
}
|
||||
else if (!varNameRegExp.test(varName)) {
|
||||
this.addError('Invalid variable name of "' + varName + '"');
|
||||
varName = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (varName) {
|
||||
template.statement('var ' + varName + (value ? "=" + value : "") + ";");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return VarNode;
|
||||
});
|
||||
58
lib/taglibs/core/WhenNode.js
Normal file
58
lib/taglibs/core/WhenNode.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/WhenNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var WhenNode = function(props) {
|
||||
WhenNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
WhenNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var test = this.getProperty("test");
|
||||
|
||||
if (!test) {
|
||||
this.addError('"test" attribute is required for ' + this.toString() + " tag.");
|
||||
}
|
||||
|
||||
var ifCode = 'if (' + test + ')';
|
||||
if (!this.firstWhen) {
|
||||
template.line('else ' + ifCode + ' {');
|
||||
}
|
||||
else {
|
||||
template.statement(ifCode + ' {');
|
||||
}
|
||||
|
||||
template
|
||||
.indent(function() {
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return WhenNode;
|
||||
});
|
||||
80
lib/taglibs/core/WithNode.js
Normal file
80
lib/taglibs/core/WithNode.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/WithNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var AttributeSplitter = require('raptor/templating/compiler/AttributeSplitter'),
|
||||
varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
||||
|
||||
var WithNode = function(props) {
|
||||
WithNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
WithNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var vars = this.getProperty("vars"),
|
||||
_this = this;
|
||||
|
||||
if (!vars) {
|
||||
this.addError('"vars" attribute is required');
|
||||
}
|
||||
|
||||
var withVars = AttributeSplitter.parse(
|
||||
vars,
|
||||
{
|
||||
"*": {
|
||||
type: "expression"
|
||||
}
|
||||
},
|
||||
{
|
||||
ordered: true,
|
||||
errorHandler: function(message) {
|
||||
_this.addError('Invalid variable declarations of "' + vars + '". Error: ' + message);
|
||||
}
|
||||
});
|
||||
|
||||
var varDefs = [];
|
||||
|
||||
raptor.forEach(withVars, function(withVar, i) {
|
||||
if (!varNameRegExp.test(withVar.name)) {
|
||||
this.addError('Invalid variable name of "' + withVar.name + '" in "' + vars + '"');
|
||||
}
|
||||
varDefs.push((i > 0 ? template.indentStr(1) + " " : "") + withVar.name + (withVar.value ? ("=" + withVar.value) : "") + (i < withVars.length - 1 ? ",\n" : ";"));
|
||||
}, this);
|
||||
|
||||
|
||||
template
|
||||
.statement('(function() {')
|
||||
.indent(function() {
|
||||
template.statement('var ' + varDefs.join(""));
|
||||
this.generateCodeForChildren(template);
|
||||
}, this)
|
||||
.line('}());');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return WithNode;
|
||||
});
|
||||
72
lib/taglibs/core/WriteNode.js
Normal file
72
lib/taglibs/core/WriteNode.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/core/WriteNode',
|
||||
'raptor/templating/compiler/Node',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var escapeXmlAttr = require('raptor/xml/utils').escapeXmlAttr,
|
||||
EscapeXmlContext = require('raptor/templating/compiler/EscapeXmlContext');
|
||||
|
||||
var WriteNode = function(props) {
|
||||
WriteNode.superclass.constructor.call(this, 'write');
|
||||
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
WriteNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var expression = this.getExpression(),
|
||||
escapeXml;
|
||||
|
||||
if (this.hasProperty('escapeXml')) {
|
||||
escapeXml = this.getProperty("escapeXml") !== false;
|
||||
}
|
||||
else {
|
||||
escapeXml = this.getProperty("escape-xml") !== false;
|
||||
}
|
||||
|
||||
if (escapeXml === true) {
|
||||
if (this.getEscapeXmlContext() === EscapeXmlContext.Attribute) {
|
||||
expression = template.getStaticHelperFunction("escapeXmlAttr", "xa") + "(" + expression + ")";
|
||||
}
|
||||
else {
|
||||
expression = template.getStaticHelperFunction("escapeXml", "x") + "(" + expression + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (expression) {
|
||||
template.write(expression);
|
||||
}
|
||||
},
|
||||
|
||||
getExpression: function() {
|
||||
return this.getProperty("expression") || this.getProperty("value") || this.getAttribute("expression") || this.getAttribute("value");
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return '<c:write expression="' + this.getExpression() + '">';
|
||||
}
|
||||
};
|
||||
|
||||
return WriteNode;
|
||||
});
|
||||
88
lib/taglibs/core/compile-time-optimizer.json
Normal file
88
lib/taglibs/core/compile-time-optimizer.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"type": "js",
|
||||
"path": "AssignNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "CoreTagTransformer.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ElseTagTransformer.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "CoreTextTransformer.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ForNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "IfNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ElseIfNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ElseNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "TagHandlerNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "TemplateNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "WriteNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "InvokeNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ScriptletNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "ChooseNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "DefNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "IncludeNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "OtherwiseNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "VarNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "WhenNode.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "WithNode.js"
|
||||
},
|
||||
{
|
||||
"type": "rtld",
|
||||
"path": "core.rtld"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
lib/taglibs/core/compile-time-package.json
Normal file
13
lib/taglibs/core/compile-time-package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "rtld",
|
||||
"name": "raptor/templating/taglibs/core",
|
||||
"version": "1.0",
|
||||
"description": "RaptorJS Templating Module",
|
||||
"homepage": "http://raptorjs.org",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Patrick Steele-Idem",
|
||||
"email": "psteeleidem@ebay.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
472
lib/taglibs/core/core.rtld
Normal file
472
lib/taglibs/core/core.rtld
Normal file
@ -0,0 +1,472 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<uri>http://raptorjs.org/templates/core</uri>
|
||||
<short-name>core</short-name>
|
||||
<prefix>c</prefix>
|
||||
|
||||
<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>
|
||||
<uri>*</uri>
|
||||
<name>functions</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<uri>*</uri>
|
||||
<name>import-functions</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<uri>*</uri>
|
||||
<name>import-helper-object</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/TemplateNode</node-class>
|
||||
</tag>
|
||||
|
||||
<tag extends="template">
|
||||
|
||||
<uri></uri>
|
||||
<name>template</name>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>*</name>
|
||||
<uri>*</uri> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<attribute uri="core">
|
||||
<name>space</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>whitespace</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>for</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>for-each</name>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>if</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>else</name>
|
||||
<type>empty</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>else-if</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>attrs</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>when</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>with</name>
|
||||
<type>custom</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>otherwise</name>
|
||||
<type>empty</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>parse-body-text</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>trim-body-indent</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>strip</name>
|
||||
<type>boolean</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>bodyContent</name>
|
||||
<type>expression</type>
|
||||
<deprecated>Use "content" attribute instead. This will be removed in the future.</deprecated>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>content</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>replace</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<attribute uri="core">
|
||||
<name>input</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/core/CoreTagTransformer</class-name>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>for</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>write</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>raptor/templating/taglibs/core/IfNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>test</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>else</name>
|
||||
<node-class>raptor/templating/taglibs/core/ElseNode</node-class>
|
||||
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/core/ElseTagTransformer</class-name>
|
||||
<after>raptor/templating/taglibs/core/CoreTagTransformer</after>
|
||||
<properties>
|
||||
<type>else</type>
|
||||
</properties>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>else-if</name>
|
||||
<attribute name="test" type="expression"/>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/ElseIfNode</node-class>
|
||||
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/core/ElseTagTransformer</class-name>
|
||||
<after>raptor/templating/taglibs/core/CoreTagTransformer</after>
|
||||
<properties>
|
||||
<type>else-if</type>
|
||||
</properties>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>invoke</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/InvokeNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>function</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
<required>true</required>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>*</name>
|
||||
<uri></uri>
|
||||
<type>string</type>
|
||||
<allow-expressions>true</allow-expressions>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>choose</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/ChooseNode</node-class>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>when</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/WhenNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>test</name>
|
||||
<type>expression</type>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>otherwise</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/OtherwiseNode</node-class>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>def</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>raptor/templating/taglibs/core/WithNode</node-class>
|
||||
|
||||
<attribute>
|
||||
<name>vars</name>
|
||||
<type>custom</type>
|
||||
<allow-expressions>false</allow-expressions>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>include</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>
|
||||
|
||||
<dynamic-attributes>true</dynamic-attributes>
|
||||
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>attr</name>
|
||||
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>value</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>uri</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>prefix</name>
|
||||
<type>string</type>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>var</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>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>assign</name>
|
||||
|
||||
<node-class>raptor/templating/taglibs/core/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>
|
||||
<class-name>raptor/templating/taglibs/core/CoreTextTransformer</class-name>
|
||||
</text-transformer>
|
||||
|
||||
</raptor-taglib>
|
||||
15
lib/taglibs/html/CommentTag.js
Normal file
15
lib/taglibs/html/CommentTag.js
Normal file
@ -0,0 +1,15 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/html/CommentTag',
|
||||
function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
return {
|
||||
process: function(input, context) {
|
||||
context.write('<!--');
|
||||
if (input.invokeBody) {
|
||||
input.invokeBody();
|
||||
}
|
||||
context.write('-->');
|
||||
}
|
||||
};
|
||||
});
|
||||
60
lib/taglibs/html/DocTypeNode.js
Normal file
60
lib/taglibs/html/DocTypeNode.js
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
'raptor/templating/taglibs/html/DocTypeNode',
|
||||
'raptor/templating/compiler/ElementNode',
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
var ExpressionParser = require('raptor/templating/compiler/ExpressionParser');
|
||||
|
||||
var DocTypeNode = function(props) {
|
||||
DocTypeNode.superclass.constructor.call(this);
|
||||
if (props) {
|
||||
this.setProperties(props);
|
||||
}
|
||||
};
|
||||
|
||||
DocTypeNode.prototype = {
|
||||
|
||||
doGenerateCode: function(template) {
|
||||
var doctype = this.getAttribute("value") || this.getProperty("value");
|
||||
|
||||
template.text("<!DOCTYPE ");
|
||||
|
||||
ExpressionParser.parse(
|
||||
doctype,
|
||||
{
|
||||
text: function(text, escapeXml) {
|
||||
template.text(text);
|
||||
},
|
||||
expression: function(expression) {
|
||||
template.write(expression);
|
||||
},
|
||||
error: function(message) {
|
||||
this.addError('Invalid doctype: "' + doctype + '". ' + message);
|
||||
}
|
||||
},
|
||||
this);
|
||||
|
||||
template.text(">");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return DocTypeNode;
|
||||
});
|
||||
68
lib/taglibs/html/HtmlTagTransformer.js
Normal file
68
lib/taglibs/html/HtmlTagTransformer.js
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
define.Class(
|
||||
"raptor/templating/taglibs/html/HtmlTagTransformer",
|
||||
['raptor'],
|
||||
function(raptor, require) {
|
||||
"use strict";
|
||||
|
||||
var DocTypeNode = require('raptor/templating/taglibs/html/DocTypeNode');
|
||||
|
||||
return {
|
||||
|
||||
process: function(node, compiler) {
|
||||
|
||||
if (node.isElementNode()) {
|
||||
var options = compiler.options || {};
|
||||
var preserveWhitespace = options.preserveWhitespace || {};
|
||||
var allowSelfClosing = options.allowSelfClosing || {};
|
||||
var startTagOnly = options.startTagOnly || {};
|
||||
|
||||
var lookupKey = node.uri ? node.uri + ":" + node.localName : node.localName;
|
||||
|
||||
if (node.isPreserveWhitespace() == null) {
|
||||
if (preserveWhitespace[lookupKey] === true) {
|
||||
node.setPreserveWhitespace(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowSelfClosing[lookupKey] === true) {
|
||||
node.setAllowSelfClosing(true);
|
||||
}
|
||||
|
||||
if (compiler.options.xhtml !== true && startTagOnly[lookupKey] === true) {
|
||||
node.setStartTagOnly(true);
|
||||
}
|
||||
|
||||
|
||||
if (node.getQName() === 'html' && node.hasAttributeNS("http://raptorjs.org/templates/html", "doctype")) {
|
||||
var doctype = node.getAttributeNS("http://raptorjs.org/templates/html", "doctype");
|
||||
|
||||
var docTypeNode = new DocTypeNode({
|
||||
value: doctype,
|
||||
pos: node.getPosition()
|
||||
});
|
||||
|
||||
node.parentNode.insertBefore(docTypeNode, node);
|
||||
|
||||
node.removeAttributeNS("http://raptorjs.org/templates/html", "doctype");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
16
lib/taglibs/html/compile-time-optimizer.json
Normal file
16
lib/taglibs/html/compile-time-optimizer.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"type": "js",
|
||||
"path": "HtmlTagTransformer.js"
|
||||
},
|
||||
{
|
||||
"type": "js",
|
||||
"path": "DocTypeNode.js"
|
||||
},
|
||||
{
|
||||
"type": "rtld",
|
||||
"path": "html.rtld"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
lib/taglibs/html/compile-time-package.json
Normal file
13
lib/taglibs/html/compile-time-package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "rtld",
|
||||
"name": "raptor/templating/taglibs/html",
|
||||
"version": "1.0",
|
||||
"description": "RaptorJS Templating Module",
|
||||
"homepage": "http://raptorjs.org",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Patrick Steele-Idem",
|
||||
"email": "psteeleidem@ebay.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
54
lib/taglibs/html/html.rtld
Normal file
54
lib/taglibs/html/html.rtld
Normal file
@ -0,0 +1,54 @@
|
||||
<raptor-taglib>
|
||||
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
<short-name>html</short-name>
|
||||
<uri>http://raptorjs.org/templates/html</uri>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>pre</name>
|
||||
<uri></uri> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/html/HtmlTagTransformer</class-name>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>html</name>
|
||||
<uri></uri> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<attribute name="doctype" type="string"/>
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/html/HtmlTagTransformer</class-name>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>doctype</name>
|
||||
<attribute name="value" type="custom"/>
|
||||
<node-class>raptor/templating/taglibs/html/DocTypeNode</node-class>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
||||
<name>*</name>
|
||||
<uri>*</uri> <!-- Register attributes supported by all tags in all namespaces -->
|
||||
|
||||
<!-- Compiler that applies to all tags as well -->
|
||||
<transformer>
|
||||
<class-name>raptor/templating/taglibs/html/HtmlTagTransformer</class-name>
|
||||
</transformer>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>comment</name>
|
||||
<handler-class>raptor/templating/taglibs/html/CommentTag</handler-class>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
25
lib/taglibs/layout/PlaceholderTag.js
Normal file
25
lib/taglibs/layout/PlaceholderTag.js
Normal file
@ -0,0 +1,25 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/layout/PlaceholderTag',
|
||||
function(require) {
|
||||
return {
|
||||
render: function(input, context) {
|
||||
|
||||
var content = input.content[input.name];
|
||||
|
||||
if (content) {
|
||||
if (content.value) {
|
||||
context.write(content.value);
|
||||
}
|
||||
else if (content.invokeBody) {
|
||||
content.invokeBody();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (input.invokeBody) {
|
||||
input.invokeBody();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
9
lib/taglibs/layout/PutTag.js
Normal file
9
lib/taglibs/layout/PutTag.js
Normal file
@ -0,0 +1,9 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/layout/PutTag',
|
||||
function(require) {
|
||||
return {
|
||||
render: function(input, context) {
|
||||
input._layout.handlePutTag(input);
|
||||
}
|
||||
};
|
||||
});
|
||||
31
lib/taglibs/layout/UseTag.js
Normal file
31
lib/taglibs/layout/UseTag.js
Normal file
@ -0,0 +1,31 @@
|
||||
define(
|
||||
'raptor/templating/taglibs/layout/UseTag',
|
||||
function(require) {
|
||||
|
||||
var templating = require('raptor/templating');
|
||||
var raptor = require('raptor');
|
||||
|
||||
return {
|
||||
render: function(input, context) {
|
||||
var content = {};
|
||||
|
||||
input.invokeBody({
|
||||
handlePutTag: function(putTag) {
|
||||
content[putTag.into] = putTag;
|
||||
}
|
||||
});
|
||||
|
||||
var viewModel = raptor.extend(
|
||||
input.dynamicAttributes || {},
|
||||
{
|
||||
layoutContent: content
|
||||
});
|
||||
|
||||
templating.render(
|
||||
input.template,
|
||||
viewModel,
|
||||
context);
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
22
lib/taglibs/layout/layout.rtld
Normal file
22
lib/taglibs/layout/layout.rtld
Normal file
@ -0,0 +1,22 @@
|
||||
<raptor-taglib>
|
||||
<tlib-version>1.0</tlib-version>
|
||||
<short-name>layout</short-name>
|
||||
<uri>http://raptorjs.org/templates/layout</uri>
|
||||
|
||||
<tag name="use" renderer="raptor/templating/taglibs/layout/UseTag" dynamic-attributes="true" dynamic-attributes-remove-dashes="true">
|
||||
<attribute name="template"/>
|
||||
<variable name="_layout"/>
|
||||
</tag>
|
||||
|
||||
<tag name="put" renderer="raptor/templating/taglibs/layout/PutTag">
|
||||
<attribute name="into"/>
|
||||
<attribute name="value"/>
|
||||
<import-variable name="_layout"/>
|
||||
</tag>
|
||||
|
||||
<tag name="placeholder" renderer="raptor/templating/taglibs/layout/PlaceholderTag">
|
||||
<attribute name="name"/>
|
||||
<import-variable expression="data.layoutContent" target-property="content"/>
|
||||
</tag>
|
||||
|
||||
</raptor-taglib>
|
||||
7
lib/taglibs/layout/optimizer.json
Normal file
7
lib/taglibs/layout/optimizer.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": [
|
||||
"UseTag.js",
|
||||
"PutTag.js",
|
||||
"PlaceholderTag.js"
|
||||
]
|
||||
}
|
||||
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "raptor-templates",
|
||||
"version": "0.1.0-SNAPSHOT",
|
||||
"description": "Raptor Templates",
|
||||
"keywords": [
|
||||
"templating",
|
||||
"template",
|
||||
"async",
|
||||
"streaming"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/raptorjs/raptor-templates.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test"
|
||||
},
|
||||
"author": "Patrick Steele-Idem <psteeleidem@ebay.com>",
|
||||
"maintainers": ["Patrick Steele-Idem <psteeleidem@ebay.com>"],
|
||||
"dependencies": {
|
||||
"raptor-logging": "~0.1.0-SNAPSHOT"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.15.1",
|
||||
"chai": "~1.8.1"
|
||||
},
|
||||
"license": "Apache License v2.0",
|
||||
"bin": {},
|
||||
"main": "lib/index.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
}
|
||||
}
|
||||
64
test/.jshintrc
Normal file
64
test/.jshintrc
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"predef": [
|
||||
"jasmine",
|
||||
"spyOn",
|
||||
"it",
|
||||
"xit",
|
||||
"console",
|
||||
"describe",
|
||||
"xdescribe",
|
||||
"beforeEach",
|
||||
"before",
|
||||
"after",
|
||||
"waits",
|
||||
"waitsFor",
|
||||
"runs",
|
||||
"raptor",
|
||||
"$rset",
|
||||
"$radd",
|
||||
"$rget",
|
||||
"$renv",
|
||||
"$rwidgets",
|
||||
"$",
|
||||
"dust",
|
||||
"__rhinoHelpers",
|
||||
"Packages",
|
||||
"JavaAdapter",
|
||||
"unescape"
|
||||
],
|
||||
|
||||
"globals": {
|
||||
"define": true,
|
||||
"require": true
|
||||
},
|
||||
|
||||
"node" : true,
|
||||
"es5" : false,
|
||||
"browser" : true,
|
||||
"boss" : false,
|
||||
"curly": false,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": true,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": false,
|
||||
"white": false,
|
||||
"eqeqeq": false,
|
||||
"latedef": true,
|
||||
"unused": "vars",
|
||||
|
||||
/* Relaxing options: */
|
||||
"eqnull": true
|
||||
}
|
||||
29
test/raptor-templates-tests.js
Normal file
29
test/raptor-templates-tests.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
var chai = require('chai');
|
||||
chai.Assertion.includeStack = true;
|
||||
require('chai').should();
|
||||
var expect = require('chai').expect;
|
||||
var path = require('path');
|
||||
|
||||
|
||||
describe('raptor-templates' , function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
for (var k in require.cache) {
|
||||
if (require.cache.hasOwnProperty(k)) {
|
||||
delete require.cache[k];
|
||||
}
|
||||
}
|
||||
|
||||
require('raptor-logging').configureLoggers({
|
||||
'raptor-templates': 'INFO'
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should do something', function(done) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user