hedera-web/js/vn/builder.js

265 lines
5.6 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-10-20 12:06:16 +00:00
var Scope = require ('./scope');
2017-10-20 17:09:06 +00:00
var kebabToCamel = require ('./string-util').kebabToCamel;
2016-09-26 09:28:47 +00:00
2017-10-20 12:06:16 +00:00
var CompilerObject = require ('./compiler-object');
var CompilerElement = require ('./compiler-element');
var CompilerText = require ('./compiler-text');
var regCompilers = [
CompilerObject,
CompilerElement,
CompilerText
];
2017-10-18 16:01:21 +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
2017-10-20 12:06:16 +00:00
,_contexts: null
/**
* Compiles an XML file.
*
2017-10-18 16:01:21 +00:00
* @param {String} path The XML path
* @return {Boolean} %true on success, %false othersise
2016-12-20 09:32:17 +00:00
*/
2017-10-18 16:01:21 +00:00
,compileFile: function (path)
{
2016-09-26 09:28:47 +00:00
this._path = path;
2017-10-18 16:01:21 +00:00
return this.compileDocument (Vn.getXml (path));
2016-09-26 09:28:47 +00:00
}
2017-10-18 16:01:21 +00:00
/**
* Compiles an XML string.
*
* @param {String} xmlString The XML string
* @return {Boolean} %true on success, %false othersise
*/
,compileString: function (xmlString)
2016-09-26 09:28:47 +00:00
{
var parser = new DOMParser ();
2017-10-18 16:01:21 +00:00
var doc = parser.parseFromString (xmlString, 'text/xml');
return this.compileDocument (doc);
2016-09-26 09:28:47 +00:00
}
2017-10-18 16:01:21 +00:00
/**
* Compiles a XML document.
*
* @param {Document} doc The DOM document
* @return {Boolean} %true on success, %false othersise
*/
,compileDocument: function (doc)
2016-09-26 09:28:47 +00:00
{
2017-10-18 16:01:21 +00:00
if (!doc)
2015-03-06 23:33:54 +00:00
return false;
2017-10-20 12:06:16 +00:00
this._preCompile ();
2017-10-18 16:01:21 +00:00
var docElement = doc.documentElement;
2015-03-06 23:33:54 +00:00
if (docElement.tagName !== 'vn')
{
2017-10-20 12:06:16 +00:00
this.showError ('The toplevel tag should be named \'vn\'');
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++)
2017-10-20 12:06:16 +00:00
this._compile (childs[i]);
2015-03-06 23:33:54 +00:00
2017-10-20 12:06:16 +00:00
this._postCompile ();
2015-03-06 23:33:54 +00:00
return true;
}
2015-03-09 08:36:54 +00:00
/**
* Compiles a single DOM node.
*
2017-10-18 16:01:21 +00:00
* @param {Node} path The DOM node
* @return {Boolean} %true on success, %false othersise
2016-12-20 09:32:17 +00:00
*/
2017-10-18 16:01:21 +00:00
,compileNode: function (node)
2015-03-09 08:36:54 +00:00
{
2017-10-20 12:06:16 +00:00
this._preCompile ();
this._mainContext = this._compile (node).id;
this._postCompile ();
return true;
2015-03-09 08:36:54 +00:00
}
2017-10-18 16:01:21 +00:00
/**
2017-10-20 12:06:16 +00:00
* Called before starting to compile nodes.
2017-10-18 16:01:21 +00:00
*/
2017-10-20 12:06:16 +00:00
,_preCompile: function ()
{
this._path = null;
this._tags = {};
this._contexts = [];
this._contextMap = {};
this._mainContext = null;
2017-10-20 12:06:16 +00:00
var compilers = [];
this._compilers = compilers;
for (var i = 0; i < regCompilers.length; i++)
compilers.push (new regCompilers[i] (this));
}
2017-10-20 12:06:16 +00:00
/**
* Called after all nodes have been compiled.
*/
,_postCompile: function ()
{
2017-10-20 12:06:16 +00:00
var compilers = this._compilers;
for (var i = 0; i < compilers.length; i++)
compilers[i].postCompile (this._contextMap);
}
2017-10-20 12:06:16 +00:00
/**
* Compiles a node.
*/
,_compile: function (node)
{
var context = null;
var tagName = null;
2017-10-18 16:01:21 +00:00
var isElement = node.nodeType === Node.ELEMENT_NODE;
2015-11-09 08:14:33 +00:00
2017-10-18 16:01:21 +00:00
if (isElement)
tagName = node.tagName.toLowerCase ();
else if (node.nodeType !== Node.TEXT_NODE
|| /^[\n\r\t]*$/.test (node.textContent))
return null;
2017-10-20 12:06:16 +00:00
var compilers = this._compilers;
for (var i = 0; i < compilers.length && context === null; i++)
context = compilers[i].compile (this, node, tagName);
context.id = this._contexts.length;
2017-10-20 12:06:16 +00:00
context.compiler = compilers[i - 1];
2017-10-18 16:01:21 +00:00
if (isElement)
2015-11-09 08:14:33 +00:00
{
var nodeId = node.getAttribute ('id');
if (nodeId)
2017-10-18 16:01:21 +00:00
{
2017-10-20 17:09:06 +00:00
this._contextMap[kebabToCamel (nodeId)] = context.id;
2017-10-18 16:01:21 +00:00
context.nodeId = nodeId;
}
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;
}
/**
2017-10-20 12:06:16 +00:00
* Creates a new scope from a compiled XML tree.
*
* @param {Document} dstDocument The document used to create the nodes
* @param {Object} signalData The object where to bind methods
* @param {Scope} parentScope The parent scope or %null for no parent
2017-10-20 17:09:06 +00:00
* @param {Lot} lot The default lot
2017-10-20 12:06:16 +00:00
* @return {Scope} The created scope
2016-12-20 09:32:17 +00:00
*/
2017-10-20 17:09:06 +00:00
,load: function (dstDocument, signalData, parentScope, extraObjects, lot)
{
2017-10-20 12:06:16 +00:00
if (this._contexts === null)
return null;
2017-10-20 12:06:16 +00:00
var doc = dstDocument ? dstDocument : document;
var contexts = this._contexts;
var len = contexts.length;
var objects = new Array (len);
2017-10-20 17:09:06 +00:00
var scope = new Scope (this, objects, signalData, parentScope, lot)
2017-10-18 16:01:21 +00:00
2017-10-20 12:06:16 +00:00
for (var i = 0; i < len; i++)
2017-10-18 16:01:21 +00:00
{
2017-10-20 12:06:16 +00:00
var context = contexts[i];
objects[i] = context.compiler.instantiate (doc, context, scope);
2017-10-18 16:01:21 +00:00
}
2017-10-20 17:09:06 +00:00
scope.init (extraObjects);
2017-10-20 12:06:16 +00:00
return scope;
}
/**
2017-10-20 12:06:16 +00:00
* Links all scope objects and connects it's events.
2016-12-20 09:32:17 +00:00
*/
2017-10-20 12:06:16 +00:00
,link: function (scope)
{
2017-10-20 12:06:16 +00:00
var contexts = this._contexts;
var objects = scope.objects;
var compilers = this._compilers;
for (var i = 0; i < compilers.length; i++)
compilers[i].preLink (scope);
2017-10-18 16:01:21 +00:00
2017-10-20 12:06:16 +00:00
for (var i = 0; i < contexts.length; i++)
2017-10-18 16:01:21 +00:00
{
2017-10-20 12:06:16 +00:00
var context = contexts[i];
context.compiler.link (context, objects[i], objects, scope);
2017-10-18 16:01:21 +00:00
}
2015-10-14 11:51:43 +00:00
2017-10-20 12:06:16 +00:00
for (var i = 0; i < contexts.length; i++)
{
2017-10-20 12:06:16 +00:00
var context = contexts[i];
context.compiler.connect (context, objects[i], objects, scope);
}
2017-10-18 16:01:21 +00:00
2017-10-20 12:06:16 +00:00
for (var i = 0; i < compilers.length; i++)
compilers[i].postLink (scope);
2017-10-18 16:01:21 +00:00
}
/**
* Logs an error parsing the node.
2017-10-20 12:06:16 +00:00
*
* @param {String} error The error message template
* @param {...} varArgs The message template arguments
*/
2017-10-20 12:06:16 +00:00
,showError: function (error)
{
var path = this._path ? this._path : '<unknown template>';
var logArgs = ['%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);
}
2017-10-18 16:01:21 +00:00
,getMain: function (result)
{
return result.objects[this._mainContext];
}
2017-10-20 17:09:06 +00:00
2017-10-20 12:06:16 +00:00
,getByTagName: function (scope, tagName)
2017-10-18 16:01:21 +00:00
{
var tags = this._tags[tagName];
if (tags)
{
var arr = new Array (tags.length);
for (var i = 0; i < tags.length; i++)
2017-10-20 12:06:16 +00:00
arr[i] = scope.objects[tags[i]];
2017-10-18 16:01:21 +00:00
return arr;
}
return [];
}
});