forked from verdnatura/hedera-web
216 lines
5.1 KiB
JavaScript
216 lines
5.1 KiB
JavaScript
const Compiler = require('./compiler');
|
|
const Component = require('./component');
|
|
const Type = require('./type');
|
|
const kebabToCamel = require('./string-util').kebabToCamel;
|
|
|
|
/**
|
|
* Compiles a @Vn.Object from element tag.
|
|
*/
|
|
module.exports = new Class({
|
|
Extends: Compiler
|
|
|
|
,_links: []
|
|
|
|
/**
|
|
* Creates a object context.
|
|
*/
|
|
,compile(builder, node, tagName) {
|
|
const klass = vnCustomTags[tagName];
|
|
if (!klass) return null;
|
|
|
|
const context = {
|
|
klass,
|
|
props: {},
|
|
funcProps: {},
|
|
objectProps: {},
|
|
childs: [],
|
|
events: {},
|
|
custom: null
|
|
};
|
|
|
|
const a = node.attributes;
|
|
for (let i = 0; i < a.length; i++) {
|
|
const attribute = a[i].nodeName;
|
|
const value = a[i].nodeValue;
|
|
|
|
if (this.isEvent(attribute)) {
|
|
const handler = this._getMethod(value)
|
|
if (handler) context.events[attribute.substr(3)] = handler;
|
|
} else if (!/^(id|property)$/.test(attribute)) {
|
|
this.propCompile(context, node, attribute, value);
|
|
}
|
|
}
|
|
|
|
const childNodes = node.childNodes;
|
|
|
|
if (childNodes)
|
|
for (let i = 0; i < childNodes.length; i++) {
|
|
const child = childNodes[i];
|
|
const isElement = child.nodeType === Node.ELEMENT_NODE;
|
|
const childTagName = isElement ? child.tagName.toLowerCase() : null;
|
|
let childContext;
|
|
|
|
if (childTagName === 'pointer') {
|
|
this._addLink(context, null, child.getAttribute('object'));
|
|
} else if (childTagName === 'custom') {
|
|
context.custom = child;
|
|
} else if (childContext = builder._compile(child)) {
|
|
let prop = isElement ? child.getAttribute('property') : null;
|
|
|
|
if (prop) {
|
|
prop = kebabToCamel(prop);
|
|
context.objectProps[prop] = childContext.id;
|
|
} else
|
|
context.childs.push(childContext.id);
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
,propCompile(context, node, attribute, value) {
|
|
const tagName = node.tagName;
|
|
const propName = kebabToCamel(attribute);
|
|
const propInfo = context.klass.Properties[propName];
|
|
|
|
if (!value) {
|
|
this.showError('Attribute \'%s\' empty on tag \'%s\'',
|
|
attribute, tagName);
|
|
return;
|
|
}
|
|
|
|
if (propName == 'vModel') {
|
|
context.vModel = this.modelExpr(value);
|
|
return;
|
|
}
|
|
|
|
if (!propInfo) {
|
|
this.showError('Attribute \'%s\' not valid for tag \'%s\'',
|
|
attribute, tagName);
|
|
return;
|
|
}
|
|
|
|
if (this.isExpr(value)) {
|
|
this.compileExpr(context, propName, value);
|
|
} else {
|
|
let isLink = false;
|
|
let propError = false;
|
|
let newValue = null;
|
|
|
|
switch (propInfo.type) {
|
|
case Boolean:
|
|
newValue = (/^(true|1)$/i).test(value);
|
|
break;
|
|
case Number:
|
|
newValue = 0 + new Number(value);
|
|
break;
|
|
case String:
|
|
newValue = this._translateValue(value);
|
|
break;
|
|
case Function:
|
|
context.funcProps[propName] = this._getMethod(value);
|
|
break;
|
|
case Type:
|
|
newValue = window[value];
|
|
break;
|
|
default:
|
|
if (propInfo.enumType)
|
|
newValue = propInfo.enumType[value];
|
|
else if (propInfo.type instanceof Function)
|
|
isLink = true;
|
|
else
|
|
propError = true;
|
|
}
|
|
|
|
if (isLink)
|
|
this._addLink(context, propName, value);
|
|
else if (newValue !== null && newValue !== undefined)
|
|
context.props[propName] = newValue;
|
|
else if (propError)
|
|
this.showError('Attribute \'%s\' invalid for tag \'%s\'',
|
|
attribute, tagName);
|
|
}
|
|
}
|
|
|
|
,instantiate(doc, context, scope) {
|
|
const object = new context.klass();
|
|
object.setProperties(context.props);
|
|
|
|
if (context.nodeId && object instanceof Component) {
|
|
const id = context.nodeId;
|
|
object.htmlId = scope.getHtmlId(id);
|
|
object.className = '_'+ id +' '+ (object.className || '');
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
,setProperty(object, property, value) {
|
|
object[property] = value;
|
|
}
|
|
|
|
,preLink(scope) {
|
|
const objects = scope.objects;
|
|
const links = this._links;
|
|
|
|
for (let i = links.length - 1; i >= 0; i--) {
|
|
const link = links[i];
|
|
const object = objects[link.context.id];
|
|
const objectRef = scope.$[link.objectId];
|
|
|
|
if (objectRef === undefined) {
|
|
this.showError('Referenced unexistent object with id \'%s\'',
|
|
link.objectId);
|
|
continue;
|
|
}
|
|
|
|
if (link.prop)
|
|
object[link.prop] = objectRef;
|
|
else
|
|
object.appendChild(objectRef);
|
|
}
|
|
}
|
|
|
|
,link(context, object, objects, scope) {
|
|
const objectProps = context.objectProps;
|
|
for (const prop in objectProps)
|
|
object[prop] = objects[objectProps[prop]];
|
|
|
|
const childs = context.childs;
|
|
for (let i = 0; i < childs.length; i++)
|
|
object.appendChild(objects[childs[i]]);
|
|
|
|
const funcProps = context.funcProps;
|
|
for (const prop in funcProps)
|
|
object[prop] = this.bindMethod(funcProps[prop], scope);
|
|
|
|
const events = context.events;
|
|
for (const event in events) {
|
|
const listener = this.bindMethod(events[event], scope);
|
|
if (listener)
|
|
object.on(event, listener, scope.thisArg);
|
|
}
|
|
|
|
if (context.vModel) {
|
|
object.on('change', function(lot) {
|
|
context.vModel.call(scope.thisArg, scope.$, lot.$);
|
|
scope.digest();
|
|
}, scope);
|
|
}
|
|
|
|
if (context.custom)
|
|
object.loadXml(scope, context.custom);
|
|
}
|
|
|
|
,_addLink(context, prop, objectId) {
|
|
this._links.push({
|
|
context
|
|
,prop
|
|
,objectId: kebabToCamel(objectId)
|
|
});
|
|
}
|
|
|
|
,_replaceFunc(token) {
|
|
return token.charAt(1).toUpperCase();
|
|
}
|
|
}); |