169 lines
3.8 KiB
JavaScript
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);
|
|
}
|
|
});
|