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

650 lines
12 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
,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];
objects[i] = context.func (context.template);
}
var links = this.links;
for (var i = 0; i < links.length; i++)
{
var link = links[i];
objects[link.contextId][link.propName] = objects[link.valueContext];
}
}
,compile: function (node, dstDocument)
{
this.contexts = [];
this.contextMap = {};
this.pointers = [];
this.links = [];
this.document = dstDocument ? dstDocument : document;
this.compileRec (node, null);
for (var i = 0; i < this.pointers.length; i++)
{
var pointerId = this.pointers[i].template;
var refContext = this.contextMap[pointerId];
}
}
,compileRec: function (node, parentContext)
{
var tagName = null;
if (node.tagName)
tagName = node.tagName.toLowerCase ();
var context = {
node: node
,parent: parentContext
,template: template
,id: this.contexts.length
,func: null
};
this.contexts.push (context);
var template =
createTextTemplate (context, node, tagName)
|| createPointerTemplate (context, node, tagName)
|| createObjectTemplate (context, node, tagName)
|| createHtmlTemplate (context, node, tagName);
var id = node.getAttribute ('id');
if (id)
this.contextMap[id] = context;
if (parentContext)
{
var parentProperty = node.getAttribute ('property');
if (!parentProperty && parentContext.template.klass)
parentProperty = parentContext.template.klass.Child;
if (parentProperty)
{
this.links.push ({
contextId: context.id,
propName: propName,
valueContext: valueContext.id
});
}
this.registerLink (parentContext, parentProperty, context);
if (klass.Parent)
this.registerLink (context, klass.Parent, parentContext);
}
var childs = node.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
this.compileRec (childs[i], context);
return context;
}
/**
* Creates a text node template.
**/
,createTextTemplate: function (context, node, tagName)
{
if (tagName === 't')
var text = _(node.firstChild.textContent);
else if (!tagName)
var text = node.textContent;
else
return null;
return
context.func = createTextInstance;
return text;
}
,createTextInstance: function (template)
{
return this.document.createTextNode (template);
}
/**
* Creates a object pointer template.
**/
,createPointerTemplate: function (context, node, tagName)
{
if (tagName !== 'pointer')
return null;
this.pointers.push (context);
return node.getAttribute ('object');
}
,createPointerInstance: function (template)
{
return this.objectMap[template];
}
/**
* Creates a object template.
**/
,createObjectTemplate: function (context, node, tagName)
{
var id = null;
var handler;
var props = {};
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')
{
id = value;
}
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);
}
}
context.func = createObjectInstance;
return {
klass: klass,
events: events,
id: id
};
}
,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.registerLink (context, attribute, value);
}
else
console.warn ('Vn.Builder: Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
,createObjectInstance: function (template)
{
var object = new template.klass (template.props);
var events = template.events;
for (var event in events)
object.on (event,
events[event].bind (this.signalData));
if (template.id)
this.objectMap[id] = object;
return object;
}
/**
* Creates a HTML node template.
**/
,createHtmlTemplate: function (context, node, tagName)
{
var id = null;
var handler;
var events = null;
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')
{
id = value;
}
else if ((handler = this.getEventHandler (attribute, value)))
{
if (!events)
events = {};
events[attribute.substr (3)] = handler;
}
else
htmlNode.setAttribute (nodeName,
this.translateValue (nodeValue));
}
context.func = createHtmlInstance;
return {
node: htmlNode,
events: events,
id: id
};
}
,createHtmlInstance: function (template)
{
var node = new template.node.cloneNode (false);
var events = template.events;
for (var event in events)
node.addEventListener (event,
events[event].bind (this.signalData));
if (template.id)
this.objectMap[id] = node;
return node;
}
//+++++++++++++++++++++++++++++++++++++++++++ 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 ();
}
});