hedera-web/web/js/vn/builder-new.js

576 lines
11 KiB
JavaScript
Executable File

/**
* Creates a object from a XML specification.
**/
Vn.Builder = new Class
({
Extends: Vn.Object
,addedMap: {}
,add: function (id, object)
{
this.addedMap[id] = object;
}
,setParent: function (parentBuilder)
{
this.parentBuilder = parentBuilder;
if (parentBuilder && !this.signalData)
this.signalData = parentBuilder.builder.signalData;
}
,getMain: function (result)
{
return result.objects[this.mainContext.id];
}
,getById: function (objectId, result)
{
var index = this.contextMap[objectId];
if (index !== undefined)
return result.objects[index];
else if (this.addedMap[objectId])
return this.addedMap[objectId];
else if (this.parentBuilder)
this.parentBuilder.getById (objectId);
return null;
}
,getByTagName: function (tagName, result)
{
var tags = this.tags[tagName];
if (tags)
{
var arr = new Array (tags.length);
for (var i = 0; i < tags.length; i++)
arr[i] = result.objects[tags[i]];
return arr;
}
return [];
}
,loadXml: function (path, dstDocument)
{
var xmlDoc = Vn.getXml (path);
if (!xmlDoc)
return false;
var docElement = xmlDoc.documentElement;
if (docElement.tagName !== 'vn')
return false;
this._compileInit (dstDocument);
var childs = docElement.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
this._compileNode (childs[i]);
this._compileEnd ();
return true;
}
,loadXmlFromNode: function (node, dstDocument)
{
this._compileInit (dstDocument);
this.mainContext = this._compileNode (node);
this._compileEnd ();
}
,load: function ()
{
var contexts = this.contexts;
var len = contexts.length;
var objects = new Array (len);
for (var i = 0; i < len; i++)
{
var context = contexts[i];
objects[i] =
this.textInstantiate (context)
|| this.objectInstantiate (context)
|| this.elementInstantiate (context);
}
var res = new BuilderResult (this, objects);
var addedObject;
for (var i = this.propLinks.length - 1; i >= 0; i--)
{
var l = this.propLinks[i];
if (addedObject = this.addedMap[l.value])
objects[l.context.id][l.prop] = addedObject;
}
for (var i = this.childLinks.length - 1; i >= 0; i--)
{
var l = this.childLinks[i];
if (addedObject = this.addedMap[l.value])
objects[l.context.id].appendChild (addedObject);
}
for (var i = 0; i < len; i++)
{
var context = contexts[i];
var object = objects[i];
this.objectLink (context, object, objects, res) ||
this.elementLink (context, object, objects, res);
}
return res;
}
,_compileInit: function (dstDocument)
{
this.tags = {};
this.contexts = [];
this.contextMap = {};
this.propLinks = [];
this.childLinks = [];
this.mainContext = null;
this.document = dstDocument ? dstDocument : document;
}
,_compileEnd: function (node)
{
var contextId;
for (var i = this.propLinks.length - 1; i >= 0; i--)
{
var l = this.propLinks[i];
if (contextId = this.contextMap[l.value])
{
l.context.objectProps[l.prop] = contextId;
this.propLinks.splice (i, 1);
}
else if (this.parentBuilder)
{
var object = this.parentBuilder.getById (l.value);
if (object)
pl.context.props[pl.prop] = object;
}
}
for (var i = this.childLinks.length - 1; i >= 0; i--)
{
var l = this.childLinks[i];
if (contextId = this.contextMap[l.value])
{
l.context.childs.push (contextId);
this.childLinks.splice (i, 1);
}
}
}
,_compileNode: function (node)
{
var context = null;
var tagName = null;
if (node.nodeType === Node.ELEMENT_NODE)
tagName = node.tagName.toLowerCase ();
else if (node.nodeType !== Node.TEXT_NODE
|| /^\s*$/.test (node.textContent))
return null;
var context =
this.textCompile (node, tagName)
|| this.objectCompile (node, tagName)
|| this.elementCompile (node, tagName);
context.id = this.contexts.length;
if (tagName)
{
var nodeId = node.getAttribute ('id');
if (nodeId)
this.contextMap[nodeId] = context.id;
var tags = this.tags[tagName];
if (!tags)
this.tags[tagName] = tags = [];
tags.push (context.id);
}
this.contexts.push (context);
return context;
}
/**
* Creates a text node context.
**/
,textCompile: function (node, tagName)
{
if (!tagName)
var text = node.textContent;
else if (tagName === 't')
var text = _(node.firstChild.textContent);
else
return null;
return {text: text};
}
,textInstantiate: function (context)
{
if (!context.text)
return null;
return this.document.createTextNode (context.text);
}
/**
* Creates a object context.
**/
,objectCompile: function (node, tagName)
{
var handler;
var props = {};
var objectProps = {};
var childs = [];
var events = {};
var klass = Vn.customTags[tagName];
if (!klass)
return null;
var context = {
klass: klass,
props: props,
events: events,
objectProps: objectProps,
childs: childs
};
var a = node.attributes;
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if ((handler = this._getEventHandler (attribute, value)))
{
events[attribute.substr (3)] = handler;
}
else if (!/^(id|property)$/.test (attribute))
{
this.propCompile (context, klass, props,
node, attribute, value);
}
}
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
{
var child = childNodes[i];
var childTagName = null;
if (child.tagName)
childTagName = child.tagName.toLowerCase ();
if (childTagName === 'pointer')
{
this.childLinks.push ({
context: context,
objectId: child.getAttribute ('object')
});
}
else if (childTagName === 'custom')
{
context.custom = child;
}
else
{
var childContext = this._compileNode (child);
if (!childContext)
continue;
var prop = null;
if (child.getAttribute)
prop = child.getAttribute ('property');
if (prop)
objectProps[prop] = childContext.id;
else
childs.push (childContext.id);
}
}
return context;
}
,propCompile: 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;
default:
if (propInfo.enumType)
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);
}
,objectInstantiate: function (context)
{
if (!context.klass)
return null;
return new context.klass (context.props);
}
,objectLink: function (context, object, objects, res)
{
if (!context.klass)
return null;
var objectProps = context.objectProps;
for (var prop in objectProps)
object[prop] = objects[objectProps[prop]];
var childs = context.childs;
for (var i = 0; i < childs.length; i++)
object.appendChild (objects[childs[i]]);
var events = context.events;
for (var event in events)
object.on (event,
events[event].bind (this.signalData));
if (context.custom)
object.loadXml (res, context.custom);
}
/**
* Creates a HTML node context.
**/
,elementCompile: function (node, tagName)
{
var handler;
var events = {};
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 ((handler = this._getEventHandler (attribute, value)))
{
events[attribute.substr (3)] = handler;
}
else if (attribute !== 'id')
htmlNode.setAttribute (attribute,
this._translateValue (value));
}
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
{
var childContext = this._compileNode (childNodes[i]);
if (childContext)
childs.push (childContext.id);
}
return {
node: htmlNode,
events: events,
childs: childs
};
}
,elementInstantiate: function (context)
{
if (!context.node)
return null;
return context.node.cloneNode (false);
}
,elementLink: function (context, object, objects)
{
if (!context.node)
return null;
var childs = context.childs;
for (var i = 0; i < childs.length; i++)
{
var child = objects[childs[i]];
if (child instanceof Htk.Widget)
object.appendChild (child.getNode ());
else if (child instanceof Node)
object.appendChild (child);
}
var events = context.events;
for (var event in events)
object.addEventListener (event,
events[event].bind (this.signalData));
}
,_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 ();
}
});
var BuilderResult = new Class
({
Extends: Vn.Object
,initialize: function (builder, objects)
{
this.builder = builder;
this.objects = objects;
}
,getMain: function ()
{
return this.builder.getMain (this);
}
,$: function (objectId)
{
return this.builder.getById (objectId, this);
}
,getById: function (objectId)
{
return this.builder.getById (objectId, this);
}
,getByTagName: function (tagName)
{
return this.builder.getByTagName (tagName, this);
}
,_destroy: function ()
{
var objects = this.objects;
for (var i = 0; i < objects.length; i++)
if (objects[i].unref)
objects[i].unref ();
this.parent ();
}
});