UNPKG

raptor

Version:

RaptorJS provides an AMD module loader that works in Node, Rhino and the web browser. It also includes various sub-modules to support building optimized web applications.

738 lines (601 loc) 27.9 kB
/* * Copyright 2011 eBay Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ define.Class( 'raptor/templating/compiler/Node', ['raptor'], function(raptor, require) { "use strict"; var forEachEntry = raptor.forEachEntry, forEach = raptor.forEach, isArray = Array.isArray, isEmpty = require('raptor/objects').isEmpty; var Node = function(nodeType) { if (!this.nodeType) { this._isRoot = false; this.preserveWhitespace = null; this.wordWrapEnabled = null; this.escapeXmlBodyText = null; this.escapeXmlContext = null; this.nodeType = nodeType; this.parentNode = null; this.previousSibling = null; this.nextSibling = null; this.firstChild = null; this.lastChild = null; this.namespaceMappings = {}; this.prefixMappings = {}; this.transformersApplied = {}; this.properties = {}; } }; Node.prototype = { setRoot: function(isRoot) { this._isRoot = isRoot; }, getPosition: function() { var pos = this.pos || this.getProperty("pos") || { toString: function() { return "(unknown position)"; } }; return pos; }, setPosition: function(pos) { this.pos = pos; }, addError: function(error) { var compiler = this.compiler, curNode = this; while (curNode != null && !compiler) { compiler = curNode.compiler; if (compiler) { break; } curNode = curNode.parentNode; } if (!compiler) { throw raptor.createError(new Error("Template compiler not set for node " + this)); } var pos = this.getPosition(); compiler.addError(error + " (" + this.toString() + ")", pos); }, setProperty: function(name, value) { this.setPropertyNS(null, name, value); }, setPropertyNS: function(uri, name, value) { if (!uri) { uri = ""; } var namespacedProps = this.properties[uri]; if (!namespacedProps) { namespacedProps = this.properties[uri] = {}; } namespacedProps[name] = value; }, setProperties: function(props) { this.setPropertiesNS(null, props); }, setPropertiesNS: function(uri, props) { if (!uri) { uri = ""; } forEachEntry(props, function(name, value) { this.setPropertyNS(uri, name, value); }, this); }, getPropertyNamespaces: function() { return Object.keys(this.properties); }, getProperties: function(uri) { return this.getPropertiesNS(null); }, hasProperty: function(name) { return this.hasPropertyNS('', name); }, hasPropertyNS: function(uri, name) { if (!uri) { uri = ""; } var namespaceProps = this.properties[uri]; return namespaceProps.hasOwnProperty(name); }, getPropertiesByNS: function() { return this.properties; }, getPropertiesNS: function(uri) { if (!uri) { uri = ""; } return this.properties[uri]; }, forEachProperty: function(callback, thisObj) { forEachEntry(this.properties, function(uri, properties) { forEachEntry(properties, function(name, value) { callback.call(thisObj, uri, name, value); }, this); }, this); }, getProperty: function(name) { return this.getPropertyNS(null, name); }, getPropertyNS: function(uri, name) { if (!uri) { uri = ""; } var namespaceProps = this.properties[uri]; return namespaceProps ? namespaceProps[name] : undefined; }, removeProperty: function(name) { this.removePropertyNS("", name); }, removePropertyNS: function(uri, name) { if (!uri) { uri = ""; } var namespaceProps = this.properties[uri]; if (namespaceProps) { delete namespaceProps[name]; } if (isEmpty(namespaceProps)) { delete this.properties[uri]; } }, removePropertiesNS: function(uri) { delete this.properties[uri]; }, forEachPropertyNS: function(uri, callback, thisObj) { if (uri == null) { uri = ''; } var props = this.properties[uri]; if (props) { forEachEntry(props, function(name, value) { callback.call(thisObj, name, value); }, this); } }, forEachChild: function(callback, thisObj) { if (!this.firstChild) { return; } /* * Convert the child linked list to an array so that * if the callback code manipulates the child nodes * looping won't break */ var children = []; var curChild = this.firstChild; while(curChild) { children.push(curChild); curChild = curChild.nextSibling; } for (var i=0, len=children.length; i<len; i++) { curChild = children[i]; if (curChild.parentNode === this) { //Make sure the node is still a child of this node if (false === callback.call(thisObj, curChild)) { return; } } } }, getExpression: function(template, childrenOnly, escapeXml, asFunction) { if (!template) { throw raptor.createError(new Error("template argument is required")); } var _this = this; return template.makeExpression({ toString: function() { return template.captureCode(function() { if (asFunction) { template.code("function() {\n") .code(template.indentStr(2) + "return context." + (escapeXml !== false ? "captureString" : "c") + "(function() {\n") .indent(3, function() { if (childrenOnly === true) { _this.generateCodeForChildren(template); } else { _this.generateCode(template); } }) .code(template.indentStr(2) + "});\n") .code(template.indentStr() + "}"); } else { template.code("context." + (escapeXml !== false ? "captureString" : "c") + "(function() {\n") .indent(function() { if (childrenOnly === true) { _this.generateCodeForChildren(template); } else { _this.generateCode(template); } }) .code(template.indentStr() + "})"); } }); } }); }, getBodyContentExpression: function(template, escapeXml) { return this.getExpression(template, true, escapeXml, false); }, getBodyContentFunctionExpression: function(template, escapeXml) { return this.getExpression(template, true, escapeXml, true); }, isTransformerApplied: function(transformer) { return this.transformersApplied[transformer.id] === true; }, setTransformerApplied: function(transformer) { this.transformersApplied[transformer.id] = true; }, hasChildren: function() { return this.firstChild != null; }, appendChild: function(childNode) { if (childNode.parentNode) { childNode.parentNode.removeChild(childNode); } if (!this.firstChild) { this.firstChild = this.lastChild = childNode; childNode.nextSibling = null; childNode.previousSibling = null; } else { this.lastChild.nextSibling = childNode; childNode.previousSibling = this.lastChild; this.lastChild = childNode; } childNode.parentNode = this; }, appendChildren: function(childNodes) { if (!childNodes) { return; } raptor.forEach(childNodes, function(childNode) { this.appendChild(childNode); }, this); }, isRoot: function() { return this._isRoot === true; }, detach: function() { if (this.parentNode) { this.parentNode.removeChild(this); } }, removeChild: function(childNode) { if (childNode.parentNode !== this) { //Check if the child node is a child of the parent return null; } var previousSibling = childNode.previousSibling, nextSibling = childNode.nextSibling; if (this.firstChild === childNode && this.lastChild === childNode) { //The child node is the one and only child node being removed this.firstChild = this.lastChild = null; } else if (this.firstChild === childNode) { //The child node being removed is the first child and there is another child after it this.firstChild = this.firstChild.nextSibling; //Make the next child the first child this.firstChild.previousSibling = null; } else if (this.lastChild === childNode) { //The child node being removed is the last child and there is another child before it this.lastChild = this.lastChild.previousSibling; //Make the previous child the last child this.lastChild.nextSibling = null; } else { previousSibling.nextSibling = nextSibling; nextSibling.previousSibling = previousSibling; } //Make sure the removed node is completely detached childNode.parentNode = null; childNode.previousSibling = null; childNode.nextSibling = null; return childNode; }, removeChildren: function() { while (this.firstChild) { this.removeChild(this.firstChild); } }, replaceChild: function(newChild, replacedChild) { if (newChild === replacedChild) { return false; } if (!replacedChild) { return false; } if (replacedChild.parentNode !== this) { return false; //The parent does not have the replacedChild as a child... nothing to do } if (this.firstChild === replacedChild && this.lastChild === replacedChild) { this.firstChild = newChild; this.lastChild = newChild; newChild.previousSibling = null; newChild.nextSibling = null; } else if (this.firstChild === replacedChild) { newChild.nextSibling = replacedChild.nextSibling; replacedChild.nextSibling.previousSibling = newChild; this.firstChild = newChild; } else if (this.lastChild === replacedChild) { newChild.previousSibling = replacedChild; replacedChild.previousSibling.nextSibling = newChild; this.lastChild = newChild; } else { replacedChild.nextSibling.previousSibling = newChild; replacedChild.previousSibling.nextSibling = newChild; newChild.nextSibling = replacedChild.nextSibling; newChild.previousSibling = replacedChild.previousSibling; } newChild.parentNode = this; replacedChild.parentNode = null; replacedChild.previousSibling = null; replacedChild.nextSibling = null; return true; }, insertAfter: function(node, referenceNode) { if (!node) { return false; } if (referenceNode && referenceNode.parentNode !== this) { return false; } if (isArray(node)) { raptor.forEach(node, function(node) { this.insertAfter(node, referenceNode); referenceNode = node; }, this); return true; } if (node === referenceNode) { return false; } if (referenceNode === this.lastChild) { this.appendChild(node); return true; } if (node.parentNode) { node.parentNode.removeChild(node); } if (!referenceNode || referenceNode === this.lastChild) { this.appendChild(node); return true; } else { referenceNode.nextSibling.previousSibling = node; node.nextSibling = referenceNode.nextSibling; node.previousSibling = referenceNode; referenceNode.nextSibling = node; } node.parentNode = this; return true; }, insertBefore: function(node, referenceNode) { if (!node) { return false; } if (referenceNode && referenceNode.parentNode !== this) { return false; } if (isArray(node)) { var nodes = node, i; for (i=nodes.length-1;i>=0; i--) { this.insertBefore(nodes[i], referenceNode); referenceNode = nodes[i]; } return true; } if (node === referenceNode) { return false; } if (node.parentNode) { node.parentNode.removeChild(node); } if (!referenceNode) { this.appendChild(node); } else if (this.firstChild === referenceNode) { this.firstChild = node; this.firstChild.nextSibling = referenceNode; this.firstChild.previousSibling = null; referenceNode.previousSibling = this.firstChild; node.parentNode = this; } else { this.insertAfter(node, referenceNode.previousSibling); } return true; }, isTextNode: function() { return false; }, isElementNode: function() { return false; }, setStripExpression: function(stripExpression) { this.stripExpression = stripExpression; }, generateCode: function(template) { this.compiler = template.compiler; var preserveWhitespace = this.isPreserveWhitespace(); if (preserveWhitespace == null) { preserveWhitespace = template.options.preserveWhitespace; if (preserveWhitespace === true || (preserveWhitespace && preserveWhitespace["*"])) { this.setPreserveWhitespace(true); } else { this.setPreserveWhitespace(false); } } var wordWrapEnabled = this.isWordWrapEnabled(); if (wordWrapEnabled == null) { wordWrapEnabled = template.options.wordWrapEnabled; if (wordWrapEnabled !== false) { this.setWordWrapEnabled(true); } } if (this.isEscapeXmlBodyText() == null) { this.setEscapeXmlBodyText(true); } try { if (!this.stripExpression || this.stripExpression.toString() === 'false') { this.doGenerateCode(template); } else if (this.stripExpression.toString() === 'true') { this.generateCodeForChildren(template); } else { //There is a strip expression if (!this.generateBeforeCode || !this.generateAfterCode) { this.addError("The c:strip directive is not supported for node " + this); this.generateCodeForChildren(template); return; } var nextStripVarId = template.getAttribute("nextStripVarId"); if (nextStripVarId == null) { nextStripVarId = template.setAttribute("nextStripVarId", 0); } var varName = '__strip' + (nextStripVarId++); template.statement('var ' + varName + ' = !(' + this.stripExpression + ');'); template .statement('if (' + varName + ') {') .indent(function() { this.generateBeforeCode(template); }, this) .line("}"); this.generateCodeForChildren(template); template .statement('if (' + varName + ') {') .indent(function() { this.generateAfterCode(template); }, this) .line("}"); } } catch(e) { throw raptor.createError(new Error("Unable to generate code for node " + this + " at position [" + this.getPosition() + "]. Exception: " + e), e); } }, /** * * @returns {Boolean} */ isPreserveWhitespace: function() { return this.preserveWhitespace; }, /** * * @param preserve */ setPreserveWhitespace: function(preserve) { this.preserveWhitespace = preserve; }, isWordWrapEnabled: function() { return this.wordWrapEnabled; }, setWordWrapEnabled: function(enabled) { this.wordWrapEnabled = enabled; }, doGenerateCode: function(template) { this.generateCodeForChildren(template); }, generateCodeForChildren: function(template, indent) { if (!template) { throw raptor.createError(new Error('The "template" argument is required')); } if (indent === true) { template.incIndent(); } this.forEachChild(function(childNode) { if (childNode.isPreserveWhitespace() == null) { childNode.setPreserveWhitespace(this.isPreserveWhitespace() === true); } if (childNode.isWordWrapEnabled() == null) { childNode.setWordWrapEnabled(this.isWordWrapEnabled() === true); } if (childNode.isEscapeXmlBodyText() == null) { childNode.setEscapeXmlBodyText(this.isEscapeXmlBodyText() !== false); } if (childNode.getEscapeXmlContext() == null) { childNode.setEscapeXmlContext(this.getEscapeXmlContext() || require('raptor/templating/compiler/EscapeXmlContext').Element); } childNode.generateCode(template); }, this); if (indent === true) { template.decIndent(); } }, addNamespaceMappings: function(namespaceMappings) { if (!namespaceMappings) { return; } var existingNamespaceMappings = this.namespaceMappings; var prefixMappings = this.prefixMappings; forEachEntry(namespaceMappings, function(prefix, uri) { existingNamespaceMappings[prefix] = uri; prefixMappings[uri] = prefix; }); }, hasNamespacePrefix: function(uri) { return this.prefixMappings.hasOwnProperty(uri); }, resolveNamespacePrefix: function(uri) { var prefix = this.prefixMappings[uri]; return (!prefix && this.parentNode) ? this.parentNode.resolveNamespacePrefix() : prefix; }, forEachNamespace: function(callback, thisObj) { forEachEntry(this.namespaceMappings, callback, thisObj); }, getNodeClass: function() { return this.nodeClass || this.getClass(); }, setNodeClass: function(nodeClass) { this.nodeClass = nodeClass; }, prettyPrintTree: function() { var out = []; var printNode = function(node, indent) { out.push(indent + node.toString() + '\n'); node.forEachChild(function(child) { printNode(child, indent + " "); }, this); }; printNode(this, ""); return out.join(''); }, setEscapeXmlBodyText: function(escapeXmlBodyText) { this.escapeXmlBodyText = escapeXmlBodyText; }, isEscapeXmlBodyText: function() { return this.escapeXmlBodyText; }, setEscapeXmlContext: function(escapeXmlContext) { this.escapeXmlContext = escapeXmlContext; }, getEscapeXmlContext: function() { return this.escapeXmlContext; } }; return Node; });