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

639 lines
14 KiB
JavaScript
Raw Normal View History

2016-09-26 09:28:47 +00:00
2022-05-24 10:18:44 +00:00
var Object = require('./object');
2016-09-26 09:28:47 +00:00
/**
2015-03-06 23:33:54 +00:00
* Creates a object from a XML specification.
2022-05-24 10:18:44 +00:00
*/
module.exports = new Class({
2016-09-26 09:28:47 +00:00
Extends: Object
,_addedMap: {}
,_contexts: null
2015-10-14 11:51:43 +00:00
2022-05-24 10:18:44 +00:00
,add: function(id, object) {
this._addedMap[id] = object;
}
2022-05-24 10:18:44 +00:00
,setParent: function(parentResult) {
this._parentResult = parentResult;
if (parentResult && !this.signalData)
this.signalData = parentResult.builder.signalData;
}
2022-05-24 10:18:44 +00:00
,getMain: function(result) {
return result.objects[this._mainContext];
}
2022-05-24 10:18:44 +00:00
,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)
2022-05-24 10:18:44 +00:00
return this._parentResult.getById(objectId);
return null;
2015-11-09 08:14:33 +00:00
}
2022-05-24 10:18:44 +00:00
,getByTagName: function(result, tagName) {
var tags = this._tags[tagName];
2022-05-24 10:18:44 +00:00
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
2022-05-24 10:18:44 +00:00
*/
,loadXml: function(path, dstDocument) {
2016-09-26 09:28:47 +00:00
this._path = path;
2022-05-24 10:18:44 +00:00
return this.loadFromXmlDoc(Vn.getXml(path), dstDocument);
2016-09-26 09:28:47 +00:00
}
2022-05-24 10:18:44 +00:00
,loadFromString: function(xmlString, dstDocument) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xmlString, 'text/xml');
return this.loadFromXmlDoc(xmlDoc, dstDocument);
2016-09-26 09:28:47 +00:00
}
2022-05-24 10:18:44 +00:00
,loadFromXmlDoc: function(xmlDoc, dstDocument, scope) {
2015-03-06 23:33:54 +00:00
if (!xmlDoc)
return false;
2022-05-24 10:18:44 +00:00
this._compileInit(dstDocument, scope);
2015-03-06 23:33:54 +00:00
var docElement = xmlDoc.documentElement;
2022-05-24 10:18:44 +00:00
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++)
2022-05-24 10:18:44 +00:00
this._compileNode(childs[i]);
2015-03-06 23:33:54 +00:00
2022-05-24 10:18:44 +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
2022-05-24 10:18:44 +00:00
*/
,loadXmlFromNode: function(node, dstDocument, scope) {
this._compileInit(dstDocument, scope);
this._mainContext = this._compileNode(node).id;
this._compileEnd();
return true;
2015-03-09 08:36:54 +00:00
}
2022-05-24 10:18:44 +00:00
,load: function() {
if (this._contexts === null)
return null;
var contexts = this._contexts;
var len = contexts.length;
2022-05-24 10:18:44 +00:00
var objects = new Array(len);
2022-05-24 10:18:44 +00:00
for (var i = 0; i < len; i++) {
var context = contexts[i];
if (context.tagName)
2022-05-24 10:18:44 +00:00
objects[i] = this.elementInstantiate(context);
else if (context.klass)
2022-05-24 10:18:44 +00:00
objects[i] = this.objectInstantiate(context);
else
2022-05-24 10:18:44 +00:00
objects[i] = this.textInstantiate(context);
2015-03-06 23:33:54 +00:00
}
2022-05-24 10:18:44 +00:00
return new BuilderResult(this, objects);
}
2022-05-24 10:18:44 +00:00
,link: function(result, self, scope) {
var objects = result.objects;
2022-05-24 10:18:44 +00:00
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
2022-05-24 10:18:44 +00:00
if (addedObject) {
if (l.prop)
objects[l.context.id][l.prop] = addedObject;
else
2022-05-24 10:18:44 +00:00
objects[l.context.id].appendChild(addedObject);
} else
this._showError('Referenced unexistent object with id \'%s\'',
l.objectId);
2015-03-06 23:33:54 +00:00
}
2022-05-24 10:18:44 +00:00
this.linkExpr(result, self, scope);
var contexts = this._contexts;
2022-05-24 10:18:44 +00:00
for (var i = 0; i < contexts.length; i++) {
var context = contexts[i];
var object = objects[i];
if (context.tagName)
2022-05-24 10:18:44 +00:00
this.elementLink(context, object, objects, result);
else if (context.klass)
2022-05-24 10:18:44 +00:00
this.objectLink(context, object, objects, result);
}
}
2022-05-24 10:18:44 +00:00
,fnExpr(expr) {
return new Function(this._scopeArgs,
'"use strict"; return ' + expr + ';'
);
}
2022-05-24 10:18:44 +00:00
,matchExpr(value) {
const match = /^{{(.*)}}$/.exec(value);
if (!match) return null;
return this.fnExpr(match[1]);
}
,linkExpr(result, self, scope) {
const contexts = this._contexts;
const objects = result.objects;
let args = [_]
if (scope) args = args.concat(scope);
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
const object = objects[i];
if (context.exprs) {
const values = [];
for (expr of context.exprs) {
let value = undefined;
try {
value = expr.apply(self, args);
} catch (e) {
console.warn('Expression error:', e.message);
continue;
}
values.push(value);
}
let k = 0;
const text = context.text.replace(/{{\d+}}/g, function() {
return values[k++];
});
object.textContent = text;
} else {
const dynProps = context.dynProps;
for (const prop in dynProps) {
let value = undefined;
try {
value = dynProps[prop].apply(self, args);
} catch (e) {
console.warn('Expression error:', e.message);
continue;
}
if (context.tagName)
object.setAttribute(prop, value);
else
object[prop] = value;
}
}
}
}
,_compileInit: function(dstDocument, scope) {
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;
2022-05-24 10:18:44 +00:00
this._scope = ['_'];
if (scope)
this._scope = this._scope.concat(scope);
this._scopeArgs = this._scope.join(',');
}
2022-05-24 10:18:44 +00:00
,_compileEnd: function() {
for (var i = this._links.length - 1; i >= 0; i--) {
var l = this._links[i];
2016-10-04 15:27:49 +00:00
var contextId = this._contextMap[l.objectId];
2022-05-24 10:18:44 +00:00
if (contextId != undefined) {
if (l.prop)
l.context.objectProps[l.prop] = contextId;
else
2022-05-24 10:18:44 +00:00
l.context.childs.push(contextId);
2022-05-24 10:18:44 +00:00
this._links.splice(i, 1);
} else {
var object = this._addedMap[l.objectId];
if (!object && this._parentResult)
2022-05-24 10:18:44 +00:00
object = this._parentResult.getById(l.objectId);
2022-05-24 10:18:44 +00:00
if (object) {
l.context.props[l.prop] = object;
2022-05-24 10:18:44 +00:00
this._links.splice(i, 1);
}
2015-03-06 23:33:54 +00:00
}
}
}
2022-05-24 10:18:44 +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)
2022-05-24 10:18:44 +00:00
tagName = node.tagName.toLowerCase();
else if (node.nodeType !== Node.TEXT_NODE
2022-05-24 10:18:44 +00:00
|| /^[\n\r\t]*$/.test(node.textContent))
return null;
var context =
2022-05-24 10:18:44 +00:00
this.textCompile(node, tagName)
|| this.objectCompile(node, tagName)
|| this.elementCompile(node, tagName);
context.id = this._contexts.length;
2022-05-24 10:18:44 +00:00
if (tagName) {
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 = [];
2022-05-24 10:18:44 +00:00
tags.push(context.id);
2015-11-09 08:14:33 +00:00
}
2022-05-24 10:18:44 +00:00
this._contexts.push(context);
return context;
}
/**
* Creates a text node context.
2022-05-24 10:18:44 +00:00
*/
,textCompile: function(node, tagName) {
if (!tagName) {
let text = node.textContent;
if (/{{.*}}/.test(text)) {
let i = 0;
const self = this;
const exprs = [];
text = text.replace(/{{((?:(?!}}).)*)}}/g, function(match, capture) {
exprs.push(self.fnExpr(capture));
return `{{${i++}}}`;
});
return {text, exprs};
} else
return {text};
} else if (tagName === 't')
return {text: _(node.firstChild.textContent)};
else
return null;
}
2022-05-24 10:18:44 +00:00
,textInstantiate: function(context) {
return this._doc.createTextNode(context.exprs ? '' : context.text);
}
/**
* Creates a object context.
2022-05-24 10:18:44 +00:00
*/
,objectCompile: function(node, tagName) {
2016-09-26 09:28:47 +00:00
var klass = vnCustomTags[tagName];
if (!klass)
return null;
var props = {};
2022-05-24 10:18:44 +00:00
var dynProps = {};
var objectProps = {};
var childs = [];
var events = {};
var context = {
klass: klass,
props: props,
2022-05-24 10:18:44 +00:00
dynProps: dynProps,
objectProps: objectProps,
childs: childs,
events: events,
custom: null
};
var a = node.attributes;
2015-11-09 08:14:33 +00:00
2022-05-24 10:18:44 +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
2022-05-24 10:18:44 +00:00
if (this._isEvent(attribute)) {
var handler = this._getMethod(value)
2015-11-19 13:57:23 +00:00
if (handler)
2022-05-24 10:18:44 +00:00
events[attribute.substr(3)] = handler;
} else if (!/^(id|property)$/.test(attribute)) {
this.propCompile(context, klass, props, dynProps,
node, attribute, value);
}
}
var childNodes = node.childNodes;
if (childNodes)
2022-05-24 10:18:44 +00:00
for (var i = 0; i < childNodes.length; i++) {
var child = childNodes[i];
var isElement = child.nodeType === Node.ELEMENT_NODE;
2022-05-24 10:18:44 +00:00
var childTagName = isElement ? child.tagName.toLowerCase() : null;
var childContext;
2022-05-24 10:18:44 +00:00
if (childTagName === 'pointer') {
this._addLink(context, null, child.getAttribute('object'));
} else if (childTagName === 'custom') {
context.custom = child;
2022-05-24 10:18:44 +00:00
} else if (childContext = this._compileNode(child)) {
var prop = isElement ? child.getAttribute('property') : null;
2022-05-24 10:18:44 +00:00
if (prop) {
prop = prop.replace(/-./g, this._replaceFunc);
objectProps[prop] = childContext.id;
2022-05-24 10:18:44 +00:00
} else
childs.push(childContext.id);
}
2015-07-07 15:27:47 +00:00
}
return context;
2015-07-07 15:27:47 +00:00
}
2022-05-24 10:18:44 +00:00
,propCompile: function(context, klass, props, dynProps, node, attribute, value) {
2015-12-02 17:26:58 +00:00
var isLink = false;
var newValue = null;
2022-05-24 10:18:44 +00:00
var propName = attribute.replace(/-./g, this._replaceFunc);
var propInfo = klass.Properties[propName];
2022-05-24 10:18:44 +00:00
if (!propInfo) {
this._showError('Attribute \'%s\' not valid for tag \'%s\'',
attribute, node.tagName);
2015-07-03 05:49:45 +00:00
return;
}
2022-05-24 10:18:44 +00:00
if (!value) {
this._showError('Attribute \'%s\' empty on tag \'%s\'',
attribute, node.tagName);
return;
}
2022-05-24 10:18:44 +00:00
const expr = this.matchExpr(value);
if (expr) {
dynProps[propName] = expr;
} else {
switch (propInfo.type) {
case Boolean:
2022-05-24 10:18:44 +00:00
newValue = (/^(true|1)$/i).test(value);
break;
case Number:
2022-05-24 10:18:44 +00:00
newValue = 0 + new Number(value);
break;
case String:
2022-05-24 10:18:44 +00:00
newValue = this._translateValue(value);
2015-03-06 23:33:54 +00:00
break;
case Function:
2022-05-24 10:18:44 +00:00
var method = this._getMethod(value);
newValue = method ? method.bind(this.signalData) : null;
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;
2022-05-24 10:18:44 +00:00
}
if (isLink)
this._addLink(context, propName, value);
else if (newValue !== null && newValue !== undefined)
props[propName] = newValue;
else
this._showError('Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
}
2022-05-24 10:18:44 +00:00
,objectInstantiate: function(context) {
return new context.klass();
}
2022-05-24 10:18:44 +00:00
,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++)
2022-05-24 10:18:44 +00:00
object.appendChild(objects[childs[i]]);
var events = context.events;
for (var event in events)
2022-05-24 10:18:44 +00:00
object.on(event, events[event], this.signalData);
if (context.custom)
2022-05-24 10:18:44 +00:00
object.loadXml(res, context.custom);
}
/**
* Creates a HTML node context.
2022-05-24 10:18:44 +00:00
*/
,elementCompile: function(node, tagName) {
var attributes = {};
2022-05-24 10:18:44 +00:00
var dynProps = {};
var childs = [];
var events = {};
var handler;
var a = node.attributes;
2022-05-24 10:18:44 +00:00
for (var i = 0; i < a.length; i++) {
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
2022-05-24 10:18:44 +00:00
const expr = this.matchExpr(value);
if (expr) {
dynProps[attribute] = expr;
} else if (this._isEvent(attribute)) {
var handler = this._getMethod(value);
2015-11-19 13:57:23 +00:00
if (handler)
2022-05-24 10:18:44 +00:00
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++)
2022-05-24 10:18:44 +00:00
if (childContext = this._compileNode(childNodes[i]))
childs.push(childContext.id);
return {
2022-05-24 10:18:44 +00:00
tagName,
attributes,
dynProps,
childs,
events
};
}
2022-05-24 10:18:44 +00:00
,elementInstantiate: function(context) {
return this._doc.createElement(context.tagName);
}
2022-05-24 10:18:44 +00:00
,elementLink: function(context, object, objects) {
var attributes = context.attributes;
for (var attribute in attributes)
2022-05-24 10:18:44 +00:00
object.setAttribute(attribute, attributes[attribute]);
2015-10-14 11:51:43 +00:00
var childs = context.childs;
2022-05-24 10:18:44 +00:00
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)
2022-05-24 10:18:44 +00:00
object.appendChild(child);
}
var events = context.events;
for (var event in events)
2022-05-24 10:18:44 +00:00
object.addEventListener(event,
events[event].bind(this.signalData));
}
2022-05-24 10:18:44 +00:00
,_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++)
2022-05-24 10:18:44 +00:00
logArgs.push(arguments[i]);
2022-05-24 10:18:44 +00:00
console.warn.apply(null, logArgs);
}
2022-05-24 10:18:44 +00:00
,_addLink: function(context, prop, objectId) {
this._links.push({
context: context
,prop: prop
,objectId: objectId
});
}
2022-05-24 10:18:44 +00:00
,_translateValue: function(value) {
var chr = value.charAt(0);
2015-10-14 11:51:43 +00:00
if (chr === '_')
2022-05-24 10:18:44 +00:00
return _(value.substr(1));
else if (chr === '\\' && value.charAt(1) === '_')
return value.substr(1);
2015-10-14 11:51:43 +00:00
return value;
}
2022-05-24 10:18:44 +00:00
,_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)
2022-05-24 10:18:44 +00:00
this._showError('Function \'%s\' not found', value);
2015-10-14 11:51:43 +00:00
return method;
}
2022-05-24 10:18:44 +00:00
,_isEvent: function(attribute) {
return /^on-\w+/.test(attribute);
2015-10-14 11:51:43 +00:00
}
2022-05-24 10:18:44 +00:00
,_replaceFunc: function(token) {
return token.charAt(1).toUpperCase();
2015-10-14 11:51:43 +00:00
}
});
2022-05-24 10:18:44 +00:00
var BuilderResult = new Class({
2016-09-26 09:28:47 +00:00
Extends: Object
2022-05-24 10:18:44 +00:00
,initialize: function(builder, objects) {
this.builder = builder;
this.objects = objects;
}
2022-05-24 10:18:44 +00:00
,getMain: function() {
return this.builder.getMain(this);
}
2015-07-03 05:49:45 +00:00
2022-05-24 10:18:44 +00:00
,$: function(objectId) {
return this.builder.getById(this, objectId);
2015-07-03 05:49:45 +00:00
}
2022-05-24 10:18:44 +00:00
,getById: function(objectId) {
return this.builder.getById(this, objectId);
}
2022-05-24 10:18:44 +00:00
,getByTagName: function(tagName) {
return this.builder.getByTagName(this, tagName);
}
2022-05-24 10:18:44 +00:00
,link: function(self, scope) {
this.builder.link(this, self, scope);
}
2022-05-24 10:18:44 +00:00
,_destroy: function() {
var objects = this.objects;
for (var i = 0; i < objects.length; i++)
2016-09-26 09:28:47 +00:00
if (objects[i] instanceof Object)
2022-05-24 10:18:44 +00:00
objects[i].unref();
2015-08-17 18:02:14 +00:00
2022-05-24 10:18:44 +00:00
this.parent();
}
});