Flattened directory structure a bit by dropping lib

This commit is contained in:
Patrick Steele-Idem 2014-04-29 22:21:20 -06:00
parent 64be29ca79
commit 1c6dd4f6cc
74 changed files with 3796 additions and 191 deletions

View File

@ -19,7 +19,7 @@ var TemplateBuilder = require('./TemplateBuilder');
var parser = require('./parser');
var Expression = require('./Expression');
var TypeConverter = require('./TypeConverter');
var raptorTaglibs = require('raptor-taglibs');
var taglibs = require('./taglibs');
var nodePath = require('path');
var ok = require('assert').ok;
var attributeParser = require('./attribute-parser');
@ -28,12 +28,12 @@ var inherit = require('raptor-util/inherit');
var _Node = require('./Node');
var ElementNode = require('./ElementNode');
var TextNode = require('./TextNode');
var TagHandlerNode = require('raptor-taglib-core/TagHandlerNode');
var TagHandlerNode = require('../taglibs/core/TagHandlerNode');
function TemplateCompiler(path, options) {
this.dirname = nodePath.dirname(path);
this.path = path;
this.taglibs = raptorTaglibs.buildLookup(this.dirname);
this.taglibs = taglibs.buildLookup(this.dirname);
this.options = options || {};
this.errors = [];
}
@ -61,11 +61,12 @@ TemplateCompiler.prototype = {
_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)
var transformerFunc = transformer.getFunc();
transformerFunc.call(transformer, 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)
}
});
} catch (e) {
throw createError(new Error('Unable to compile template at path "' + templateBuilder.filePath + '. Error: ' + e.message), e);
throw 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

View File

@ -1,3 +1,3 @@
{
"main": "lib/raptor-templates-compiler.js"
"main": "./raptor-templates-compiler.js"
}

View File

@ -15,9 +15,7 @@
*/
'use strict';
var extend = require('raptor-util').extend;
var raptorTaglibs = require('raptor-taglibs');
var fs = require('fs');
var nodePath = require('path');
var defaultOptions = {
minify: false,
@ -90,10 +88,10 @@ extend(exports, {
});
exports.TemplateCompiler = require('./TemplateCompiler');
exports.taglibs = require('./taglibs');
raptorTaglibs.excludeDir(nodePath.join(__dirname, '../..'));
raptorTaglibs.registerTaglib(require.resolve('raptor-taglib-core/raptor-taglib.json'));
raptorTaglibs.registerTaglib(require.resolve('raptor-taglib-html/raptor-taglib.json'));
raptorTaglibs.registerTaglib(require.resolve('raptor-taglib-caching/raptor-taglib.json'));
raptorTaglibs.registerTaglib(require.resolve('raptor-taglib-layout/raptor-taglib.json'));
raptorTaglibs.registerTaglib(require.resolve('raptor-taglib-async/raptor-taglib.json'));
exports.taglibs.registerTaglib(require.resolve('../taglibs/core/raptor-taglib.json'));
exports.taglibs.registerTaglib(require.resolve('../taglibs/html/raptor-taglib.json'));
exports.taglibs.registerTaglib(require.resolve('../taglibs/caching/raptor-taglib.json'));
exports.taglibs.registerTaglib(require.resolve('../taglibs/layout/raptor-taglib.json'));
exports.taglibs.registerTaglib(require.resolve('../taglibs/async/raptor-taglib.json'));

260
compiler/taglibs/Taglib.js Normal file
View File

@ -0,0 +1,260 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var forEachEntry = require('raptor-util').forEachEntry;
var ok = require('assert').ok;
var makeClass = require('raptor-util').makeClass;
function Taglib(id) {
ok(id, '"id" expected');
this.id = id;
this.dirname = null;
this.tags = {};
this.textTransformers = [];
this.attributes = {};
this.patternAttributes = [];
this.inputFilesLookup = {};
}
Taglib.prototype = {
addInputFile: function(path) {
this.inputFilesLookup[path] = true;
},
getInputFiles: function() {
return Object.keys(this.inputFilesLookup);
},
addAttribute: function (attribute) {
if (attribute.pattern) {
this.patternAttributes.push(attribute);
} else if (attribute.name) {
this.attributes[attribute.name] = attribute;
} else {
throw new Error('Invalid attribute: ' + require('util').inspect(attribute));
}
},
getAttribute: function (name) {
var attribute = this.attributes[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) {
ok(arguments.length === 1, 'Invalid args');
ok(tag.name, '"tag.name" is required');
this.tags[tag.name] = tag;
tag.taglibId = this.id;
},
addTextTransformer: function (transformer) {
this.textTransformers.push(transformer);
},
forEachTag: function (callback, thisObj) {
forEachEntry(this.tags, function (key, tag) {
callback.call(thisObj, tag);
}, this);
}
};
Taglib.Tag = makeClass({
$init: function(taglib) {
this.taglibId = taglib ? taglib.id : null;
this.renderer = null;
this.nodeClass = null;
this.template = null;
this.attributes = {};
this.transformers = {};
this.nestedVariables = {};
this.importedVariables = {};
this.patternAttributes = [];
},
inheritFrom: function (superTag) {
var subTag = this;
/*
* Have the sub tag inherit any properties from the super tag that are not in the sub tag
*/
forEachEntry(superTag, function (k, v) {
if (subTag[k] === undefined) {
subTag[k] = v;
}
});
function inheritProps(sub, sup) {
forEachEntry(sup, function (k, v) {
if (!sub[k]) {
sub[k] = v;
}
});
}
[
'attributes',
'transformers',
'nestedVariables',
'importedVariables'
].forEach(function (propName) {
inheritProps(subTag[propName], superTag[propName]);
});
subTag.patternAttributes = superTag.patternAttributes.concat(subTag.patternAttributes);
},
forEachVariable: function (callback, thisObj) {
forEachEntry(this.nestedVariables, function (key, variable) {
callback.call(thisObj, variable);
});
},
forEachImportedVariable: function (callback, thisObj) {
forEachEntry(this.importedVariables, function (key, importedVariable) {
callback.call(thisObj, importedVariable);
});
},
forEachTransformer: function (callback, thisObj) {
forEachEntry(this.transformers, function (key, transformer) {
callback.call(thisObj, transformer);
});
},
hasTransformers: function () {
/*jshint unused:false */
for (var k in this.transformers) {
if (this.transformers.hasOwnProperty(k)) {
return true;
}
}
return false;
},
addAttribute: function (attr) {
if (attr.pattern) {
this.patternAttributes.push(attr);
} else {
if (attr.name === '*') {
attr.dynamicAttribute = true;
if (attr.targetProperty === null || attr.targetProperty === '') {
attr.targetProperty = null;
}
else if (!attr.targetProperty) {
attr.targetProperty = '*';
}
}
this.attributes[attr.name] = attr;
}
},
toString: function () {
return '[Tag: <' + this.name + '@' + this.taglibId + '>]';
},
forEachAttribute: function (callback, thisObj) {
for (var attrName in this.attributes) {
if (this.attributes.hasOwnProperty(attrName)) {
callback.call(thisObj, this.attributes[attrName]);
}
}
},
addNestedVariable: function (nestedVariable) {
var key = nestedVariable.nameFromAttribute ? 'attr:' + nestedVariable.nameFromAttribute : nestedVariable.name;
this.nestedVariables[key] = nestedVariable;
},
addImportedVariable: function (importedVariable) {
var key = importedVariable.targetProperty;
this.importedVariables[key] = importedVariable;
},
addTransformer: function (transformer) {
var key = transformer.path;
transformer.taglibId = this.taglibId;
this.transformers[key] = transformer;
}
});
Taglib.Attribute = makeClass({
$init: function(name) {
this.name = name;
this.type = null;
this.required = false;
this.type = 'string';
this.allowExpressions = true;
}
});
Taglib.Property = makeClass({
$init: function() {
this.name = null;
this.type = 'string';
this.value = undefined;
}
});
Taglib.NestedVariable = makeClass({
$init: function() {
this.name = null;
}
});
Taglib.ImportedVariable = makeClass({
$init: function() {
this.targetProperty = null;
this.expression = null;
}
});
var nextTransformerId = 0;
Taglib.Transformer = makeClass({
$init: function() {
this.id = nextTransformerId++;
this.name = null;
this.tag = null;
this.path = null;
this.priority = null;
this._func = null;
this.properties = {};
},
getFunc: function () {
if (!this.path) {
throw new Error('Transformer path not defined for tag transformer (tag=' + this.tag + ')');
}
if (!this._func) {
var transformer = require(this.path);
if (typeof transformer === 'function') {
if (transformer.prototype.process) {
var Clazz = transformer;
var instance = new Clazz();
instance.id = this.id;
this._func = instance.process.bind(instance);
} else {
this._func = transformer;
}
} else {
this._func = transformer.process || transformer.transform;
}
}
return this._func;
},
toString: function () {
return '[Taglib.Transformer: ' + this.path + ']';
}
});
module.exports = Taglib;

View File

@ -0,0 +1,236 @@
var ok = require('assert').ok;
var createError = require('raptor-util').createError;
function transformerComparator(a, b) {
a = a.priority;
b = b.priority;
if (a == null) {
a = Number.MAX_VALUE;
}
if (b == null) {
b = Number.MAX_VALUE;
}
return a - b;
}
function merge(target, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
if (target[k] && typeof target[k] === 'object' &&
source[k] && typeof source[k] === 'object') {
if (Array.isArray(target[k]) || Array.isArray(source[k])) {
var targetArray = target[k];
var sourceArray = source[k];
if (!Array.isArray(targetArray)) {
targetArray = [targetArray];
}
if (!Array.isArray(sourceArray)) {
sourceArray = [sourceArray];
}
target[k] = [].concat(targetArray).concat(sourceArray);
} else {
var Ctor = target[k].constructor;
var newTarget = new Ctor();
merge(newTarget, target[k]);
merge(newTarget, source[k]);
target[k] = newTarget;
}
} else {
target[k] = source[k];
}
}
}
return target;
}
function TaglibLookup() {
this.merged = {};
this.taglibsById = {};
this._inputFiles = null;
}
TaglibLookup.prototype = {
addTaglib: function (taglib) {
ok(taglib, '"taglib" is required');
ok(taglib.id, '"taglib.id" expected');
if (this.taglibsById.hasOwnProperty(taglib.id)) {
return;
}
this.taglibsById[taglib.id] = taglib;
merge(this.merged, taglib);
},
getTag: function (element) {
if (typeof element === 'string') {
element = {
localName: element
};
}
var tags = this.merged.tags;
if (!tags) {
return;
}
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
return tags[tagKey];
},
getAttribute: function (element, attr) {
if (typeof element === 'string') {
element = {
localName: element
};
}
if (typeof attr === 'string') {
attr = {
localName: attr
};
}
var tags = this.merged.tags;
if (!tags) {
return;
}
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
var tag = tags[tagKey];
if (!tag) {
tag = tags['*'];
if (!tag) {
return;
}
}
var attrKey = attr.namespace ? attr.namespace + ':' + attr.localName : attr.localName;
function findAttribute(attributes) {
var attribute = attributes[attrKey];
if (attribute === undefined) {
if (tag.patternAttributes) {
for (var i = 0, len = tag.patternAttributes.length; i < len; i++) {
var patternAttribute = tag.patternAttributes[i];
if (patternAttribute.pattern.test(attrKey)) {
attribute = patternAttribute;
break;
}
}
}
if (attribute === undefined) {
attribute = tag.attributes['*'];
}
}
return attribute;
}
var attribute = findAttribute(tag.attributes);
if (attribute === null) {
// This is an imported attribute
attribute = findAttribute(this.merged.attributes);
}
return attribute;
},
forEachNodeTransformer: function (node, callback, thisObj) {
/*
* Based on the type of node we have to choose how to transform it
*/
if (node.isElementNode()) {
this.forEachTagTransformer(node, callback, thisObj);
} else if (node.isTextNode()) {
this.forEachTextTransformer(callback, thisObj);
}
},
forEachTagTransformer: function (element, callback, thisObj) {
if (typeof element === 'string') {
element = {
localName: element
};
}
var tagKey = element.namespace ? element.namespace + ':' + element.localName : element.localName;
/*
* If the node is an element node then we need to find all matching
* transformers based on the URI and the local name of the element.
*/
var transformers = [];
function addTransformer(transformer) {
if (!transformer || !transformer.getFunc) {
throw createError(new Error('Invalid transformer'));
}
transformers.push(transformer);
}
/*
* Handle all of the transformers for all possible matching transformers.
*
* Start with the least specific and end with the most specific.
*/
if (this.merged.tags[tagKey]) {
this.merged.tags[tagKey].forEachTransformer(addTransformer);
}
if (this.merged.tags['*']) {
this.merged.tags['*'].forEachTransformer(addTransformer);
}
transformers.sort(transformerComparator);
transformers.forEach(callback, thisObj);
},
forEachTextTransformer: function (callback, thisObj) {
this.merged.textTransformers.sort(transformerComparator);
this.merged.textTransformers.forEach(callback, thisObj);
},
getInputFiles: function() {
if (!this._inputFiles) {
var inputFilesSet = {};
for (var taglibId in this.taglibsById) {
if (this.taglibsById.hasOwnProperty(taglibId)) {
var taglibInputFiles = this.taglibsById[taglibId].getInputFiles();
var len = taglibInputFiles.length;
if (len) {
for (var i=0; i<len; i++) {
inputFilesSet[taglibInputFiles[i]] = true;
}
}
}
}
this._inputFiles = Object.keys(inputFilesSet);
}
return this._inputFiles;
}
};
module.exports = TaglibLookup;

View File

@ -0,0 +1,6 @@
exports.Taglib = require('./Taglib');
exports.loader = require('./taglib-loader');
exports.lookup = require('./taglib-lookup');
exports.buildLookup = exports.lookup.buildLookup;
exports.registerTaglib = exports.lookup.registerTaglib;
exports.excludeDir = exports.lookup.excludeDir;

View File

@ -0,0 +1,47 @@
// Rather than using a full-blown JavaScript parser, we are going to use a few regular expressions
// to tokenize the code and find what we are interested in
var tagStartRegExp = /(^\s*(?:exports.(?:tag|TAG))|(?:TAG)\s*=\s*)\{/m;
// Tokens: "<string>", '<string>', /*<some comment*/, //<single line comment>, {, }, ;
var tokensRegExp = /"(?:[^"]|\\")*"|'(?:[^'])|(\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/)|(\/\/.*)|[\{\};]/g;
function extractTagDef(code) {
var startMatches = tagStartRegExp.exec(code);
var tagDefStart;
var tagDefEnd;
if (startMatches) {
tagDefStart = startMatches.index + startMatches[1].length;
var nextTokenMatches;
tokensRegExp.lastIndex = tagDefStart;
var depth = 0;
while ((nextTokenMatches = tokensRegExp.exec(code))) {
if (nextTokenMatches[0] === '{') {
depth++;
continue;
} else if (nextTokenMatches[0] === '}') {
if (--depth === 0) {
tagDefEnd = tokensRegExp.lastIndex;
break;
}
}
else if (nextTokenMatches[0] === ';') {
tagDefEnd = nextTokenMatches.index;
break;
}
}
if (tagDefStart != null && tagDefEnd != null) {
var jsTagDef = code.substring(tagDefStart, tagDefEnd);
var tagDefObject = eval('(' + jsTagDef + ')');
return tagDefObject;
}
} else {
return null;
}
}
exports.extractTagDef = extractTagDef;

View File

@ -0,0 +1,410 @@
var fs = require('fs');
var ok = require('assert').ok;
var nodePath = require('path');
var Taglib = require('./Taglib');
var cache = {};
var forEachEntry = require('raptor-util').forEachEntry;
var raptorRegexp = require('raptor-regexp');
var tagDefFromCode = require('./tag-def-from-code');
var resolve = require('raptor-modules/resolver').serverResolveRequire;
var propertyHandlers = require('property-handlers');
function createDefaultTagDef() {
return {
attributes: {
'*': {
type: 'string',
targetProperty: null
}
}
};
}
function buildAttribute(attr, attrProps, path) {
propertyHandlers(attrProps, {
type: function(value) {
attr.type = value;
},
targetProperty: function(value) {
attr.targetProperty = value;
},
defaultValue: function(value) {
attr.defaultValue = value;
},
pattern: function(value) {
if (value === true) {
var patternRegExp = raptorRegexp.simple(attr.name);
attr.pattern = patternRegExp;
}
},
allowExpressions: function(value) {
attr.allowExpressions = value;
},
preserveName: function(value) {
attr.preserveName = value;
},
required: function(value) {
attr.required = value === true;
},
removeDashes: function(value) {
attr.removeDashes = value === true;
}
}, path);
return attr;
}
function handleAttributes(value, parent, path) {
forEachEntry(value, function(attrName, attrProps) {
var attr = new Taglib.Attribute(attrName);
if (attrProps == null) {
attrProps = {
type: 'string'
};
}
else if (typeof attrProps === 'string') {
attrProps = {
type: attrProps
};
}
buildAttribute(attr, attrProps, '"' + attrName + '" attribute as part of ' + path);
parent.addAttribute(attr);
});
}
function buildTag(tagObject, path, taglib, dirname) {
ok(tagObject);
ok(typeof path === 'string');
ok(taglib);
ok(typeof dirname === 'string');
var tag = new Taglib.Tag(taglib);
propertyHandlers(tagObject, {
name: function(value) {
tag.name = value;
},
renderer: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Renderer at path "' + path + '" does not exist.');
}
tag.renderer = path;
},
template: function(value) {
var path = nodePath.resolve(dirname, value);
if (!fs.existsSync(path)) {
throw new Error('Template at path "' + path + '" does not exist.');
}
tag.template = path;
},
attributes: function(value) {
handleAttributes(value, tag, path);
},
nodeClass: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Node module at path "' + path + '" does not exist.');
}
tag.nodeClass = path;
},
transformer: function(value) {
var transformer = new Taglib.Transformer();
if (typeof value === 'string') {
value = {
path: value
};
}
propertyHandlers(value, {
path: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Transformer at path "' + path + '" does not exist.');
}
transformer.path = path;
},
priority: function(value) {
transformer.priority = value;
},
name: function(value) {
transformer.name = value;
},
properties: function(value) {
var properties = transformer.properties || (transformer.properties = {});
for (var k in value) {
if (value.hasOwnProperty(k)) {
properties[k] = value[k];
}
}
}
}, 'transformer in ' + path);
ok(transformer.path, '"path" is required for transformer');
tag.addTransformer(transformer);
},
'var': function(value) {
tag.addNestedVariable({
name: value
});
},
vars: function(value) {
if (value) {
value.forEach(function(v, i) {
var nestedVariable;
if (typeof v === 'string') {
nestedVariable = {
name: v
};
} else {
nestedVariable = {};
propertyHandlers(v, {
name: function(value) {
nestedVariable.name = value;
},
nameFromAttribute: function(value) {
nestedVariable.nameFromAttribute = value;
}
}, 'var at index ' + i);
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
throw new Error('The "name" or "name-from-attribute" attribute is required for a nested variable');
}
}
tag.addNestedVariable(nestedVariable);
});
}
},
importVar: function(value) {
forEachEntry(value, function(varName, varValue) {
var importedVar = {
targetProperty: varName
};
var expression = varValue;
if (!expression) {
expression = varName;
}
else if (typeof expression === 'object') {
expression = expression.expression;
}
if (!expression) {
throw new Error('Invalid "import-var": ' + require('util').inspect(varValue));
}
importedVar.expression = expression;
tag.addImportedVariable(importedVar);
});
}
}, path);
return tag;
}
/**
* @param {String} tagsConfigPath path to tag definition file
* @param {String} tagsConfigDirname path to directory of tags config file (should be path.dirname(tagsConfigPath))
* @param {String} dir the path to directory to scan
* @param {String} taglib the taglib that is being loaded
*/
function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, taglib) {
dir = nodePath.resolve(tagsConfigDirname, dir);
var children = fs.readdirSync(dir);
for (var i=0, len=children.length; i<len; i++) {
var childFilename = children[i];
if (childFilename === 'node_modules') {
continue;
}
var tagDirname = nodePath.join(dir, childFilename);
var tagFile = nodePath.join(dir, childFilename, 'raptor-tag.json');
var tag = null;
var rendererFile = nodePath.join(dir, childFilename, 'renderer.js');
var templateFile = nodePath.join(dir, childFilename, 'template.rhtml');
var tagDef = null;
// Record dependencies so that we can check if a template is up-to-date
taglib.addInputFile(tagFile);
taglib.addInputFile(rendererFile);
if (fs.existsSync(tagFile)) {
// raptor-tag.json exists in the directory, use that as the tag definition
tagDef = JSON.parse(fs.readFileSync(tagFile, {encoding: 'utf8'}));
if (!tagDef.renderer || !tagDef.template) {
if (fs.existsSync(rendererFile)) {
tagDef.renderer = rendererFile;
} else if (fs.existsSync(templateFile)) {
tagDef.template = templateFile;
} else {
throw new Error('Invalid tag. Neither a renderer or a template was found for tag.');
}
}
tag = buildTag(tagDef, tagsConfigPath, taglib, tagDirname);
tag.name = childFilename;
taglib.addTag(tag);
} else {
// raptor-tag.json does *not* exist... checking for a 'renderer.js'
if (fs.existsSync(rendererFile)) {
var rendererCode = fs.readFileSync(rendererFile, {encoding: 'utf8'});
tagDef = tagDefFromCode.extractTagDef(rendererCode);
if (!tagDef) {
tagDef = createDefaultTagDef();
}
tagDef.renderer = rendererFile;
tag = buildTag(tagDef, tagsConfigPath, taglib, tagDirname);
tag.name = childFilename;
taglib.addTag(tag);
} else if (fs.existsSync(templateFile)) {
var templateCode = fs.readFileSync(templateFile, {encoding: 'utf8'});
tagDef = tagDefFromCode.extractTagDef(templateCode);
if (!tagDef) {
tagDef = createDefaultTagDef();
}
tagDef.template = templateFile;
}
if (tagDef) {
tag = buildTag(tagDef, tagsConfigPath, taglib, tagDirname);
tag.name = childFilename;
taglib.addTag(tag);
}
}
}
}
function load(path) {
if (cache[path]) {
return cache[path];
}
var src = fs.readFileSync(path, {encoding: 'utf8'});
var taglib = new Taglib(path);
taglib.addInputFile(path);
var dirname = nodePath.dirname(path);
var taglibObject;
try {
taglibObject = JSON.parse(src);
}
catch(e) {
throw new Error('Unable to parse taglib JSON at path "' + path + '". Exception: ' + e);
}
propertyHandlers(taglibObject, {
attributes: function(value) {
handleAttributes(value, taglib, path);
},
tags: function(tags) {
forEachEntry(tags, function(tagName, path) {
ok(path, 'Invalid tag definition for "' + tagName + '"');
var tagObject;
var tagDirname;
if (typeof path === 'string') {
path = nodePath.resolve(dirname, path);
taglib.addInputFile(path);
tagDirname = nodePath.dirname(path);
if (!fs.existsSync(path)) {
throw new Error('Tag at path "' + path + '" does not exist. Taglib: ' + taglib.id);
}
var tagJSON = fs.readFileSync(path, {encoding: 'utf8'});
try {
tagObject = JSON.parse(tagJSON);
}
catch(e) {
throw new Error('Unable to parse tag JSON for tag at path "' + path + '"');
}
} else {
tagDirname = dirname; // Tag is in the same taglib file
tagObject = path;
path = '<' + tagName + '> tag in ' + taglib.id;
}
var tag = buildTag(tagObject, path, taglib, tagDirname);
if (tag.name === undefined) {
tag.name = tagName;
}
taglib.addTag(tag);
});
},
tagsDir: function(dir) {
if (Array.isArray(dir)) {
for (var i = 0; i < dir.length; i++) {
scanTagsDir(path, dirname, dir[i], taglib);
}
} else {
scanTagsDir(path, dirname, dir, taglib);
}
},
textTransformer: function(value) {
var transformer = new Taglib.Transformer();
if (typeof value === 'string') {
value = {
path: value
};
}
propertyHandlers(value, {
path: function(value) {
var path = resolve(value, dirname);
if (!fs.existsSync(path)) {
throw new Error('Transformer at path "' + path + '" does not exist.');
}
transformer.path = path;
}
}, 'text-transformer in ' + path);
ok(transformer.path, '"path" is required for transformer');
taglib.addTextTransformer(transformer);
}
}, path);
taglib.id = path;
cache[path] = taglib;
return taglib;
}
exports.load = load;

View File

@ -0,0 +1,134 @@
var nodePath = require('path');
var fs = require('fs');
var taglibLoader = require('./taglib-loader');
var existsCache = {};
var TaglibLookup = require('./TaglibLookup');
exports.registeredTaglibs = [];
var lookupCache = {};
var discoverCache = {};
var excludedDirs = {};
function existsCached(path) {
var exists = existsCache[path];
if (exists === undefined) {
exists = fs.existsSync(path);
existsCache = exists;
}
return exists;
}
function tryDir(dirname, discovered) {
var taglibPath = nodePath.join(dirname, 'raptor-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
discovered.push(taglib);
}
}
function tryNodeModules(parent, discovered) {
if (nodePath.basename(parent) === 'node_modules') {
return;
}
var nodeModulesDir = nodePath.join(parent, 'node_modules');
if (existsCached(nodeModulesDir)) {
var children = fs.readdirSync(nodeModulesDir);
children.forEach(function(moduleDirBasename) {
var moduleDir = nodePath.join(nodeModulesDir, moduleDirBasename);
var taglibPath = nodePath.join(moduleDir, 'raptor-taglib.json');
if (existsCached(taglibPath)) {
var taglib = taglibLoader.load(taglibPath);
taglib.moduleName = moduleDirBasename;
discovered.push(taglib);
}
var stat;
try {
stat = fs.statSync(moduleDir);
} catch(e) {
return;
}
if (stat.isDirectory()) {
}
});
}
}
function discoverHelper(dirname, discovered) {
if (!excludedDirs[dirname]) {
tryDir(dirname, discovered);
tryNodeModules(dirname, discovered);
}
var parent = nodePath.dirname(dirname);
if (parent && parent !== dirname) {
discoverHelper(parent, discovered);
}
}
function discover(dirname) {
var discovered = discoverCache[dirname];
if (discovered) {
return discovered;
}
discovered = [];
discoverHelper(dirname, discovered);
discovered = discovered.concat(exports.registeredTaglibs);
discoverCache[dirname] = discovered;
return discovered;
}
function buildLookup(dirname) {
var taglibs = discover(dirname);
var lookupCacheKey = taglibs
.map(function(taglib) {
return taglib.id;
})
.join(',');
var lookup = lookupCache[lookupCacheKey];
if (lookup === undefined) {
lookup = new TaglibLookup();
for (var i=taglibs.length-1; i>=0; i--) {
lookup.addTaglib(taglibs[i]);
}
lookupCache[lookupCacheKey] = lookup;
}
return lookup;
}
function registerTaglib(taglib) {
if (typeof taglib === 'string') {
taglib = taglibLoader.load(taglib);
}
exports.registeredTaglibs.push(taglib);
}
function excludeDir(dirname) {
excludedDirs[dirname] = true;
}
exports.excludeDir = excludeDir;
exports.registerTaglib = registerTaglib;
exports.buildLookup = buildLookup;

39
dust/index.js Normal file
View File

@ -0,0 +1,39 @@
var raptorDust = require('raptor-dust');
exports.registerHelpers = function(dust) {
raptorDust.registerHelpers({
'async-fragment': {
buildInput: function(chunk, context, bodies, params, renderContext) {
var arg = params.arg = {};
for (var k in params) {
if (params.hasOwnProperty(k)) {
if (k.startsWith('arg-')) {
arg[k.substring(4)] = params[k];
delete params[k];
}
}
}
var dataProvider = params.dataProvider;
if (typeof dataProvider === 'string') {
var dataProviderFunc = context.get(dataProvider);
if (dataProviderFunc) {
params.dataProvider = dataProviderFunc;
}
}
params.invokeBody = function(asyncContext, data) {
var varName = params['var'];
var newContextObj = {};
newContextObj[varName] = data;
var newContext = context.push(newContextObj);
asyncContext.renderDustBody(bodies.block, newContext);
};
return params;
},
render: require('../taglibs/async/async-fragment-tag')
}
}, dust);
};

View File

@ -1,62 +1,60 @@
{
"name": "raptor-templates",
"description": "Raptor Templates",
"keywords": [
"templating",
"template",
"async",
"streaming"
],
"repository": {
"type": "git",
"url": "https://github.com/raptorjs3/raptor-templates.git"
},
"scripts": {
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test"
},
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
"maintainers": [
"Patrick Steele-Idem <pnidem@gmail.com>"
],
"dependencies": {
"raptor-detect": "^0.2.0-beta",
"raptor-logging": "^0.2.0-beta",
"raptor-strings": "^0.2.0-beta",
"raptor-regexp": "^0.2.0-beta",
"raptor-util": "^0.2.0-beta",
"raptor-arrays": "^0.2.0-beta",
"raptor-json": "^0.2.0-beta",
"raptor-modules": "^0.2.0-beta",
"raptor-render-context": "^0.2.0-beta",
"raptor-data-providers": "^0.2.0-beta",
"raptor-xml": "^0.2.0-beta",
"raptor-objects": "^0.2.0-beta",
"raptor-ecma": "^0.2.0-beta",
"htmlparser2": "~3.5.1",
"char-props": "~0.1.5",
"raptor-promises": "^0.2.0-beta",
"raptor-args": "^0.1.9-beta",
"minimatch": "^0.2.14",
"property-handlers": "^0.2.1-beta",
"raptor-taglibs": "^0.2.0-beta",
"raptor-taglib-async": "^0.2.0-beta",
"raptor-taglib-caching": "^0.2.0-beta",
"raptor-taglib-core": "^0.2.0-beta",
"raptor-taglib-html": "^0.2.0-beta",
"raptor-taglib-layout": "^0.2.0-beta"
},
"devDependencies": {
"mocha": "~1.15.1",
"chai": "~1.8.1"
},
"license": "Apache License v2.0",
"bin": {
"rhtmlc": "bin/rhtmlc"
},
"main": "runtime/lib/raptor-templates.js",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"ebay": {},
"version": "0.2.32-beta"
}
"name": "raptor-templates",
"description": "Raptor Templates",
"keywords": [
"templating",
"template",
"async",
"streaming"
],
"repository": {
"type": "git",
"url": "https://github.com/raptorjs3/raptor-templates.git"
},
"scripts": {
"test": "node_modules/.bin/mocha --ui bdd --reporter spec ./test && node_modules/.bin/jshint compiler/ runtime/ taglibs/ dust/"
},
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
"maintainers": [
"Patrick Steele-Idem <pnidem@gmail.com>"
],
"dependencies": {
"raptor-detect": "^0.2.0-beta",
"raptor-logging": "^0.2.0-beta",
"raptor-strings": "^0.2.0-beta",
"raptor-regexp": "^0.2.0-beta",
"raptor-util": "^0.2.0-beta",
"raptor-arrays": "^0.2.0-beta",
"raptor-json": "^0.2.0-beta",
"raptor-modules": "^0.2.0-beta",
"raptor-render-context": "^0.2.0-beta",
"raptor-data-providers": "^0.2.0-beta",
"raptor-xml": "^0.2.0-beta",
"raptor-objects": "^0.2.0-beta",
"raptor-ecma": "^0.2.0-beta",
"htmlparser2": "~3.5.1",
"char-props": "~0.1.5",
"raptor-promises": "^0.2.0-beta",
"raptor-args": "^0.1.9-beta",
"minimatch": "^0.2.14",
"property-handlers": "^0.2.1-beta",
"raptor-dust": "^0.3.6-beta"
},
"devDependencies": {
"mocha": "~1.15.1",
"chai": "~1.8.1",
"jshint": "^2.5.0",
"raptor-cache": "^0.2.7-beta",
"dustjs-linkedin": "^2.3.4"
},
"license": "Apache License v2.0",
"bin": {
"rhtmlc": "bin/rhtmlc"
},
"main": "runtime/raptor-templates-runtime.js",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"ebay": {},
"version": "0.2.32-beta"
}

View File

@ -1,6 +1,6 @@
var escapeXml = require('raptor-util/escapeXml');
var escapeXmlAttr = escapeXml.attr;
var runtime = require('./raptor-templates'); // Circular dependnecy, but that is okay
var runtime = require('./'); // Circular dependnecy, but that is okay
var extend = require('raptor-util/extend');
var attr = require('raptor-util/attr');
var attrs = require('raptor-util/attrs');

View File

@ -1,99 +0,0 @@
exports.workDir = null;
var logger = require('raptor-logging').logger(module);
var taglibsWorkFile = null;
var taglibsLastModified = null;
function getWorkDir() {
return exports.workDir;
}
function _readTaglibsLastModified(workDir) {
var taglibsWorkFile = 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;
}
function recordLoadedTaglib(taglibResource) {
var workDir = getWorkDir();
if (workDir) {
var taglibsLastModified = _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;
taglibsWorkFile.writeAsString(newStr);
}
}
}
function _validateWorkDir(workDir) {
if (!workDir) {
return;
}
logger.debug('Validating work directory at path "' + workDir + '"...');
var taglibsLastModified = _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://')) {
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();
}
}
function setWorkDir(workDir) {
exports.workDir = _workDir;
if (workDir) {
logger.debug('Setting work directory to "' + workDir + '"...');
var File = require('raptor-files/File');
if (typeof workDir === 'string') {
workDir = new File(workDir);
}
taglibsWorkFile = new File(workDir, 'taglibs.txt');
_validateWorkDir(workDir);
} else {
workDir = null;
taglibsWorkFile = null;
}
}
exports.recordLoadedTaglib = recordLoadedTaglib;
exports.setWorkDir = setWorkDir;

View File

@ -1,7 +1,7 @@
var nodePath = require('path');
var fs = require('fs');
var Module = require('module').Module;
var raptorTemplatesCompiler = require('../../compiler');
var raptorTemplatesCompiler = require('../compiler');
var cwd = process.cwd();
function loadSource(templatePath, compiledSrc) {

View File

@ -1,6 +1,6 @@
{
"main": "lib/raptor-templates.js",
"main": "./raptor-templates-runtime.js",
"browser": {
"./lib/loader.js": "./lib/loader_browser.js"
"./loader.js": "./loader_browser.js"
}
}

View File

@ -0,0 +1,40 @@
'use strict';
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
module.exports = function transform(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 (name, value) {
if (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) {
var extendFuncName = template.getStaticHelperFunction('extend', 'xt');
argString = extendFuncName + '(' + arg + ', ' + argString + ')';
}
if (argString) {
node.setProperty('arg', template.makeExpression(argString));
}
};

View File

@ -0,0 +1,45 @@
'use strict';
var raptorDataProviders = require('raptor-data-providers');
module.exports = function render(input, context) {
var dataProvider = input.dataProvider;
var dataProviders = raptorDataProviders.forContext(context, false /* don't create if missing */);
var arg = input.arg || {};
arg.context = context;
var asyncContext = context.beginAsync(input.timeout);
function onError(e) {
asyncContext.error(e || 'Async fragment failed');
}
function renderBody(data) {
try {
if (input.invokeBody) {
input.invokeBody(asyncContext, data);
}
asyncContext.end();
} catch (e) {
onError(e);
}
}
var method = input.method;
if (method) {
dataProvider = dataProvider[method].bind(dataProvider);
}
try {
dataProviders.requestData(dataProvider, arg, function(err, data) {
if (err) {
return onError(err);
}
renderBody(data);
});
} catch (e) {
onError(e);
}
};

View File

@ -0,0 +1,8 @@
{
"dependencies": [
{
"package": "raptor/render-context/async"
},
"AsyncFragmentTag.js"
]
}

View File

@ -0,0 +1,37 @@
{
"tags": {
"async-fragment": {
"renderer": "./async-fragment-tag",
"attributes": {
"data-provider": {
"type": "string"
},
"arg": {
"type": "expression",
"preserve-name": true
},
"arg-*": {
"pattern": true,
"type": "string",
"preserve-name": true
},
"var": {
"type": "identifier"
},
"timeout": {
"type": "integer"
},
"method": {
"type": "string"
}
},
"vars": [
"context",
{
"name-from-attribute": "var OR dependency OR data-provider|keep"
}
],
"transformer": "./async-fragment-tag-transformer"
}
}
}

View File

@ -0,0 +1,49 @@
'use strict';
var promiseUtil = require('raptor-promises/util');
module.exports = {
render: function (input, context) {
var attributes = context.attributes;
var cacheProvider = attributes.cacheProvider;
var cache;
var cacheKey = input.cacheKey;
if (!cacheKey) {
throw new Error('cache-key is required for <cached-fragment>');
}
if (!cacheProvider) {
var raptorCacheModulePath;
try {
raptorCacheModulePath = require.resolve('raptor-cache');
}
catch(e) {
throw new Error('The "raptor-cache" module should be installed as an application-level dependency when using caching tags');
}
cacheProvider = require(raptorCacheModulePath).getDefaultProvider();
}
cache = cacheProvider.getCache(input.cacheName);
var cachePromise = cache.get(
cacheKey,
{
builder: function() {
var result = context.captureString(function () {
if (input.invokeBody) {
input.invokeBody();
}
});
return result;
}
});
var asyncContext = context.beginAsync();
promiseUtil.immediateThen(
cachePromise,
function (result) {
asyncContext.end(result);
},
function (e) {
asyncContext.error(e);
});
}
};

View File

@ -0,0 +1,15 @@
{
"tags": {
"cached-fragment": {
"renderer": "./cached-fragment-tag",
"attributes": {
"cache-key": {
"type": "string"
},
"cache-name": {
"type": "string"
}
}
}
}
}

View 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.
*/
'use strict';
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
function AssignNode(props) {
AssignNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
AssignNode.prototype = {
doGenerateCode: function (template) {
var varName = this.getProperty('var');
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 + ';');
}
}
};
module.exports = AssignNode;

View 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.
*/
'use strict';
var WhenNode = require('./WhenNode');
var OtherwiseNode = require('./OtherwiseNode');
function ChooseNode(props) {
ChooseNode.$super.call(this);
}
ChooseNode.prototype = {
doGenerateCode: function (template) {
var otherwiseNode = null;
var foundWhenNode = false;
var allowedNodes = [];
this.forEachChild(function (child) {
if (child.isTextNode()) {
var trimmed = child.getText().trim();
if (trimmed !== '') {
this.addError('Static text "' + trimmed + '" 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.');
}
}
};
module.exports = ChooseNode;

63
taglibs/core/DefNode.js Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var funcDefRegExp = /^([A-Za-z_][A-Za-z0-9_]*)(?:\(((?:[A-Za-z_][A-Za-z0-9_]*,\s*)*[A-Za-z_][A-Za-z0-9_]*)?\))?$/;
function DefNode(props) {
DefNode.$super.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 = func.trim();
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;
}
if (func.indexOf('(') === -1) {
func += '()';
}
template.statement('function ' + func + ' {').indent(function () {
template.line('return __helpers.c(context, function() {').indent(function () {
this.generateCodeForChildren(template);
}, this).line('});');
}, this).line('}');
}
};
module.exports = DefNode;

View File

@ -0,0 +1,38 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function ElseIfNode(props) {
ElseIfNode.$super.call(this, 'c-else-if');
if (props) {
this.setProperties(props);
}
}
ElseIfNode.nodeType = 'element';
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('}');
}
};
module.exports = ElseIfNode;

37
taglibs/core/ElseNode.js Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function ElseNode(props) {
ElseNode.$super.call(this, 'c-else');
if (props) {
this.setProperties(props);
}
}
ElseNode.nodeType = 'element';
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('}');
}
};
module.exports = ElseNode;

142
taglibs/core/ForNode.js Normal file
View File

@ -0,0 +1,142 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var forEachRegEx = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(.+)$/;
var forEachPropRegEx = /^\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*,\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s+in\s+(.+)$/;
var stringify = require('raptor-json/stringify').stringify;
function parseForEach(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]
};
}
}
function ForNode(props) {
ForNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
ForNode.prototype = {
doGenerateCode: function (template) {
var each = this.getProperty('each');
var separator = this.getProperty('separator');
var statusVar = this.getProperty('statusVar') || this.getProperty('varStatus');
var customIterator = this.getProperty('iterator');
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']);
var varName = parts['var'];
var nameVarName = parts.nameVar;
var 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 (customIterator) {
var statusVarFlag = '';
if (statusVar) {
statusVarFlag = ', true';
forEachParams = [
varName,
statusVar
];
} else {
forEachParams = [varName];
}
template.statement(customIterator + '(' + items + ', function(' + forEachParams.join(',') + ') {').indent(function () {
this.generateCodeForChildren(template);
}, this).line('}' + statusVarFlag + ');');
} else 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).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('});');
}
}
}
};
module.exports = ForNode;

38
taglibs/core/IfNode.js Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function IfNode(props) {
IfNode.$super.call(this, 'c-if');
if (props) {
this.setProperties(props);
}
}
IfNode.nodeType = 'element';
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('}');
}
};
module.exports = IfNode;

View File

@ -0,0 +1,83 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var stringify = require('raptor-json/stringify');
var nodePath = require('path');
var fs = require('fs');
var extend = require('raptor-util').extend;
function IncludeNode(props) {
IncludeNode.$super.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 templatePath = this.getProperty('template');
var templateData = this.getProperty('templateData') || this.getProperty('template-data');
var resourcePath;
var _this = this;
if (templatePath) {
this.removeProperty('template');
var dataExpression;
if (templateData) {
dataExpression = templateData;
} else {
dataExpression = {
toString: function () {
var propParts = [];
_this.forEachProperty(function (name, value) {
name = name.replace(/-([a-z])/g, function (match, lower) {
return lower.toUpperCase();
});
propParts.push(stringify(name) + ': ' + value);
}, _this);
if (_this.hasChildren()) {
propParts.push(stringify('invokeBody') + ': ' + _this.getBodyContentFunctionExpression(template, false));
}
return '{' + propParts.join(', ') + '}';
}
};
}
template.include(templatePath, dataExpression);
} else if ((resourcePath = this.getAttribute('resource'))) {
var isStatic = this.getProperty('static') !== false;
if (isStatic) {
resourcePath = nodePath.resolve(template.dirname, resourcePath);
if (!fs.existsSync(resourcePath)) {
this.addError('Resource not found: ' + resourcePath);
return;
}
template.write(stringify(fs.readFileSync(resourcePath, {encoding: 'utf8'})));
}
} else {
this.addError('"template" or "resource" attribute is required');
}
}
};
module.exports = IncludeNode;

100
taglibs/core/InvokeNode.js Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var forEach = require('raptor-util').forEach;
function InvokeNode(props) {
InvokeNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
InvokeNode.prototype = {
doGenerateCode: function (template) {
var func = this.getProperty('function');
var funcDef;
var bodyParam;
var definedFunctions = template.getAttribute('core:definedFunctions');
if (!func) {
this.addError('"function" attribute is required');
return;
}
if (func.indexOf('(') === -1) {
funcDef = definedFunctions ? definedFunctions[func] : null;
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.forEachProperty(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');
}
}
}
};
module.exports = InvokeNode;

View File

@ -0,0 +1,34 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function OtherwiseNode(props) {
OtherwiseNode.$super.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>';
}
};
module.exports = OtherwiseNode;

View 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.
*/
'use strict';
function RequireNode(props) {
RequireNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
RequireNode.prototype = {
javaScriptOnly: true,
doGenerateCode: function (template) {
var module = this.getProperty('module');
var varName = this.getProperty('var');
if (!module) {
this.addError('"module" attribute is required');
}
if (!varName) {
this.addError('"varName" attribute is required');
}
if (module && varName) {
template.addStaticVar(varName, 'require(' + module + ')');
}
}
};
module.exports = RequireNode;

View File

@ -0,0 +1,32 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function ScriptletNode(code) {
ScriptletNode.$super.call(this, 'scriptlet');
this.code = code;
}
ScriptletNode.prototype = {
doGenerateCode: function (template) {
if (this.code) {
template.code(this.code);
}
},
toString: function () {
return '{%' + this.code + '%}';
}
};
module.exports = ScriptletNode;

View File

@ -0,0 +1,178 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var extend = require('raptor-util').extend;
var forEachEntry = require('raptor-util').forEachEntry;
var stringify = require('raptor-json/stringify');
var raptorModulesResolver = require('raptor-modules/resolver');
function addHandlerVar(template, renderer) {
var handlerVars = template._handlerVars || (template._handlerVars = {});
var handlerVar = handlerVars[renderer];
if (!handlerVar) {
handlerVars[renderer] = handlerVar = renderer.replace(/[.\-\/]/g, '_').replace(/^[_]+/g, '');
template.addStaticVar(handlerVar, 'require(' + stringify(renderer) + ')');
}
return handlerVar;
}
function getPropsStr(props, template) {
var propsArray = [];
if (props) {
template.indent(function () {
forEachEntry(props, function (name, value) {
if (template.isExpression(value)) {
var expressionStr;
template.indent(function () {
expressionStr = value.expression.toString();
});
propsArray.push(template.indentStr() + stringify(name) + ': ' + expressionStr);
} else if (typeof value === 'string' || typeof value === 'object') {
propsArray.push(template.indentStr() + stringify(name) + ': ' + stringify(value));
} else {
propsArray.push(template.indentStr() + stringify(name) + ': ' + value);
}
});
});
if (propsArray.length) {
return '{\n' + propsArray.join(',\n') + '\n' + template.indentStr() + '}';
} else {
return '{}';
}
} else {
return '{}';
}
}
function TagHandlerNode(tag) {
if (!this.nodeType) {
TagHandlerNode.$super.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;
},
setDynamicAttributesProperty: function(name) {
this.dynamicAttributesProperty = name;
},
addPreInvokeCode: function (code) {
this.preInvokeCode.push(code);
},
addPostInvokeCode: function (code) {
this.postInvokeCode.push(code);
},
setInputExpression: function (expression) {
this.inputExpression = expression;
},
doGenerateCode: function (template) {
var rendererPath = raptorModulesResolver.deresolve(this.tag.renderer, template.dirname);
var handlerVar = addHandlerVar(template, rendererPath);
this.tag.forEachImportedVariable(function (importedVariable) {
this.setProperty(importedVariable.targetProperty, template.makeExpression(importedVariable.expression));
}, this);
var _this = this;
var variableNames = [];
_this.tag.forEachVariable(function (nestedVar) {
var varName;
if (nestedVar.nameFromAttribute) {
var possibleNameAttributes = nestedVar.nameFromAttribute.split(/\s+or\s+|\s*,\s*/i);
for (var i = 0, len = possibleNameAttributes.length; i < len; i++) {
var attrName = possibleNameAttributes[i];
var keep = false;
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 = nestedVar.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.contextHelperMethodCall('t', function () {
template.code('\n').indent(function () {
template.line(handlerVar + ',').indent();
if (_this.inputExpression) {
template.code(_this.inputExpression);
} else {
if (_this.dynamicAttributes) {
template.indent(function() {
_this.setProperty(_this.dynamicAttributesProperty, template.makeExpression(getPropsStr(_this.dynamicAttributes, template)));
});
}
template.code(getPropsStr(_this.getProperties(), template));
}
if (_this.hasChildren()) {
var bodyParams = [];
variableNames.forEach(function (varName) {
bodyParams.push(varName);
});
template.code(',\n').line('function(' + bodyParams.join(',') + ') {').indent(function () {
_this.generateCodeForChildren(template);
}).indent().code('}');
}
});
});
if (_this.postInvokeCode.length) {
_this.postInvokeCode.forEach(function (code) {
template.indent().code(code).code('\n');
});
}
}
};
module.exports = TagHandlerNode;

View File

@ -0,0 +1,45 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function TemplateNode(props) {
TemplateNode.$super.call(this, 'c-template');
if (props) {
this.setProperties(props);
}
}
TemplateNode.nodeType = 'element';
TemplateNode.prototype = {
doGenerateCode: function (template) {
var params = this.getProperty('params');
if (params) {
params = params.split(/(?:\s*,\s*)|(?:\s+)/g);
params.forEach(function (param) {
param = param.trim();
if (param.length) {
template.addVar(param, 'data.' + param);
}
}, this);
} else {
params = null;
}
this.generateCodeForChildren(template);
}
};
module.exports = TemplateNode;

47
taglibs/core/VarNode.js Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
function VarNode(props) {
VarNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
VarNode.prototype = {
javaScriptOnly: true,
doGenerateCode: function (template) {
var varName = this.getProperty('name');
var 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;
}
var isStatic = this.getProperty('static');
if (varName) {
if (isStatic) {
template.addStaticVar(varName, value);
} else {
template.statement('var ' + varName + (value ? '=' + value : '') + ';');
}
}
}
};
module.exports = VarNode;

41
taglibs/core/WhenNode.js Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function WhenNode(props) {
WhenNode.$super.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('}');
}
};
module.exports = WhenNode;

51
taglibs/core/WithNode.js Normal file
View 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.
*/
'use strict';
var varNameRegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
function WithNode(props) {
WithNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
WithNode.prototype = {
doGenerateCode: function (template) {
var vars = this.getProperty('vars');
var _this = this;
if (!vars) {
this.addError('"vars" attribute is required');
}
var withVars = template.parseAttribute(vars, { '*': { type: 'expression' } }, {
ordered: true,
errorHandler: function (message) {
_this.addError('Invalid variable declarations of "' + vars + '". Error: ' + message);
}
});
var varDefs = [];
withVars.forEach(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('}());');
}
};
module.exports = WithNode;

55
taglibs/core/WriteNode.js Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function WriteNode(props) {
WriteNode.$super.call(this, 'c-write');
if (props) {
this.setProperties(props);
}
}
WriteNode.nodeType = 'element';
WriteNode.prototype = {
doGenerateCode: function (template) {
var expression = this.getExpression();
var escapeXml;
if (this.hasProperty('escapeXml')) {
escapeXml = this.getProperty('escapeXml') !== false;
} else {
escapeXml = this.getProperty('escape-xml') !== false;
}
if (escapeXml === true) {
if (this.getEscapeXmlContext() === '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() + '">';
}
};
module.exports = WriteNode;

View File

@ -0,0 +1,340 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function removeDashes(str) {
return str.replace(/-([a-z])/g, function (match, lower) {
return lower.toUpperCase();
});
}
var extend = require('raptor-util').extend;
var WriteNode = require('./WriteNode');
var ForNode = require('./ForNode');
var IfNode = require('./IfNode');
var ElseIfNode = require('./ElseIfNode');
var ElseNode = require('./ElseNode');
var WithNode = require('./WithNode');
var WhenNode = require('./WhenNode');
var OtherwiseNode = require('./OtherwiseNode');
var TagHandlerNode = require('./TagHandlerNode');
var IncludeNode = require('./IncludeNode');
var resolver = require('raptor-modules/resolver');
function handleAttr(node, compiler, template) {
var parentNode = node.parentNode;
if (!parentNode.isElementNode()) {
node.addError(node.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('namespace') || '';
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('namespace');
node.removeAttribute('prefix');
if (node.hasAttributesAnyNS()) {
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('ATTRIBUTE');
//Escape body text and expressions as attributes
parentNode.setAttributeNS(attrUri, attrName, node.getBodyContentExpression(template), attrPrefix, false);
}
}
function findNestedAttrs(node, compiler, template) {
node.forEachChild(function (child) {
if (child.qName === 'c-attr') {
handleAttr(child, compiler, template);
}
});
}
module.exports = function transform(node, compiler, template) {
//Find and handle nested <c-attrs> elements
findNestedAttrs(node, compiler, template);
var inputAttr;
var forEachNode;
var tag;
function forEachProp(callback, thisObj) {
var foundProps = {};
node.forEachAttributeAnyNS(function (attr) {
var attrDef = compiler.taglibs.getAttribute(node, attr);
var type = attrDef ? attrDef.type || 'string' : 'string';
var value;
if (!attrDef) {
// var isAttrForTaglib = compiler.taglibs.isTaglib(attrUri);
//Tag doesn't allow dynamic attributes
node.addError('The tag "' + tag.name + '" in taglib "' + tag.taglibId + '" does not support attribute "' + attr + '"');
return;
}
if (compiler.isExpression(attr.value)) {
value = attr.value;
} else {
try {
value = compiler.convertType(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.dynamicAttribute) {
propName = attr.qName;
} else {
if (attrDef.targetProperty) {
propName = attrDef.targetProperty;
} else if (attrDef.preserveName) {
propName = attr.localName;
} else {
propName = removeDashes(attr.localName);
}
}
foundProps[propName] = true;
callback.call(thisObj, propName, value, attrDef);
});
tag.forEachAttribute(function (attr) {
if (attr.hasOwnProperty('defaultValue') && !foundProps[attr.name]) {
callback.call(thisObj, attr.name, template.makeExpression(JSON.stringify(attr.defaultValue)), '', attr);
}
});
}
tag = node.tag || compiler.taglibs.getTag(node);
var coreAttrHandlers = {
'c-space': function(attr) {
this['c-whitespace'](attr);
},
'c-whitespace': function(attr) {
if (attr.value === 'preserve') {
node.setPreserveWhitespace(true);
}
},
'c-escape-xml': function(attr) {
node.setEscapeXmlBodyText(attr.value !== 'false');
},
'c-parse-body-text': function(attr) {
node.parseBodyText = attr.value !== 'false';
},
'c-when': function(attr) {
var whenNode = compiler.createNode(WhenNode, {
test: template.makeExpression(attr.value),
pos: node.getPosition()
});
node.parentNode.replaceChild(whenNode, node);
whenNode.appendChild(node);
},
'c-otherwise': function(attr) {
var otherwiseNode = compiler.createNode(OtherwiseNode, { pos: node.getPosition() });
node.parentNode.replaceChild(otherwiseNode, node);
otherwiseNode.appendChild(node);
},
'c-attrs': function(attr) {
node.dynamicAttributesExpression = attr.value;
},
'c-for-each': function(attr) {
this['for'](attr);
},
'c-for': function(attr) {
var forEachProps = compiler.parseAttribute(attr.value, {
each: { type: 'custom' },
separator: { type: 'expression' },
'iterator': { 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 "' + attr.value + '". Error: ' + message);
}
});
forEachProps.pos = node.getPosition();
//Copy the position property
forEachNode = compiler.createNode(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);
},
'c-if': function(attr) {
var ifNode = compiler.createNode(IfNode, {
test: template.makeExpression(attr.value),
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);
},
'c-else-if': function(attr) {
var elseIfNode = compiler.createNode(ElseIfNode, {
test: template.makeExpression(attr.value),
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);
},
'c-else': function(attr) {
var elseNode = compiler.createNode(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);
},
'c-with': function(attr) {
var withNode = compiler.createNode(WithNode, {
vars: attr.value,
pos: node.getPosition()
});
node.parentNode.replaceChild(withNode, node);
withNode.appendChild(node);
},
'c-body-content': function(attr) {
this.content(attr);
},
'c-content': function(attr) {
var newChild = compiler.createNode(WriteNode, {
expression: attr.value,
pos: node.getPosition()
});
node.removeChildren();
node.appendChild(newChild);
},
'c-trim-body-indent': function(attr) {
if (attr.value === 'true') {
node.trimBodyIndent = true;
}
},
'c-strip': function(attr) {
if (!node.setStripExpression) {
node.addError('The c-strip directive is not allowed for target node');
}
node.setStripExpression(attr.value);
},
'c-replace': function(attr) {
var replaceWriteNode = compiler.createNode(WriteNode, {
expression: attr.value,
pos: node.getPosition()
});
//Replace the existing node with an node that only has children
node.parentNode.replaceChild(replaceWriteNode, node);
node = replaceWriteNode;
},
'c-input': function(attr) {
inputAttr = attr.value;
}
};
node.forEachAttributeNS('', function(attr) {
var handler = coreAttrHandlers[attr.qName];
if (handler) {
node.removeAttribute(attr.localName);
coreAttrHandlers[attr.localName](attr);
}
});
if (tag) {
if (tag.preserveWhitespace) {
node.setPreserveWhitespace(true);
}
if (tag.renderer || tag.template) {
if (tag.renderer) {
//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.setInputExpression(template.makeExpression(inputAttr));
}
} else {
var templatePath = resolver.deresolve(tag.template, compiler.dirname);
// 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, templatePath);
}
forEachProp(function (name, value, attrDef) {
if (attrDef.dynamicAttribute && attrDef.targetProperty) {
if (attrDef.removeDashes === true) {
name = removeDashes(name);
}
node.addDynamicAttribute(name, value);
node.setDynamicAttributesProperty(attrDef.targetProperty);
} else {
node.setProperty(name, value);
}
});
} else if (tag.nodeClass) {
var NodeCompilerClass = require(tag.nodeClass);
compiler.inheritNode(NodeCompilerClass);
extend(node, NodeCompilerClass.prototype);
NodeCompilerClass.call(node);
node.setNodeClass(NodeCompilerClass);
forEachProp(function (name, value) {
node.setProperty(name, value);
});
}
}
};

View File

@ -0,0 +1,81 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var WriteNode = require('./WriteNode');
var ScriptletNode = require('./ScriptletNode');
module.exports = function transform(node, compiler) {
if (node.parentNode && node.parentNode.parseBodyText === false) {
return; //Don't try to parse expressions
}
var parts = [];
compiler.parseExpression(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);
}
});
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 = compiler.createTextNode(part.text, part.escapeXml !== false);
newNode.setTransformerApplied(this); //We shouldn't reprocess the new text node
} else if (part.hasOwnProperty('expression')) {
newNode = compiler.createNode(WriteNode, {
expression: part.expression,
escapeXml: part.escapeXml !== false
});
} else if (part.hasOwnProperty('scriptlet')) {
newNode = compiler.createNode(ScriptletNode, part.scriptlet);
}
if (newNode) {
newNode.setPosition(node.getPosition());
newNodes.push(newNode);
}
}
if (newNodes.length) {
node.parentNode.insertAfter(newNodes, node);
}
}
};

View 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.
*/
'use strict';
module.exports = function transform(node, compiler) {
var curNode = node.previousSibling;
var matchingNode;
var IfNode = compiler.getNodeClass('c-if');
var ElseIfNode = compiler.getNodeClass('c-else-if');
var whitespaceNodes = [];
while (curNode) {
if (curNode.getNodeClass() === ElseIfNode || curNode.getNodeClass() === IfNode) {
matchingNode = curNode;
break;
} else if (curNode.isTextNode()) {
var trimmed = curNode.getText().trim();
if (trimmed !== '') {
node.addError('Static text "' + trimmed + '" 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;
};

View File

@ -0,0 +1,289 @@
{
"tags": {
"c-template": {
"attributes": {
"name": {
"allow-expressions": false,
"type": "string"
},
"params": {
"allow-expressions": false,
"type": "string"
}
},
"node-class": "./TemplateNode"
},
"*": {
"attributes": {
"c-space": {
"type": "custom",
"allow-expressions": false
},
"c-whitespace": {
"type": "custom",
"allow-expressions": false
},
"c-for": {
"allow-expressions": false,
"type": "string"
},
"c-for-each": {
"allow-expressions": false,
"type": "string"
},
"c-if": {
"type": "expression"
},
"c-else": {
"type": "empty"
},
"c-else-if": {
"type": "expression"
},
"c-attrs": {
"type": "expression"
},
"c-when": {
"type": "expression"
},
"c-with": {
"type": "custom"
},
"c-otherwise": {
"type": "empty"
},
"c-parse-body-text": {
"type": "boolean",
"allow-expressions": false
},
"c-trim-body-indent": {
"type": "boolean",
"allow-expressions": false
},
"c-strip": {
"type": "boolean",
"allow-expressions": false
},
"c-content": {
"type": "expression"
},
"c-replace": {
"type": "expression"
},
"c-input": {
"type": "expression"
}
},
"transformer": {
"path": "./core-tag-transformer",
"priority": 0
}
},
"c-for": {
"node-class": "./ForNode",
"attributes": {
"each": {
"required": false,
"allow-expressions": false,
"type": "string"
},
"separator": {
"type": "string"
},
"status-var": {
"type": "identifier",
"allow-expressions": false
},
"for-loop": {
"type": "boolean",
"allow-expressions": false
},
"iterator": {
"type": "expression"
}
}
},
"c-write": {
"node-class": "./WriteNode",
"attributes": {
"value": {
"required": true,
"type": "expression"
},
"escape-xml": {
"type": "boolean",
"allow-expressions": false
}
}
},
"c-if": {
"node-class": "./IfNode",
"attributes": {
"test": {
"type": "expression"
}
}
},
"c-else": {
"node-class": "./ElseNode",
"transformer": {
"path": "./else-tag-transformer",
"properties": {
"type": "else"
}
}
},
"c-else-if": {
"attributes": {
"test": {
"type": "expression"
}
},
"node-class": "./ElseIfNode",
"transformer": {
"path": "./else-tag-transformer",
"properties": {
"type": "else-if"
}
}
},
"c-invoke": {
"node-class": "./InvokeNode",
"attributes": {
"function": {
"type": "custom",
"allow-expressions": false,
"required": true
},
"*": {
"type": "string",
"allow-expressions": true
}
}
},
"c-choose": {
"node-class": "./ChooseNode"
},
"c-when": {
"node-class": "./WhenNode",
"attributes": {
"test": {
"type": "expression"
}
}
},
"c-otherwise": {
"node-class": "./OtherwiseNode"
},
"c-def": {
"node-class": "./DefNode",
"attributes": {
"function": {
"type": "custom",
"allow-expressions": false
},
"body-param": {
"type": "custom",
"allow-expressions": false
}
}
},
"c-with": {
"node-class": "./WithNode",
"attributes": {
"vars": {
"type": "custom",
"allow-expressions": false
}
}
},
"c-include": {
"node-class": "./IncludeNode",
"attributes": {
"template": {
"type": "string"
},
"template-data": {
"type": "expression"
},
"resource": {
"type": "string"
},
"static": {
"type": "boolean",
"allow-expressions": false
},
"*": {
"type": "string"
}
}
},
"c-attr": {
"attributes": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"namespace": {
"type": "string"
},
"prefix": {
"type": "string"
}
}
},
"c-var": {
"node-class": "./VarNode",
"attributes": {
"name": {
"type": "custom",
"allow-expressions": false
},
"value": {
"type": "expression"
},
"static": {
"type": "boolean"
},
"string-value": {
"type": "string"
},
"boolean-value": {
"type": "boolean"
},
"number-value": {
"type": "number"
}
}
},
"c-require": {
"node-class": "./RequireNode",
"attributes": {
"module": {
"type": "string"
},
"var": {
"type": "custom",
"allow-expressions": false
}
}
},
"c-assign": {
"node-class": "./AssignNode",
"attributes": {
"var": {
"type": "custom",
"allow-expressions": false
},
"value": {
"type": "expression"
}
}
}
},
"text-transformer": {
"path": "./core-text-transformer"
}
}

View File

@ -0,0 +1,8 @@
'use strict';
module.exports = function render(input, context) {
context.write('<!--');
if (input.invokeBody) {
input.invokeBody();
}
context.write('-->');
};

View File

@ -0,0 +1,45 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function DocTypeNode(props) {
DocTypeNode.$super.call(this);
if (props) {
this.setProperties(props);
}
}
DocTypeNode.nodeType = 'element';
DocTypeNode.prototype = {
doGenerateCode: function (template) {
var doctype = this.getAttribute('value') || this.getProperty('value');
template.text('<!DOCTYPE ');
template.parseExpression(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('>');
}
};
module.exports = DocTypeNode;

View File

@ -0,0 +1,49 @@
/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var DocTypeNode = require('./DocTypeNode');
module.exports = function transform(node, compiler) {
if (node.isElementNode()) {
var options = compiler.options || {};
var preserveWhitespace = options.preserveWhitespace || {};
var allowSelfClosing = options.allowSelfClosing || {};
var startTagOnly = options.startTagOnly || {};
var lookupKey = node.namespace ? node.namespace + ':' + node.localName : node.localName;
if (node.isPreserveWhitespace() == null) {
if (preserveWhitespace[lookupKey] === true) {
node.setPreserveWhitespace(true);
}
}
if (allowSelfClosing[lookupKey] === true) {
node.setAllowSelfClosing(true);
}
if (compiler.options.xhtml !== true && startTagOnly[lookupKey] === true) {
node.setStartTagOnly(true);
}
if (node.getQName() === 'html' && node.hasAttribute('html-doctype')) {
var doctype = node.getAttribute('html-doctype');
var docTypeNode = compiler.createNode(DocTypeNode, {
value: doctype,
pos: node.getPosition()
});
node.parentNode.insertBefore(docTypeNode, node);
node.removeAttribute('html-doctype');
}
}
};

View File

@ -0,0 +1,25 @@
{
"tags": {
"html-html": {
"attributes": {
"doctype": {
"type": "string"
}
}
},
"html-doctype": {
"attributes": {
"value": {
"type": "custom"
}
},
"node-class": "./DocTypeNode"
},
"*": {
"transformer": "./html-tag-transformer"
},
"html-comment": {
"renderer": "./CommentTag"
}
}
}

View File

@ -0,0 +1,14 @@
module.exports = function render(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();
}
}
};

View File

@ -0,0 +1,3 @@
module.exports = function render(input, context) {
input._layout.handlePutTag(input);
};

View File

@ -0,0 +1,42 @@
{
"tags": {
"layout-use": {
"renderer": "./use-tag",
"attributes": {
"template": {
"type": "template"
},
"*": {
"remove-dashes": true,
"type": "string"
}
},
"var": "_layout"
},
"layout-put": {
"renderer": "./put-tag",
"attributes": {
"into": {
"type": "string"
},
"value": {
"type": "string"
}
},
"import-var": {
"_layout": "_layout"
}
},
"layout-placeholder": {
"renderer": "./placeholder-tag",
"attributes": {
"name": {
"type": "string"
}
},
"import-var": {
"content": "data.layoutContent"
}
}
}
}

13
taglibs/layout/use-tag.js Normal file
View File

@ -0,0 +1,13 @@
var extend = require('raptor-util').extend;
module.exports = function render(input, context) {
var content = {};
input.invokeBody({
handlePutTag: function (putTag) {
content[putTag.into] = putTag;
}
});
var viewModel = extend(input['*'] || {}, { layoutContent: content });
input.template(viewModel, context);
};

View File

@ -12,19 +12,7 @@
"after",
"waits",
"waitsFor",
"runs",
"raptor",
"$rset",
"$radd",
"$rget",
"$renv",
"$rwidgets",
"$",
"dust",
"__rhinoHelpers",
"Packages",
"JavaAdapter",
"unescape"
"runs"
],
"globals": {

101
test/dust-tests.js Normal file
View File

@ -0,0 +1,101 @@
'use strict';
var chai = require('chai');
chai.Assertion.includeStack = true;
require('chai').should();
var expect = require('chai').expect;
var nodePath = require('path');
var fs = require('fs');
var dust = require('dustjs-linkedin');
dust.onLoad = function(path, callback) {
if (!fs.existsSync(path)) {
if (!path.endsWith('.dust')) {
path += '.dust';
}
}
fs.readFile(path, 'utf-8', callback);
};
require('../dust').registerHelpers(dust);
function testRender(path, data, done, options) {
options = options || {};
var inputPath = nodePath.join(__dirname, path);
var expectedPath = nodePath.join(__dirname, path + '.expected.html');
var actualPath = nodePath.join(__dirname, path + '.actual.html');
dust.render(inputPath, data, function(err, output) {
if (err) {
return done(err);
}
try {
fs.writeFileSync(actualPath, output, {encoding: 'utf8'});
var expected;
try {
expected = options.expected || fs.readFileSync(expectedPath, {encoding: 'utf8'});
}
catch(e) {
expected = 'TBD';
fs.writeFileSync(expectedPath, expected, {encoding: 'utf8'});
}
if (output !== expected) {
throw new Error('Unexpected output for "' + inputPath + '":\nEXPECTED (' + expectedPath + '):\n---------\n' + expected +
'\n---------\nACTUAL (' + actualPath + '):\n---------\n' + output + '\n---------');
}
done();
} catch(e) {
return done(e);
}
});
}
describe('raptor-templates/dust' , function() {
beforeEach(function(done) {
done();
});
// it('should compile a simple page template', function() {
// testCompiler('test-project/src/pages/page1.rhtml');
// });
it('should allow the async-fragment tag to be used inside a Dust template', function(done) {
var users = {
"0": {
name: "John B. Flowers",
occupation: "Clock repairer",
gender: "Male"
},
"1": {
name: "Pamela R. Rice",
occupation: "Cartographer",
gender: "Female"
},
"2": {
name: "Barbara C. Rigsby",
occupation: "Enrollment specialist",
gender: "Female"
},
"3": {
name: "Anthony J. Ward",
occupation: "Clinical laboratory technologist",
gender: "Male"
}
};
testRender('test-project/dust/async.dust', {
userIds: [0, 1, 2, 3],
userInfo: function(arg, callback) {
callback(null, users[arg.userId]);
}
}, done);
});
});

177
test/raptor-taglib-test.js Normal file
View File

@ -0,0 +1,177 @@
'use strict';
var chai = require('chai');
chai.Assertion.includeStack = true;
require('chai').should();
var expect = require('chai').expect;
var nodePath = require('path');
describe('raptor-taglibs/taglib-lookup' , function() {
beforeEach(function(done) {
for (var k in require.cache) {
if (require.cache.hasOwnProperty(k)) {
delete require.cache[k];
}
}
done();
});
it('should lookup core attributes for top-level template', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
// console.log('LOOKUP: ', Object.keys(lookup.attributes));
var ifAttr = lookup.getAttribute('div', 'c-if');
expect(ifAttr != null).to.equal(true);
expect(ifAttr.type).to.equal('expression');
});
it('should lookup core tag for top-level template', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
var ifTag = lookup.getTag('c-if');
expect(ifTag != null).to.equal(true);
expect(ifTag.name).to.equal('c-if');
});
it('should lookup core template for top-level template', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
// console.log(Object.keys(lookup.tags));
var templateTag = lookup.getTag('c-template');
expect(templateTag != null).to.equal(true);
expect(templateTag.name).to.equal('c-template');
});
it('should lookup custom tag for top-level template', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
var tag = lookup.getTag('test-hello');
// console.log(Object.keys(lookup.tags));
expect(tag != null).to.equal(true);
expect(tag.name).to.equal('test-hello');
});
it('should lookup custom attributes for top-level template', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
// console.log(Object.keys(lookup.attributes));
var attr = lookup.getAttribute('test-hello', 'name');
expect(attr != null).to.equal(true);
expect(attr.type).to.equal('string');
var attr2 = lookup.getAttribute('test-hello', 'splat');
expect(attr2 != null).to.equal(true);
expect(attr2.type).to.equal('number');
attr = lookup.getAttribute('test-hello', 'expr');
expect(attr != null).to.equal(true);
expect(attr.type).to.equal('expression');
});
it('should allow for dynamic attributes', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
// console.log(Object.keys(lookup.attributes));
var attr = lookup.getAttribute('test-hello', 'DYNAMIC');
expect(attr != null).to.equal(true);
expect(attr.type).to.equal('number');
});
it('should cache a lookup', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup1 = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
var lookup2 = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project'));
var lookup3 = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/empty'));
var lookup4 = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
expect(lookup1).to.equal(lookup2);
expect(lookup2).to.equal(lookup3);
expect(lookup1).to.not.equal(lookup4);
});
it('should lookup nested tags', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
var tag = lookup.getTag('nested-foo');
expect(tag != null).to.equal(true);
expect(tag.name).to.equal('nested-foo');
});
it('should lookup attributes for nested tags', function() {
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
// console.log(Object.keys(lookup.attributes));
var attr = lookup.getAttribute('nested-foo', 'attr1');
expect(attr != null).to.equal(true);
expect(attr.type).to.equal('string');
});
it('should lookup tag transformers correctly for un-namespaced tags', function() {
var transformers = [];
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
lookup.forEachTagTransformer('div', function(transformer) {
transformers.push(transformer);
});
expect(transformers.length).to.equal(2);
});
it('should lookup tag transformers correctly for namespaced tag with transformer', function() {
var transformers;
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup;
// lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
// transformers = [];
// lookup.forEachTagTransformer('nested-foo', function(transformer) {
// transformers.push(transformer);
// });
// expect(transformers.length).to.equal(2);
lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/transformers'));
transformers = [];
lookup.forEachTagTransformer('transform-foo', function(transformer) {
transformers.push(transformer);
});
expect(transformers.length).to.equal(3);
expect(transformers[0].path.indexOf('foo')).to.not.equal(-1);
expect(transformers[1].path.indexOf('core-tag-transformer')).to.not.equal(-1);
expect(transformers[2].path.indexOf('html-tag-transformer')).to.not.equal(-1);
transformers = [];
lookup.forEachTagTransformer('transform-bar', function(transformer) {
transformers.push(transformer);
});
expect(transformers.length).to.equal(3);
expect(transformers[0].path.indexOf('core-tag-transformer')).to.not.equal(-1);
expect(transformers[1].path.indexOf('bar')).to.not.equal(-1);
expect(transformers[2].path.indexOf('html-tag-transformer')).to.not.equal(-1);
});
it('should lookup tag transformers core tag with custom node', function() {
var transformers = [];
var taglibLookup = require('../compiler').taglibs.lookup;
var lookup = taglibLookup.buildLookup(nodePath.join(__dirname, 'test-project/nested'));
lookup.forEachTagTransformer('c-else', function(transformer) {
transformers.push(transformer);
});
expect(transformers.length).to.equal(3);
expect(transformers[0].path.indexOf('core-tag-transformer')).to.not.equal(-1);
expect(transformers[1].path.indexOf('else-tag-transformer')).to.not.equal(-1);
expect(transformers[2].path.indexOf('html-tag-transformer')).to.not.equal(-1);
});
});

View File

@ -0,0 +1,19 @@
<ul>
{#userIds}
<li>
{@async-fragment dataProvider="userInfo" var="userInfo" arg-userId=. }
<ul>
<li>
<b>Name:</b> {userInfo.name}
</li>
<li>
<b>Gender:</b> {userInfo.gender}
</li>
<li>
<b>Occupation:</b> {userInfo.occupation}
</li>
</ul>
{/async-fragment}
</li>
{/userIds}
</ul>

View File

@ -0,0 +1 @@
<ul><li><ul><li><b>Name:</b> John B. Flowers</li><li><b>Gender:</b> Male</li><li><b>Occupation:</b> Clock repairer</li></ul></li><li><ul><li><b>Name:</b> Pamela R. Rice</li><li><b>Gender:</b> Female</li><li><b>Occupation:</b> Cartographer</li></ul></li><li><ul><li><b>Name:</b> Barbara C. Rigsby</li><li><b>Gender:</b> Female</li><li><b>Occupation:</b> Enrollment specialist</li></ul></li><li><ul><li><b>Name:</b> Anthony J. Ward</li><li><b>Gender:</b> Male</li><li><b>Occupation:</b> Clinical laboratory technologist</li></ul></li></ul>