forked from verdnatura/hedera-web
703 lines
14 KiB
JavaScript
Executable File
703 lines
14 KiB
JavaScript
Executable File
/**
|
|
* Creates a object from a XML specification.
|
|
**/
|
|
Vn.Builder = new Class
|
|
({
|
|
Extends: Vn.Object
|
|
,objectMap: {}
|
|
,tags: {}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++ Deprecated
|
|
|
|
,loadXml: function (xmlDoc)
|
|
{
|
|
if (!xmlDoc)
|
|
return false;
|
|
|
|
var docElement = xmlDoc.documentElement;
|
|
|
|
if (docElement.tagName !== 'vn')
|
|
return false;
|
|
|
|
this.contexts = [];
|
|
var childs = docElement.childNodes;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
this.loadNode (childs[i], null);
|
|
|
|
this.resolveProperties ();
|
|
delete this.contexts;
|
|
return true;
|
|
}
|
|
|
|
,loadXmlFromNode: function (node)
|
|
{
|
|
this.contexts = [];
|
|
var mainNode = this.loadNode (node);
|
|
this.resolveProperties ();
|
|
delete this.contexts;
|
|
return mainNode;
|
|
}
|
|
|
|
,add: function (id, object)
|
|
{
|
|
this.objectMap[id] = object;
|
|
}
|
|
|
|
,loadNode: function (node)
|
|
{
|
|
var customNode;
|
|
var htmlNode = null;
|
|
var tagName = null;
|
|
|
|
if (node.tagName)
|
|
tagName = node.tagName.toLowerCase ();
|
|
|
|
if (tagName === 't')
|
|
{
|
|
htmlNode = document.createTextNode (_(node.firstChild.textContent));
|
|
}
|
|
else if (!tagName)
|
|
{
|
|
htmlNode = document.importNode (node, false);
|
|
}
|
|
else if ((customNode = this.loadCustomNode (node, null)))
|
|
{
|
|
if (customNode instanceof Htk.Widget)
|
|
htmlNode = customNode.getNode ();
|
|
}
|
|
else
|
|
{
|
|
htmlNode = document.createElement (tagName);
|
|
|
|
var a = node.attributes;
|
|
|
|
for (var i = 0; i < a.length; i++)
|
|
{
|
|
var nodeName = a[i].nodeName;
|
|
var nodeValue = a[i].nodeValue;
|
|
|
|
if (/^on-\w+/.test (nodeName))
|
|
{
|
|
var method = this.getMethod (nodeValue);
|
|
htmlNode.addEventListener (
|
|
nodeName.substr (3), method.bind (this.signalData));
|
|
}
|
|
else if (nodeName === 'id')
|
|
{
|
|
this.objectMap[nodeValue] = htmlNode;
|
|
}
|
|
else
|
|
htmlNode.setAttribute (nodeName,
|
|
this.translateValue (nodeValue));
|
|
}
|
|
|
|
var childs = node.childNodes;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
{
|
|
var htmlChild = this.loadNode (childs[i]);
|
|
|
|
if (htmlChild)
|
|
htmlNode.appendChild (htmlChild);
|
|
}
|
|
}
|
|
|
|
return htmlNode;
|
|
}
|
|
|
|
,loadCustomNode: function (node, parentContext)
|
|
{
|
|
if (!node.tagName)
|
|
return null;
|
|
|
|
var tagName = node.tagName.toLowerCase ();
|
|
var klass = Vn.customTags[tagName];
|
|
|
|
if (!klass)
|
|
return null;
|
|
|
|
var customNode = new klass ();
|
|
|
|
if (!this.tags[tagName])
|
|
this.tags[tagName] = [];
|
|
|
|
this.tags[tagName].push (customNode);
|
|
|
|
var context = {
|
|
node: node
|
|
,parent: parentContext
|
|
,object: customNode
|
|
,klass: klass
|
|
};
|
|
this.contexts.push (context);
|
|
|
|
var nodeId = node.getAttribute ('id');
|
|
|
|
if (nodeId)
|
|
this.objectMap[nodeId] = customNode;
|
|
|
|
var childs = node.childNodes;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
this.loadCustomNode (childs[i], context);
|
|
|
|
return customNode;
|
|
}
|
|
|
|
,resolveProperties: function ()
|
|
{
|
|
for (var i = 0; i < this.contexts.length; i++)
|
|
{
|
|
var c = this.contexts[i];
|
|
var a = c.node.attributes;
|
|
|
|
for (var j = 0; j < a.length; j++)
|
|
this.setAttribute (c, a[j].nodeName, a[j].nodeValue);
|
|
|
|
if (c.parent)
|
|
{
|
|
var parentProperty = c.node.getAttribute ('property');
|
|
|
|
if (!parentProperty)
|
|
parentProperty = c.parent.klass.Child;
|
|
|
|
if (parentProperty)
|
|
this.setProperty (c.parent, parentProperty, c.object);
|
|
|
|
if (c.klass.Parent)
|
|
this.setProperty (c, c.klass.Parent, c.parent.object);
|
|
}
|
|
|
|
c.object.loadXml (this, c.node);
|
|
}
|
|
}
|
|
|
|
,setAttribute: function (c, attribute, value)
|
|
{
|
|
if (/^on-\w+/.test (attribute))
|
|
{
|
|
var method = this.getMethod (value);
|
|
|
|
if (method)
|
|
c.object.on (attribute.substr (3), method, this.signalData);
|
|
}
|
|
else if (!/^(id|property)$/.test (attribute))
|
|
{
|
|
this.setProperty (c, attribute, value)
|
|
}
|
|
}
|
|
|
|
,setProperty: function (c, attribute, value)
|
|
{
|
|
var propName = attribute.replace (/-./g, this.replaceFunc);
|
|
var prop = c.klass.Properties[propName];
|
|
|
|
if (!prop)
|
|
{
|
|
console.warn ('Vn.Builder: Attribute \'%s\' not valid for tag \'%s\'',
|
|
attribute, c.node.tagName);
|
|
return;
|
|
}
|
|
if (!value)
|
|
return;
|
|
|
|
switch (prop.type)
|
|
{
|
|
case Boolean:
|
|
value = (/^(true|1)$/i).test (value);
|
|
break;
|
|
case Number:
|
|
value = 0 + new Number (value);
|
|
break;
|
|
case String:
|
|
value = this.translateValue (value);
|
|
break;
|
|
case Function:
|
|
{
|
|
var method = this.getMethod (value);
|
|
value = method ? method.bind (this.signalData) : null;
|
|
break;
|
|
}
|
|
default:
|
|
if (prop.type instanceof Function)
|
|
{
|
|
if (typeof value == 'string')
|
|
value = this.get (value);
|
|
if (!(value instanceof prop.type))
|
|
return;
|
|
}
|
|
else if (prop.enumType)
|
|
value = prop.enumType[value];
|
|
}
|
|
|
|
if (value !== undefined)
|
|
c.object[propName] = value;
|
|
else
|
|
console.warn ('Vn.Builder: Empty attribute \'%s\' on tag \'%s\'',
|
|
attribute, c.node.tagName);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++ Alpha
|
|
|
|
,compile: function (node, dstDocument)
|
|
{
|
|
this.contexts = [];
|
|
this.contextMap = {};
|
|
this.propLinks = [];
|
|
this.childLinks = [];
|
|
this.document = dstDocument ? dstDocument : document;
|
|
|
|
this.compileRec (node, null);
|
|
|
|
for (var i = 0; i < this.propLinks.length; i++)
|
|
{
|
|
var pl = this.propLinks[i];
|
|
var contextId = this.contextMap[pl.value];
|
|
|
|
if (contextId)
|
|
{
|
|
pl.context.objectProps[pl.prop] = contextId;
|
|
continue;
|
|
}
|
|
|
|
var object = this.parentBuilder.get (pl.value);
|
|
|
|
if (object)
|
|
{
|
|
pl.context.props[pl.prop] = object;
|
|
continue;
|
|
}
|
|
|
|
console.warn ('Vn.Builder: Referenced unexistent object with id \'%s\'',
|
|
pl.value);
|
|
}
|
|
|
|
delete this.propLinks;
|
|
|
|
for (var i = 0; i < this.childLinks.length; i++)
|
|
{
|
|
var cl = this.childLinks[i];
|
|
var contextId = this.contextMap[pl.value];
|
|
|
|
if (contextId)
|
|
pl.context.childs.push (contextId);
|
|
else
|
|
console.warn ('Vn.Builder: Referenced unexistent object with id \'%s\'',
|
|
pl.value);
|
|
}
|
|
|
|
delete this.childLinks;
|
|
}
|
|
|
|
,compileRec: function (node)
|
|
{
|
|
var tagName = null;
|
|
|
|
if (node.tagName)
|
|
tagName = node.tagName.toLowerCase ();
|
|
|
|
var nextId = this.contexts.length;
|
|
var context =
|
|
createTextTemplate (nextId, node, tagName)
|
|
|| createObjectTemplate (nextId, node, tagName)
|
|
|| createHtmlTemplate (nextId, node, tagName);
|
|
|
|
this.contexts.push (context);
|
|
return context;
|
|
}
|
|
|
|
,load: function (thisData)
|
|
{
|
|
var contexts = this.contexts;
|
|
var objects = new Array (contexts.length);
|
|
|
|
for (var i = 0; i < contexts.length; i++)
|
|
{
|
|
var context = contexts[i];
|
|
|
|
if (context.func)
|
|
objects[i] = context.func (context);
|
|
}
|
|
|
|
for (var i = 0; i < contexts.length; i++)
|
|
{
|
|
var context = contexts[i];
|
|
|
|
if (context.linkFunc)
|
|
context.linkFunc (context, objects[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a text node template.
|
|
**/
|
|
,createTextTemplate: function (contextId, node, tagName)
|
|
{
|
|
if (tagName === 't')
|
|
var text = _(node.firstChild.textContent);
|
|
else if (!tagName)
|
|
var text = node.textContent;
|
|
else
|
|
return null;
|
|
|
|
return {
|
|
id: contextId,
|
|
text: text,
|
|
func: this.createTextInstance
|
|
};
|
|
}
|
|
|
|
,createTextInstance: function (context)
|
|
{
|
|
return this.document.createTextNode (context.text);
|
|
}
|
|
|
|
/**
|
|
* Creates a object template.
|
|
**/
|
|
,createObjectTemplate: function (contextId, node, tagName)
|
|
{
|
|
var id = null;
|
|
var handler;
|
|
var props = {};
|
|
var objectProps = {};
|
|
var childs = [];
|
|
var events = null;
|
|
var klass = Vn.customTags[tagName];
|
|
|
|
if (!klass)
|
|
return null;
|
|
|
|
var a = node.attributes;
|
|
|
|
for (var i = 0; i < a.length; i++)
|
|
{
|
|
var attribute = a[i].nodeName;
|
|
var value = a[i].nodeValue;
|
|
|
|
if (attribute === 'id')
|
|
{
|
|
this.contextMap[value] = contextId;
|
|
}
|
|
else if ((handler = this.getEventHandler (attribute, value)))
|
|
{
|
|
if (!events)
|
|
events = {};
|
|
|
|
events[attribute.substr (3)] = handler;
|
|
}
|
|
else if (attribute !== 'property')
|
|
{
|
|
this.createPropTemplate (context, klass, props,
|
|
node, attribute, value);
|
|
}
|
|
}
|
|
|
|
var context = {
|
|
id: contextId,
|
|
func: this.createObjectInstance,
|
|
linkFunc: this.createObjectLink,
|
|
klass: klass,
|
|
props: props,
|
|
events: events,
|
|
objectProps: objectProps,
|
|
childs: childs
|
|
};
|
|
|
|
var childs = node.childNodes;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
{
|
|
var child = childs[i];
|
|
var childTagName = child.tagName.toLowerCase ();
|
|
|
|
if (childTagName === 'pointer')
|
|
{
|
|
this.childLinks.push ({
|
|
context: context,
|
|
objectId: child.getAttribute ('object')
|
|
});
|
|
}
|
|
else if (childTagName === 'custom')
|
|
{
|
|
context.custom = child.firstElementChild;
|
|
}
|
|
else
|
|
{
|
|
var childContext = this.compileRec (child);
|
|
var prop = child.getAttribute ('property');
|
|
|
|
if (prop)
|
|
objectProps[prop] = childContext.id;
|
|
else
|
|
childs.push (childContext.id);
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
,createPropTemplate: function (context, klass, props, node, attribute, value)
|
|
{
|
|
var newValue = null;
|
|
var propName = attribute.replace (/-./g, this.replaceFunc);
|
|
var propInfo = klass.Properties[propName];
|
|
|
|
if (!propInfo)
|
|
{
|
|
console.warn ('Vn.Builder: Attribute \'%s\' not valid for tag \'%s\'',
|
|
attribute, node.tagName);
|
|
return;
|
|
}
|
|
if (!value)
|
|
{
|
|
console.warn ('Vn.Builder: Attribute \'%s\' empty on tag \'%s\'',
|
|
attribute, node.tagName);
|
|
return;
|
|
}
|
|
|
|
switch (propInfo.type)
|
|
{
|
|
case Boolean:
|
|
newValue = (/^(true|1)$/i).test (value);
|
|
break;
|
|
case Number:
|
|
newValue = 0 + new Number (value);
|
|
break;
|
|
case String:
|
|
newValue = this.translateValue (value);
|
|
break;
|
|
case Function:
|
|
newValue = this.getMethod (value);
|
|
break;
|
|
case Vn.Enum:
|
|
newValue = propInfo.enumType[value];
|
|
break;
|
|
}
|
|
|
|
if (newValue !== null && newValue !== undefined)
|
|
{
|
|
props[propName] = newValue;
|
|
}
|
|
else if (propInfo.type instanceof Function)
|
|
{
|
|
this.propLinks.push ({
|
|
context: context,
|
|
prop: attribute,
|
|
value: value
|
|
});
|
|
}
|
|
else
|
|
console.warn ('Vn.Builder: Attribute \'%s\' invalid for tag \'%s\'',
|
|
attribute, node.tagName);
|
|
}
|
|
|
|
,createObjectInstance: function (context)
|
|
{
|
|
var object = new context.klass (context.props);
|
|
|
|
var events = context.events;
|
|
|
|
for (var event in events)
|
|
object.on (event,
|
|
events[event].bind (this.signalData));
|
|
|
|
return object;
|
|
}
|
|
|
|
,createObjectLink: function (context, object)
|
|
{
|
|
var objectProps = context.objectProps;
|
|
|
|
if (objectProps)
|
|
for (var prop in objectProps)
|
|
object[prop] = this.objects[objectProps[prop].id];
|
|
|
|
var childs = context.childs;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
object.appendChild (childs[i]);
|
|
|
|
if (context.custom)
|
|
object.loadXml (context.custom);
|
|
}
|
|
|
|
/**
|
|
* Creates a HTML node template.
|
|
**/
|
|
,createHtmlTemplate: function (contextId, node, tagName)
|
|
{
|
|
var handler;
|
|
var events = null;
|
|
var childs = [];
|
|
var htmlNode = this.document.createElement (tagName);
|
|
|
|
var a = node.attributes;
|
|
|
|
for (var i = 0; i < a.length; i++)
|
|
{
|
|
var attribute = a[i].nodeName;
|
|
var value = a[i].nodeValue;
|
|
|
|
if (attribute === 'id')
|
|
{
|
|
this.contextMap[value] = contextId;
|
|
}
|
|
else if ((handler = this.getEventHandler (attribute, value)))
|
|
{
|
|
if (!events)
|
|
events = {};
|
|
|
|
events[attribute.substr (3)] = handler;
|
|
}
|
|
else
|
|
htmlNode.setAttribute (nodeName,
|
|
this.translateValue (nodeValue));
|
|
}
|
|
|
|
var childNodes = node.childNodes;
|
|
|
|
if (childNodes)
|
|
for (var i = 0; i < childNodes.length; i++)
|
|
{
|
|
var childContext = this.compileRec (childNodes[i]);
|
|
childs.push (childContext.id);
|
|
}
|
|
|
|
return {
|
|
id: contextId,
|
|
func: this.createHtmlInstance,
|
|
linkFunc: this.createHtmlLink,
|
|
node: htmlNode,
|
|
events: events,
|
|
childs: childs
|
|
};
|
|
}
|
|
|
|
,createHtmlInstance: function (context)
|
|
{
|
|
var object = new context.node.cloneNode (false);
|
|
|
|
var events = context.events;
|
|
|
|
for (var event in events)
|
|
object.addEventListener (event,
|
|
events[event].bind (this.signalData));
|
|
|
|
return object;
|
|
}
|
|
|
|
,createHtmlLink: function (context, object)
|
|
{
|
|
var childs = context.childs;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
object.appendChild (this.objects[childs[i]]);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++ Utilities
|
|
|
|
,translateValue: function (value)
|
|
{
|
|
var chr = value.charAt (0);
|
|
|
|
if (chr == '_')
|
|
return _(value.substr (1));
|
|
else if (chr == '\\' && value.charAt (1) == '_')
|
|
return value.substr (1);
|
|
|
|
return value;
|
|
}
|
|
|
|
,getMethod: function (value)
|
|
{
|
|
if (this.signalData)
|
|
var methodName = 'this.signalData.'+ value;
|
|
else
|
|
var methodName = value;
|
|
|
|
var method;
|
|
|
|
try {
|
|
method = eval (methodName);
|
|
}
|
|
catch (e)
|
|
{
|
|
method = null;
|
|
}
|
|
|
|
if (method == null)
|
|
console.warn ('Vn.Builder: Function \'%s\' not found',
|
|
value);
|
|
|
|
return method;
|
|
}
|
|
|
|
,getEventHandler: function (attribute, value)
|
|
{
|
|
if (!(/^on-\w+/.test (attribute)))
|
|
return null;
|
|
|
|
return this.getMethod (value);
|
|
}
|
|
|
|
,replaceFunc: function (token)
|
|
{
|
|
return token.charAt(1).toUpperCase ();
|
|
}
|
|
|
|
,setParent: function (parentBuilder)
|
|
{
|
|
this.parentBuilder = parentBuilder;
|
|
|
|
if (parentBuilder)
|
|
this.signalData = parentBuilder.signalData;
|
|
}
|
|
|
|
,$: function (objectId)
|
|
{
|
|
return this.get (objectId);
|
|
}
|
|
|
|
,get: function (objectId)
|
|
{
|
|
var object = this.objectMap[objectId];
|
|
|
|
if (object)
|
|
return object;
|
|
if (this.parentBuilder)
|
|
return this.parentBuilder.get (objectId);
|
|
|
|
return null;
|
|
}
|
|
|
|
,getObjects: function (tagName)
|
|
{
|
|
if (this.tags[tagName])
|
|
return this.tags[tagName];
|
|
|
|
return [];
|
|
}
|
|
|
|
,_destroy: function ()
|
|
{
|
|
for (var tag in this.tags)
|
|
{
|
|
var objects = this.tags[tag];
|
|
|
|
for (var i = 0; i < objects.length; i++)
|
|
objects[i].unref ();
|
|
}
|
|
|
|
this.parent ();
|
|
}
|
|
});
|
|
|