/**
@class
JsTemplates are used to parse rio templates (*.jst files). JsTemplates are created by rio pages as a starting point
for a pages html state. You should rarely have to instantiate this class directly.
*/
rio.JsTemplate = Class.create({
initialize: function(options) {
this._name = options.name || "template";
this._template = options.text.gsub(" ", "\u00a0");
this._path = options.path;
},
mixinMethods: function() {
var template = this;
return {
buildHtml: function() {
return template.render(this);
}
};
},
xml: function() {
return '
' + this._template + "
";
},
parse: function() {
return rio.JsTemplate.parse(this._path ? this._template : this.xml());
},
render: function(context) {
var xml = this.parse();
try {
if (this._path) {
var nodes = xml.childNodes.item(0).getElementsByTagName(this._path)[0].childNodes;
var divNode;
var i = 0;
while (divNode == undefined) {
var node = nodes.item(i);
if (node.nodeType == 1) {
divNode = node;
}
i++;
}
return this.renderElement(node, context);
} else {
return this.renderElement(xml.childNodes.item(0), context);
}
} catch(e) {
rio.error(e, "Rendering error. Your xml might be malformed.\nRemember to escape these characters [<, \", &] with [<, ", &])");
throw(e);
}
},
renderElement: function(xmlElement, context) {
var nodes = xmlElement.childNodes;
var htmlChildren = [];
var i, item;
for (i = 0; i < nodes.length; i++) {
item = nodes.item(i);
switch(item.nodeType) {
case 1: // Element
if (item.nodeName.startsWith("rio:")) {
htmlChildren.push(this.renderComponent(item, context).html());
} else {
htmlChildren.push(this.renderElement(item, context));
}
break;
case 3: // Text
htmlChildren.push(item.nodeValue);
break;
}
}
var htmlAttributes = {};
var attributes = xmlElement.attributes;
var rioId;
for (i = 0; i < attributes.length; i++) {
item = attributes.item(i);
if (item.nodeName == "rio:id") {
rioId = item.nodeValue;
} else {
htmlAttributes[item.nodeName] = item.nodeValue;
}
}
var htmlElement = rio.Tag.build(xmlElement.nodeName, htmlChildren, htmlAttributes);
if (rioId) {
context[("get-" + rioId + "Element").camelize()] = function() {
return htmlElement;
};
}
return htmlElement;
},
renderChildNodes: function(xmlElement, context) {
var nodes = xmlElement.childNodes;
var renderedChildren = [];
for (var i = 0; i < nodes.length; i++) {
var item = nodes.item(i);
switch(item.nodeType) {
case 1: // Element
if (item.nodeName.startsWith("rio:")) {
if (item.nodeName == "rio:Object") {
renderedChildren.push(this.renderRioObject(item, context));
} else {
renderedChildren.push(this.renderComponent(item, context));
}
} else {
renderedChildren.push(this.renderElement(item, context));
}
break;
case 3: // Text
if (item.nodeValue.strip() != "") {
renderedChildren.push(item.nodeValue);
}
break;
}
}
return renderedChildren.reduce();
},
renderRioObject: function(xmlElement, context) {
var obj = {};
var nodes = xmlElement.childNodes;
for (var i = 0; i < nodes.length; i++) {
var item = nodes.item(i);
if (item.nodeType == 1) {
obj[item.nodeName] = this.renderChildNodes(item, context);
}
}
return obj;
},
renderComponent: function(xmlElement, context) {
var componentType = xmlElement.nodeName.match(/rio:(.*)/)[1];
var componentAttributes = {};
var attributes = xmlElement.attributes;
var rioId, i, item;
for (i = 0; i < attributes.length; i++) {
item = attributes.item(i);
if (item.nodeName == "rio:id") {
rioId = item.nodeValue;
} else {
var value = item.nodeValue;
var executable = value.match(/\{(.*)\}/);
if (executable) {
rio.pageBinding = context;
try {
value = eval("(function() { return " + executable[1] + "; }).bind(rio.pageBinding)();");
} catch(e) {
rio.error(e, "Error evaluating " + this._name.classize() + " template attribute: " + executable[1]);
throw(e);
}
delete rio.pageBinding;
}
componentAttributes[item.nodeName] = value;
}
}
var nodes = xmlElement.childNodes;
for (i = 0; i < nodes.length; i++) {
item = nodes.item(i);
if (item.nodeType == 1) {
if (item.nodeName == "rio:id") {
rioId = item.nodeValue;
} else {
componentAttributes[item.nodeName] = this.renderChildNodes(item, context);
}
}
}
try {
var component = new rio.components[componentType](componentAttributes);
} catch(e2) {
if (rio.components[componentType] == undefined) {
rio.error(e2, componentType + " not found. Add 'components/" + componentType.underscore() + "' to the " + this._name.classize() + " require list.");
}
throw(e2);
}
if (rioId) {
context[("get-" + rioId).camelize()] = function() {
return component;
};
}
return component;
},
name: function() {
return this._name;
}
});
Object.extend(rio.JsTemplate, {
build: function(path) {
path = rio.boot.appRoot + path;
if (this._templatePreloaded(path)) {
return new rio.JsTemplate({
name: this._templateNameFromPath(path),
text: this._allTemplatesFile,
path: path.gsub("/", "--")
});
} else {
rio.log("missed: " + path);
return new rio.JsTemplate({
name: this._templateNameFromPath(path),
text: this._templateFile(path)
});
}
},
preload: function(path) {
this._allTemplatesFile = this._templateFile(path);
this._allTemplatesList = [];
var doc = this.parse(this._allTemplatesFile);
var templateNodes = doc.childNodes.item(0).childNodes;
for (var i = 0; i < templateNodes.length; i++) {
var item = templateNodes.item(i);
if (item.nodeType == 1) {
this._allTemplatesList.push(item.nodeName.gsub("--", "/"));
}
}
},
parse: function(xml) {
var xmlDoc;
if (Prototype.Browser.IE) {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(xml);
} else {
try {
var parser = new DOMParser();
xmlDoc = parser.parseFromString(xml,"text/xml");
} catch(e) {
rio.log(e);
}
}
return xmlDoc;
},
_templatePreloaded: function(path) {
return (this._allTemplatesList || []).include(path);
},
_templateNameFromPath: function(path) {
return path.split("/").last().camelize();
},
_templateFile: function(path) {
return rio.File.open(rio.boot.root + rio.boot.appRoot + path + ".jst", {
asynchronous: false,
onFailure: function() {
rio.Application.fail("Failed loading template - " + path);
}
});
},
toString: function() {
return "JsTemplate";
}
});