marko/lib/compiler/ElementNode.js
2014-01-07 16:59:30 -07:00

406 lines
14 KiB
JavaScript

/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
define.Class(
'raptor/templating/compiler/ElementNode',
'raptor/templating/compiler/Node',
['raptor'],
function(raptor, require) {
"use strict";
var forEachEntry = raptor.forEachEntry,
forEach = raptor.forEach,
objects = require('raptor/objects'),
escapeXmlAttr = require('raptor/xml/utils').escapeXmlAttr,
XML_URI = 'http://www.w3.org/XML/1998/namespace',
XML_URI_ALT = 'http://www.w3.org/XML/1998/namespace',
ExpressionParser = require('raptor/templating/compiler/ExpressionParser');
var ElementNode = function(localName, uri, prefix) {
ElementNode.superclass.constructor.call(this, 'element');
if (!this._elementNode) {
this._elementNode = true;
this.dynamicAttributesExpression = null;
this.attributesByNS = {};
this.prefix = prefix;
this.localName = this.tagName = localName;
this.uri = uri;
this.allowSelfClosing = false;
this.startTagOnly = false;
}
};
ElementNode.prototype = {
getQName: function() {
return this.localName ? (this.prefix ? this.prefix + ":" : "") + this.localName : null;
},
/**
*
* @param startTagOnly
*/
setStartTagOnly: function(startTagOnly) {
this.startTagOnly = true;
},
/**
*
* @param allowSelfClosing
*/
setAllowSelfClosing: function(allowSelfClosing) {
this.allowSelfClosing = allowSelfClosing;
},
/**
*
* @returns {Boolean}
*/
isElementNode: function() {
return true;
},
/**
*
* @returns {Boolean}
*/
isTextNode: function() {
return false;
},
getAllAttributes: function() {
var allAttrs = [];
forEachEntry(this.attributesByNS, function(uri, attrs) {
forEachEntry(attrs, function(name, attr) {
allAttrs.push(attr);
});
}, this);
return allAttrs;
},
forEachAttributeAnyNS: function(callback, thisObj) {
forEachEntry(this.attributesByNS, function(uri, attrs) {
forEachEntry(attrs, function(name, attr) {
callback.call(thisObj, attr);
});
});
},
forEachAttributeNS: function(uri, callback, thisObj) {
var attrs = this.attributesByNS[uri || ''];
if (attrs) {
forEachEntry(attrs, function(name, attr) {
callback.call(thisObj, attr);
});
}
},
/**
*
* @returns {Array}
*/
getAttributes: function() {
var attributes = [];
forEachEntry(this.attributes, function(name, attr) {
attributes.push(attr);
}, this);
return attributes;
},
/**
*
* @param name
* @returns
*/
getAttribute: function(name) {
return this.getAttributeNS(null, name);
},
/**
*
* @param uri
* @param localName
* @returns
*/
getAttributeNS: function(uri, localName) {
var attrNS = this.attributesByNS[uri || ''];
var attr = attrNS ? attrNS[localName] : undefined;
return attr ? attr.value : undefined;
},
/**
*
* @param localName
* @param value
*/
setAttribute: function(localName, value, escapeXml) {
this.setAttributeNS(null, localName, value, null, escapeXml);
},
/**
*
* @param uri
* @param localName
* @param value
* @param prefix
*/
setAttributeNS: function(uri, localName, value, prefix, escapeXml) {
var attrNS = this.attributesByNS[uri || ''] || (this.attributesByNS[uri || ''] = {});
attrNS[localName] = {
localName: localName,
value: value,
prefix: prefix,
uri: uri,
escapeXml: escapeXml,
qName: prefix ? (prefix + ":" + localName) : localName,
name: uri ? (uri + ":" + localName) : localName,
toString: function() {
return this.name;
}
};
},
/**
*
* @param name
*/
setEmptyAttribute: function(name) {
this.setAttribute(name, null);
},
/**
*
* @param localName
*/
removeAttribute: function(localName) {
this.removeAttributeNS(null, localName);
},
/**
*
* @param uri
* @param localName
*/
removeAttributeNS: function(uri, localName) {
var attrNS = this.attributesByNS[uri || ''] || (this.attributesByNS[uri || ''] = {});
if (attrNS) {
delete attrNS[localName];
if (objects.isEmpty(attrNS)) {
delete this.attributesByNS[uri || ''];
}
}
},
removeAttributesNS: function(uri) {
delete this.attributesByNS[uri || ''];
},
/**
*
* @returns {Boolean}
*/
isPreserveWhitespace: function() {
var preserveSpace = ElementNode.superclass.isPreserveWhitespace.call(this);
if (preserveSpace === true) {
return true;
}
var preserveAttr = this.getAttributeNS(XML_URI, "space") || this.getAttributeNS(XML_URI_ALT, "space") || this.getAttribute("xml:space") === "preserve";
if (preserveAttr === 'preserve') {
return true;
}
return preserveSpace;
},
hasAttributesAnyNS: function() {
return !objects.isEmpty(this.attributesByNS);
},
hasAttributes: function() {
return this.hasAttributesNS('');
},
hasAttributesNS: function(uri) {
return this.attributesByNS[uri || ''] !== undefined;
},
hasAttribute: function(localName) {
return this.hasAttributeNS('', localName);
},
hasAttributeNS: function(uri, localName) {
var attrsNS = this.attributesByNS[uri || ''];
return attrsNS ? attrsNS.hasOwnProperty(localName) : false;
},
removePreserveSpaceAttr: function() {
this.removeAttributeNS(XML_URI, "space");
this.removeAttributeNS(XML_URI_ALT, "space");
this.removeAttribute("space");
},
setStripExpression: function(stripExpression) {
this.stripExpression = stripExpression;
},
/**
*
* @param template
*/
doGenerateCode: function(template) {
this.generateBeforeCode(template);
this.generateCodeForChildren(template);
this.generateAfterCode(template);
},
generateBeforeCode: function(template) {
var preserveWhitespace = this.preserveWhitespace = this.isPreserveWhitespace();
var name = this.prefix ? (this.prefix + ":" + this.localName) : this.localName;
if (preserveWhitespace) {
this.removePreserveSpaceAttr();
}
template.text("<" + name);
this.forEachAttributeAnyNS(function(attr) {
var prefix = attr.prefix;
if (!prefix && attr.uri) {
prefix = this.resolveNamespacePrefix(attr.uri);
}
if (prefix) {
name = prefix + (attr.localName ? (":" + attr.localName) : "");
}
else {
name = attr.localName;
}
if (attr.value === null || attr.value === undefined) {
template.text(' ' + name);
}
else if (template.isExpression(attr.value)) {
template.attr(name, attr.value, attr.escapeXml !== false);
}
else {
var attrParts = [],
hasExpression = false,
invalidAttr = false;
ExpressionParser.parse(
attr.value,
{
text: function(text, escapeXml) {
attrParts.push({
text: text,
escapeXml: escapeXml !== false
});
},
expression: function(expression, escapeXml) {
hasExpression = true;
attrParts.push({
expression: expression,
escapeXml: escapeXml !== false
});
},
error: function(message) {
invalidAttr = true;
this.addError('Invalid expression found in attribute "' + name + '". ' + message);
}
},
this);
if (invalidAttr) {
template.text(name + '="' + escapeXmlAttr(attr.value) + '"');
}
else {
if (hasExpression && attrParts.length === 1) {
template.attr(name, attrParts[0].expression, attrParts[0].escapeXml !== false);
}
else {
template.text(' ' + name + '="');
forEach(attrParts, function(part) {
if (part.text) {
template.text(part.escapeXml !== false ? escapeXmlAttr(part.text) : part.text);
}
else if (part.expression) {
template.write(part.expression, {escapeXmlAttr: part.escapeXml !== false});
}
else {
throw raptor.createError(new Error("Illegal state"));
}
});
template.text('"');
}
}
}
}, this);
if (this.dynamicAttributesExpression) {
template.attrs(this.dynamicAttributesExpression);
}
if (this.hasChildren()) {
template.text(">");
}
else {
if (this.startTagOnly) {
template.text(">");
}
else if (this.allowSelfClosing) {
template.text("/>");
}
}
},
generateAfterCode: function(template) {
var name = this.prefix ? (this.prefix + ":" + this.localName) : this.localName;
if (this.hasChildren()) {
template.text("</" + name + ">");
}
else {
if (!this.startTagOnly && !this.allowSelfClosing) {
template.text("></" + name + ">");
}
}
},
toString: function() {
return "<" + (this.prefix ? (this.prefix + ":" + this.localName) : this.localName) + ">";
}
};
return ElementNode;
});