hedera-web/js/vn/builder.js

169 lines
3.8 KiB
JavaScript

const VnObject = require('./object');
const Scope = require('./scope');
const kebabToCamel = require('./string-util').kebabToCamel;
const CompilerObject = require('./compiler-object');
const CompilerElement = require('./compiler-element');
const CompilerText = require('./compiler-text');
const regCompilers = [
CompilerText,
CompilerObject,
CompilerElement
];
/**
* Creates a object from a XML specification.
*/
module.exports = new Class({
Extends: VnObject
/**
* 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) {
const parser = new DOMParser();
const 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();
const docElement = doc.documentElement;
if (docElement.tagName !== 'vn') {
this.showError('The toplevel tag should be named \'vn\'');
this._contexts = null;
return false;
}
const childs = docElement.childNodes;
if (childs)
for (let i = 0; i < childs.length; i++)
this._compile(childs[i]);
this._postCompile();
return true;
}
/**
* Compiles a single DOM node.
*
* @path Node The DOM node
* @return %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._exprContexts = [];
this._contextMap = {};
this._links = [];
this._mainContext = null;
this._compilers = [];
for (regCompiler of regCompilers)
this._compilers.push(new regCompiler(this));
}
/**
* Called after all nodes have been compiled.
*/
,_postCompile: function() {
for (const compiler of this._compilers)
compiler.postCompile(this._contextMap);
}
/**
* Compiles a node.
*/
,_compile: function(node) {
let context = null;
let tagName = null;
const 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;
let i;
const compilers = this._compilers;
for (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) {
const nodeId = node.getAttribute('id');
if (nodeId) {
this._contextMap[kebabToCamel(nodeId)] = context.id;
context.nodeId = nodeId;
}
let tags = this._tags[tagName];
if (!tags)
this._tags[tagName] = tags = [];
tags.push(context.id);
}
this._contexts.push(context);
return context;
}
,load: function(dstDocument, thisArg, parentScope) {
if (!this._contexts) return null;
const doc = dstDocument ? dstDocument : document;
const objects = new Array(this._contexts.length);
const exprValues = new Array(this._exprContexts.length);
return new Scope(this, doc, objects, exprValues, thisArg, parentScope);
}
,showError: function(error) {
const path = this._path ? this._path : 'Node';
const logArgs = ['Vn.Builder: %s: '+ error, path];
for (let i = 1; i < arguments.length; i++)
logArgs.push(arguments[i]);
console.warn.apply(null, logArgs);
}
});