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

625 lines
12 KiB
JavaScript
Raw Normal View History

2016-09-26 09:28:47 +00:00
2017-04-10 15:17:56 +00:00
var VnObject = require ('./object');
2017-03-23 16:20:51 +00:00
var Type = require ('./type');
2016-09-26 09:28:47 +00:00
/**
2015-03-06 23:33:54 +00:00
* Creates a object from a XML specification.
2016-12-20 09:32:17 +00:00
*/
2016-09-26 09:28:47 +00:00
module.exports = new Class
({
2017-04-10 15:17:56 +00:00
Extends: VnObject
,_addedMap: {}
,_contexts: null
2015-10-14 11:51:43 +00:00
,add: function (id, object)
2015-11-09 08:14:33 +00:00
{
this._addedMap[id] = object;
}
,setParent: function (parentResult)
{
this._parentResult = parentResult;
if (parentResult && !this.signalData)
this.signalData = parentResult.builder.signalData;
}
,getMain: function (result)
{
return result.objects[this._mainContext];
}
,getById: function (result, objectId)
{
var index = this._contextMap[objectId];
if (index !== undefined)
return result.objects[index];
2016-10-04 15:27:49 +00:00
var object = this._addedMap[objectId];
if (object !== undefined)
return object;
if (this._parentResult)
return this._parentResult.getById (objectId);
return null;
2015-11-09 08:14:33 +00:00
}
,getByTagName: function (result, tagName)
{
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 [];
}
/**
* Compiles an XML file.
*
* @path String The XML path
* @dstDocument Document The document used to create the nodes
* @return %true on success, %false othersise
2016-12-20 09:32:17 +00:00
*/
,loadXml: function (path, dstDocument)
{
2016-09-26 09:28:47 +00:00
this._path = path;
return this.loadFromXmlDoc (Vn.getXml (path), dstDocument);
}
2016-09-26 09:28:47 +00:00
,loadFromString: function (xmlString, dstDocument)
{
var parser = new DOMParser ();
var xmlDoc = parser.parseFromString (xmlString, 'text/xml');
return this.loadFromXmlDoc (xmlDoc);
}
,loadFromXmlDoc: function (xmlDoc, dstDocument)
{
2015-03-06 23:33:54 +00:00
if (!xmlDoc)
return false;
this._compileInit (dstDocument);
2015-03-06 23:33:54 +00:00
var docElement = xmlDoc.documentElement;
if (docElement.tagName !== 'vn')
{
this._showError ('Malformed XML');
this._contexts = null;
2015-03-06 23:33:54 +00:00
return false;
}
2015-03-06 23:33:54 +00:00
var childs = docElement.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
this._compileNode (childs[i]);
2015-03-06 23:33:54 +00:00
this._compileEnd ();
2015-03-06 23:33:54 +00:00
return true;
}
2015-03-09 08:36:54 +00:00
/**
* Compiles a single DOM node.
*
* @path Node The DOM node
* @dstDocument Document The document used to create the nodes
* @return %true on success, %false othersise
2016-12-20 09:32:17 +00:00
*/
,loadXmlFromNode: function (node, dstDocument)
2015-03-09 08:36:54 +00:00
{
this._compileInit (dstDocument);
this._mainContext = this._compileNode (node).id;
this._compileEnd ();
return true;
2015-03-09 08:36:54 +00:00
}
,load: function ()
{
if (this._contexts === null)
return null;
var contexts = this._contexts;
var len = contexts.length;
var objects = new Array (len);
for (var i = 0; i < len; i++)
2015-03-06 23:33:54 +00:00
{
var context = contexts[i];
if (context.tagName)
objects[i] = this.elementInstantiate (context);
else if (context.klass)
objects[i] = this.objectInstantiate (context);
else
objects[i] = this.textInstantiate (context);
2015-03-06 23:33:54 +00:00
}
return new BuilderResult (this, objects);
}
,link: function (result)
{
var objects = result.objects;
for (var i = this._links.length - 1; i >= 0; i--)
{
var l = this._links[i];
var addedObject = this._addedMap[l.objectId];
2016-10-04 15:27:49 +00:00
if (addedObject)
2016-10-04 15:27:49 +00:00
{
if (l.prop)
objects[l.context.id][l.prop] = addedObject;
else
objects[l.context.id].appendChild (addedObject);
2016-10-04 15:27:49 +00:00
}
else
this._showError ('Referenced unexistent object with id \'%s\'',
l.objectId);
2015-03-06 23:33:54 +00:00
}
var contexts = this._contexts;
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
var object = objects[i];
if (context.tagName)
this.elementLink (context, object, objects, result);
else if (context.klass)
this.objectLink (context, object, objects, result);
}
}
,_compileInit: function (dstDocument)
{
this._path = null;
this._tags = {};
this._contexts = [];
this._contextMap = {};
this._links = [];
this._mainContext = null;
2016-10-16 14:16:08 +00:00
this._doc = dstDocument ? dstDocument : document;
}
,_compileEnd: function (node)
{
for (var i = this._links.length - 1; i >= 0; i--)
2015-03-06 23:33:54 +00:00
{
var l = this._links[i];
2016-10-04 15:27:49 +00:00
var contextId = this._contextMap[l.objectId];
if (contextId != undefined)
{
if (l.prop)
l.context.objectProps[l.prop] = contextId;
else
l.context.childs.push (contextId);
this._links.splice (i, 1);
}
else
2015-03-06 23:33:54 +00:00
{
var object = this._addedMap[l.objectId];
if (!object && this._parentResult)
object = this._parentResult.getById (l.objectId);
if (object)
{
l.context.props[l.prop] = object;
this._links.splice (i, 1);
}
2015-03-06 23:33:54 +00:00
}
}
}
,_compileNode: function (node)
{
var context = null;
var tagName = null;
2015-11-09 08:14:33 +00:00
if (node.nodeType === Node.ELEMENT_NODE)
tagName = node.tagName.toLowerCase ();
else if (node.nodeType !== Node.TEXT_NODE
|| /^[\n\r\t]*$/.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)
2015-11-09 08:14:33 +00:00
{
var nodeId = node.getAttribute ('id');
if (nodeId)
this._contextMap[nodeId] = context.id;
2015-11-09 08:14:33 +00:00
var tags = this._tags[tagName];
if (!tags)
this._tags[tagName] = tags = [];
tags.push (context.id);
2015-11-09 08:14:33 +00:00
}
this._contexts.push (context);
return context;
}
/**
* Creates a text node context.
2016-12-20 09:32:17 +00:00
*/
,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)
{
2016-10-16 14:16:08 +00:00
return this._doc.createTextNode (context.text);
}
/**
* Creates a object context.
2016-12-20 09:32:17 +00:00
*/
,objectCompile: function (node, tagName)
{
2016-09-26 09:28:47 +00:00
var klass = vnCustomTags[tagName];
if (!klass)
return null;
var props = {};
var objectProps = {};
var childs = [];
var events = {};
var context = {
klass: klass,
props: props,
objectProps: objectProps,
childs: childs,
events: events,
custom: null
};
var a = node.attributes;
2015-11-09 08:14:33 +00:00
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
2015-11-09 08:14:33 +00:00
2015-11-19 13:57:23 +00:00
if (this._isEvent (attribute))
{
2015-11-19 13:57:23 +00:00
var handler = this._getMethod (value)
if (handler)
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++)
2015-07-07 15:27:47 +00:00
{
var child = childNodes[i];
2017-04-07 11:00:33 +00:00
var childContext = null;
var childTagName = null;
2017-04-07 11:00:33 +00:00
if (child.nodeType === Node.ELEMENT_NODE)
childTagName = child.tagName.toLowerCase ();
if (childTagName === 'pointer')
{
this._addLink (context, null, child.getAttribute ('object'));
}
else if (childTagName === 'custom')
{
context.custom = child;
}
else if (childContext = this._compileNode (child))
{
2017-04-07 11:00:33 +00:00
var prop = null;
if (childTagName)
prop = child.getAttribute ('property');
if (prop)
{
prop = prop.replace (/-./g, this._replaceFunc);
objectProps[prop] = childContext.id;
}
else
childs.push (childContext.id);
}
2015-07-07 15:27:47 +00:00
}
return context;
2015-07-07 15:27:47 +00:00
}
,propCompile: function (context, klass, props, node, attribute, value)
{
2015-12-02 17:26:58 +00:00
var isLink = false;
var newValue = null;
var propName = attribute.replace (/-./g, this._replaceFunc);
var propInfo = klass.Properties[propName];
if (!propInfo)
2015-07-03 05:49:45 +00:00
{
this._showError ('Attribute \'%s\' not valid for tag \'%s\'',
attribute, node.tagName);
2015-07-03 05:49:45 +00:00
return;
}
if (!value)
{
this._showError ('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);
2015-03-06 23:33:54 +00:00
break;
case Function:
2015-12-02 17:26:58 +00:00
var method = this._getMethod (value);
newValue = method ? method.bind (this.signalData) : null;
break;
2017-03-23 16:20:51 +00:00
case Type:
newValue = window[value];
break;
default:
if (propInfo.enumType)
newValue = propInfo.enumType[value];
2015-12-02 17:26:58 +00:00
else if (propInfo.type instanceof Function)
isLink = true;
}
2015-12-02 17:26:58 +00:00
if (isLink)
this._addLink (context, propName, value);
2015-12-02 17:26:58 +00:00
else if (newValue !== null && newValue !== undefined)
props[propName] = newValue;
2015-07-10 12:30:08 +00:00
else
this._showError ('Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
,objectInstantiate: function (context)
{
return new context.klass ();
}
,objectLink: function (context, object, objects, res)
{
object.setProperties (context.props);
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], this.signalData);
if (context.custom)
object.loadXml (res, context.custom);
}
/**
* Creates a HTML node context.
2016-12-20 09:32:17 +00:00
*/
,elementCompile: function (node, tagName)
{
var attributes = {};
var childs = [];
var events = {};
var handler;
var a = node.attributes;
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
2015-11-19 13:57:23 +00:00
if (this._isEvent (attribute))
{
var handler = this._getMethod (value);
if (handler)
events[attribute.substr (3)] = handler;
}
else if (attribute !== 'id')
attributes[attribute] = this._translateValue (value);
}
2015-10-14 11:51:43 +00:00
var childContext;
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
if (childContext = this._compileNode (childNodes[i]))
childs.push (childContext.id);
return {
tagName: tagName,
attributes: attributes,
childs: childs,
events: events
};
}
,elementInstantiate: function (context)
{
2016-10-16 14:16:08 +00:00
return this._doc.createElement (context.tagName);
}
,elementLink: function (context, object, objects)
{
var attributes = context.attributes;
for (var attribute in attributes)
object.setAttribute (attribute, attributes[attribute]);
2015-10-14 11:51:43 +00:00
var childs = context.childs;
for (var i = 0; i < childs.length; i++)
{
var child = objects[childs[i]];
if (child instanceof Htk.Widget)
2016-10-16 14:16:08 +00:00
child = child.node;
if (child instanceof Node)
object.appendChild (child);
}
var events = context.events;
for (var event in events)
object.addEventListener (event,
events[event].bind (this.signalData));
}
,_showError: function (error)
{
var path = this._path ? this._path : 'Node';
var logArgs = ['Vn.Builder: %s: '+ error, path];
for (var i = 1; i < arguments.length; i++)
logArgs.push (arguments[i]);
2017-03-23 16:20:51 +00:00
console.warn.apply (console, logArgs);
}
,_addLink: function (context, prop, objectId)
{
this._links.push ({
context: context
,prop: prop
,objectId: objectId
});
}
,_translateValue: function (value)
2015-10-14 11:51:43 +00:00
{
var chr = value.charAt (0);
if (chr === '_')
2015-10-14 11:51:43 +00:00
return _(value.substr (1));
else if (chr === '\\' && value.charAt (1) === '_')
2015-10-14 11:51:43 +00:00
return value.substr (1);
return value;
}
,_getMethod: function (value)
2015-10-14 11:51:43 +00:00
{
if (this.signalData)
2016-08-22 10:41:05 +00:00
var method = this.signalData[value];
2015-10-14 11:51:43 +00:00
else
2016-08-22 10:41:05 +00:00
var method = window[value];
2015-10-14 11:51:43 +00:00
2015-11-19 13:57:23 +00:00
if (method === undefined)
this._showError ('Function \'%s\' not found', value);
2015-10-14 11:51:43 +00:00
return method;
}
2015-11-19 13:57:23 +00:00
,_isEvent: function (attribute)
{
return /^on-\w+/.test (attribute);
2015-10-14 11:51:43 +00:00
}
,_replaceFunc: function (token)
2015-10-14 11:51:43 +00:00
{
return token.charAt(1).toUpperCase ();
}
});
var BuilderResult = new Class
({
2017-04-10 15:17:56 +00:00
Extends: VnObject
,initialize: function (builder, objects)
{
this.builder = builder;
this.objects = objects;
}
,getMain: function ()
{
return this.builder.getMain (this);
}
2015-07-03 05:49:45 +00:00
,$: function (objectId)
{
return this.builder.getById (this, objectId);
2015-07-03 05:49:45 +00:00
}
2015-11-09 08:14:33 +00:00
,getById: function (objectId)
{
return this.builder.getById (this, objectId);
}
2015-11-09 08:14:33 +00:00
,getByTagName: function (tagName)
{
return this.builder.getByTagName (this, tagName);
}
,link: function ()
{
this.builder.link (this);
}
2015-08-17 18:02:14 +00:00
,_destroy: function ()
{
var objects = this.objects;
for (var i = 0; i < objects.length; i++)
2017-04-10 15:17:56 +00:00
if (objects[i] instanceof VnObject)
objects[i].unref ();
2015-08-17 18:02:14 +00:00
this.parent ();
}
});