hedera-web/js/vn/compiler-object.js

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();
}
});