/* * Copyright 2011 eBay Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ define.Class( 'raptor/templating/compiler/TemplateCompiler', ['raptor'], function(raptor, require, exports, module) { "use strict"; var TemplateBuilder = require('raptor/templating/compiler/TemplateBuilder'), ParseTreeBuilder = require('raptor/templating/compiler/ParseTreeBuilder'), Expression = require('raptor/templating/compiler/Expression'), minifier = require.exists("raptor/js-minifier") ? require("raptor/js-minifier") : null, TypeConverter = require('raptor/templating/compiler/TypeConverter'), logger = module.logger(); /** * @param taglibs {raptor/templating/compiler/TaglibCollection} The collection of taglibs that are available to the compiler * @param options {object} The options for the compiler. */ var TemplateCompiler = function(taglibs, options) { this.taglibs = taglibs; this.options = options || {}; this.workDir = this.options.workDir || require('raptor/templating/compiler').workDir || require('raptor/temp').workDir; this.errors = []; }; TemplateCompiler.prototype = { /** * This method processes every node in the tree using a pre-order traversal. * That is, the parent node is transformed before its child nodes are * transformed. * *
* NOTE: * This method is repeatedly called until there are no more nodes in the tree * that need to be transformed. This is because transformers might add * new nodes to the tree in a position that has already been passed and * we want to make sure that all new nodes added to the tree are transformed * as necessary. * * @param node {raptor/templating/compiler/Node} The root node to transform * @param templateBuilder {raptor/templating/compiler/TemplateBuilder} The template builder object that is used to control how the compiled code is generated */ transformTree: function(rootNode, templateBuilder) { if (!templateBuilder) { throw raptor.createError(new Error("The templateBuilder argument is required")); } var transformTreeHelper = function(node) { try { this.taglibs.forEachNodeTransformer( //Handle all of the transformers that are appropriate for this node node, //The node being transformed function(transformer) { if (!node.isTransformerApplied(transformer)) { //Check to make sure a transformer of a certain type is only applied once to a node node.setTransformerApplied(transformer); //Mark the node as have been transformed by the current transformer this._transformerApplied = true; //Set the flag to indicate that a node was transformed node.compiler = this; transformer.getInstance().process(node, this, templateBuilder); //Have the transformer process the node (NOTE: Just because a node is being processed by the transformer doesn't mean that it has to modify the parse tree) } }, this); } catch(e) { throw raptor.createError(new Error('Unable to compile template at path "' + templateBuilder.filePath + ". Error: " + e.message), e); } /* * Now process the child nodes by looping over the child nodes * and transforming the subtree recursively * * NOTE: The length of the childNodes array might change as the tree is being performed. * The checks to prevent transformers from being applied multiple times makes * sure that this is not a problem. */ node.forEachChild(function(childNode) { if (!childNode.parentNode) { return; //The child node might have been removed from the tree } transformTreeHelper.call(this, childNode); }, this); }; /* * The tree is continuously transformed until we go through an entire pass where * there were no new nodes that needed to be transformed. This loop makes sure that * nodes added by transformers are also transformed. */ do { this._transformerApplied = false; //Reset the flag to indicate that no transforms were yet applied to any of the nodes for this pass transformTreeHelper.call(this, rootNode); //Run the transforms on the tree } while (this._transformerApplied); }, /** * Compiles the XML source code for a template and returns the resulting compiled JavaScript code. * *
* When the returned code is evaluated by a JavaScript engine it will register the function
* to render the template. The function is registered with the name found as the "name" attribute
* of the root <c:template> element unless a template name is passed in as a compiler option.
*
*
* @param xmlSrc {String} The XML source code for the template
* @param filePath {String} The path to the input template for debugging/error reporting only
* @returns {String} The JavaScript code for the compiled template
*/
compile: function(xmlSrc, resource, callback, thisObj) {
var _this = this,
rootNode,
templateBuilder,
handleErrors = function() {
var message = "Errors in template:\n",
errors = _this.getErrors();
for (var i=0, len=errors.length; i