0
1
Fork 0
hedera-web-mindshore/web/js/vn/builder.js

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 ();
}
});