265 lines
5.6 KiB
JavaScript
265 lines
5.6 KiB
JavaScript
|
|
var VnObject = require ('./object');
|
|
var Scope = require ('./scope');
|
|
var kebabToCamel = require ('./string-util').kebabToCamel;
|
|
|
|
var CompilerObject = require ('./compiler-object');
|
|
var CompilerElement = require ('./compiler-element');
|
|
var CompilerText = require ('./compiler-text');
|
|
|
|
var regCompilers = [
|
|
CompilerObject,
|
|
CompilerElement,
|
|
CompilerText
|
|
];
|
|
|
|
/**
|
|
* Creates a object from a XML specification.
|
|
*/
|
|
module.exports = new Class
|
|
({
|
|
Extends: VnObject
|
|
|
|
,_contexts: null
|
|
|
|
/**
|
|
* Compiles an XML file.
|
|
*
|
|
* @param {String} path The XML path
|
|
* @return {Boolean} %true on success, %false othersise
|
|
*/
|
|
,compileFile: function (path)
|
|
{
|
|
this._path = path;
|
|
return this.compileDocument (Vn.getXml (path));
|
|
}
|
|
|
|
/**
|
|
* Compiles an XML string.
|
|
*
|
|
* @param {String} xmlString The XML string
|
|
* @return {Boolean} %true on success, %false othersise
|
|
*/
|
|
,compileString: function (xmlString)
|
|
{
|
|
var parser = new DOMParser ();
|
|
var doc = parser.parseFromString (xmlString, 'text/xml');
|
|
return this.compileDocument (doc);
|
|
}
|
|
|
|
/**
|
|
* Compiles a XML document.
|
|
*
|
|
* @param {Document} doc The DOM document
|
|
* @return {Boolean} %true on success, %false othersise
|
|
*/
|
|
,compileDocument: function (doc)
|
|
{
|
|
if (!doc)
|
|
return false;
|
|
|
|
this._preCompile ();
|
|
|
|
var docElement = doc.documentElement;
|
|
|
|
if (docElement.tagName !== 'vn')
|
|
{
|
|
this.showError ('The toplevel tag should be named \'vn\'');
|
|
this._contexts = null;
|
|
return false;
|
|
}
|
|
|
|
var childs = docElement.childNodes;
|
|
|
|
if (childs)
|
|
for (var i = 0; i < childs.length; i++)
|
|
this._compile (childs[i]);
|
|
|
|
this._postCompile ();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Compiles a single DOM node.
|
|
*
|
|
* @param {Node} path The DOM node
|
|
* @return {Boolean} %true on success, %false othersise
|
|
*/
|
|
,compileNode: function (node)
|
|
{
|
|
this._preCompile ();
|
|
this._mainContext = this._compile (node).id;
|
|
this._postCompile ();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Called before starting to compile nodes.
|
|
*/
|
|
,_preCompile: function ()
|
|
{
|
|
this._path = null;
|
|
this._tags = {};
|
|
this._contexts = [];
|
|
this._contextMap = {};
|
|
this._mainContext = null;
|
|
|
|
var compilers = [];
|
|
this._compilers = compilers;
|
|
|
|
for (var i = 0; i < regCompilers.length; i++)
|
|
compilers.push (new regCompilers[i] (this));
|
|
}
|
|
|
|
/**
|
|
* Called after all nodes have been compiled.
|
|
*/
|
|
,_postCompile: function ()
|
|
{
|
|
var compilers = this._compilers;
|
|
for (var i = 0; i < compilers.length; i++)
|
|
compilers[i].postCompile (this._contextMap);
|
|
}
|
|
|
|
/**
|
|
* Compiles a node.
|
|
*/
|
|
,_compile: function (node)
|
|
{
|
|
var context = null;
|
|
var tagName = null;
|
|
var isElement = node.nodeType === Node.ELEMENT_NODE;
|
|
|
|
if (isElement)
|
|
tagName = node.tagName.toLowerCase ();
|
|
else if (node.nodeType !== Node.TEXT_NODE
|
|
|| /^[\n\r\t]*$/.test (node.textContent))
|
|
return null;
|
|
|
|
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;
|
|
context.compiler = compilers[i - 1];
|
|
|
|
if (isElement)
|
|
{
|
|
var nodeId = node.getAttribute ('id');
|
|
|
|
if (nodeId)
|
|
{
|
|
this._contextMap[kebabToCamel (nodeId)] = context.id;
|
|
context.nodeId = nodeId;
|
|
}
|
|
|
|
var tags = this._tags[tagName];
|
|
|
|
if (!tags)
|
|
this._tags[tagName] = tags = [];
|
|
|
|
tags.push (context.id);
|
|
}
|
|
|
|
this._contexts.push (context);
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* @param {Lot} lot The default lot
|
|
* @return {Scope} The created scope
|
|
*/
|
|
,load: function (dstDocument, signalData, parentScope, extraObjects, lot)
|
|
{
|
|
if (this._contexts === null)
|
|
return null;
|
|
|
|
var doc = dstDocument ? dstDocument : document;
|
|
var contexts = this._contexts;
|
|
var len = contexts.length;
|
|
var objects = new Array (len);
|
|
var scope = new Scope (this, objects, signalData, parentScope, lot)
|
|
|
|
for (var i = 0; i < len; i++)
|
|
{
|
|
var context = contexts[i];
|
|
objects[i] = context.compiler.instantiate (doc, context, scope);
|
|
}
|
|
|
|
scope.init (extraObjects);
|
|
return scope;
|
|
}
|
|
|
|
/**
|
|
* Links all scope objects and connects it's events.
|
|
*/
|
|
,link: function (scope)
|
|
{
|
|
var contexts = this._contexts;
|
|
var objects = scope.objects;
|
|
var compilers = this._compilers;
|
|
|
|
for (var i = 0; i < compilers.length; i++)
|
|
compilers[i].preLink (scope);
|
|
|
|
for (var i = 0; i < contexts.length; i++)
|
|
{
|
|
var context = contexts[i];
|
|
context.compiler.link (context, objects[i], objects, scope);
|
|
}
|
|
|
|
for (var i = 0; i < contexts.length; i++)
|
|
{
|
|
var context = contexts[i];
|
|
context.compiler.connect (context, objects[i], objects, scope);
|
|
}
|
|
|
|
for (var i = 0; i < compilers.length; i++)
|
|
compilers[i].postLink (scope);
|
|
}
|
|
|
|
/**
|
|
* Logs an error parsing the node.
|
|
*
|
|
* @param {String} error The error message template
|
|
* @param {...} varArgs The message template arguments
|
|
*/
|
|
,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]);
|
|
|
|
console.warn.apply (console, logArgs);
|
|
}
|
|
|
|
,getMain: function (result)
|
|
{
|
|
return result.objects[this._mainContext];
|
|
}
|
|
|
|
,getByTagName: function (scope, tagName)
|
|
{
|
|
var tags = this._tags[tagName];
|
|
|
|
if (tags)
|
|
{
|
|
var arr = new Array (tags.length);
|
|
|
|
for (var i = 0; i < tags.length; i++)
|
|
arr[i] = scope.objects[tags[i]];
|
|
|
|
return arr;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
});
|