270 lines
5.3 KiB
JavaScript
270 lines
5.3 KiB
JavaScript
|
|
var VnObject = require ('./object');
|
|
var Compiler = require ('./compiler');
|
|
var Component = require ('./component');
|
|
var Type = require ('./type');
|
|
var kebabToCamel = require ('./string-util').kebabToCamel;
|
|
|
|
var specialAttrs = {
|
|
id : 1,
|
|
property : 1
|
|
};
|
|
|
|
/**
|
|
* Compiles a @Vn.Object from element tag.
|
|
*/
|
|
module.exports = new Class
|
|
({
|
|
Extends: Compiler
|
|
|
|
,_links: []
|
|
|
|
,compile: function (builder, node, tagName)
|
|
{
|
|
var klass = vnCustomTags[tagName];
|
|
|
|
if (!klass)
|
|
return null;
|
|
|
|
var props = {};
|
|
var objectProps = {};
|
|
var funcProps = {};
|
|
var childs = [];
|
|
var events = {};
|
|
|
|
var context = {
|
|
klass: klass,
|
|
props: props,
|
|
objectProps: objectProps,
|
|
funcProps: funcProps,
|
|
childs: childs,
|
|
events: events,
|
|
custom: null
|
|
};
|
|
|
|
var a = node.attributes;
|
|
|
|
for (var i = 0; i < a.length; i++)
|
|
{
|
|
var attribute = a[i].nodeName;
|
|
var value = a[i].nodeValue;
|
|
|
|
if (this.isEvent (attribute))
|
|
events[attribute.substr (3)] = value;
|
|
else if (!specialAttrs[attribute])
|
|
this.propCompile (context, klass, node, attribute, value);
|
|
}
|
|
|
|
var childNodes = node.childNodes;
|
|
|
|
if (childNodes)
|
|
for (var i = 0; i < childNodes.length; i++)
|
|
{
|
|
var child = childNodes[i];
|
|
var childContext = null;
|
|
var childTagName = null;
|
|
|
|
if (child.nodeType === Node.ELEMENT_NODE)
|
|
childTagName = child.tagName.toLowerCase ();
|
|
|
|
if (childTagName === 'pointer')
|
|
{
|
|
this.addLink (context, null, child.getAttribute ('object'));
|
|
}
|
|
else if (childTagName === 'custom')
|
|
{
|
|
context.custom = child;
|
|
}
|
|
else if (childContext = builder._compile (child))
|
|
{
|
|
var prop = null;
|
|
|
|
if (childTagName)
|
|
prop = child.getAttribute ('property');
|
|
|
|
if (prop)
|
|
{
|
|
prop = kebabToCamel (prop);
|
|
objectProps[prop] = childContext.id;
|
|
}
|
|
else
|
|
childs.push (childContext.id);
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
,propCompile: function (context, klass, node, attribute, value)
|
|
{
|
|
var isLink = false;
|
|
var propError = false;
|
|
var newValue = null;
|
|
var propName = kebabToCamel (attribute);
|
|
var propInfo = klass.Properties[propName];
|
|
|
|
if (!propInfo)
|
|
{
|
|
this.showError ('Attribute \'%s\' not valid for tag \'%s\'',
|
|
attribute, node.tagName);
|
|
return;
|
|
}
|
|
if (!value)
|
|
{
|
|
this.showError ('Attribute \'%s\' empty on tag \'%s\'',
|
|
attribute, node.tagName);
|
|
return;
|
|
}
|
|
|
|
if (this._interpoler.compile (context, propName, value))
|
|
return;
|
|
|
|
switch (propInfo.type)
|
|
{
|
|
case null:
|
|
newValue = value;
|
|
break;
|
|
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] = 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)
|
|
context.props[propName] = newValue;
|
|
else if (propError)
|
|
this.showError ('Attribute \'%s\' invalid for tag \'%s\'',
|
|
attribute, node.tagName);
|
|
}
|
|
|
|
,addLink: function (context, prop, objectId)
|
|
{
|
|
this._links.push ({
|
|
context: context
|
|
,prop: prop
|
|
,objectId: kebabToCamel (objectId)
|
|
});
|
|
}
|
|
|
|
,instantiate: function (doc, context, scope)
|
|
{
|
|
var object = new context.klass ();
|
|
object.setProperties (context.props);
|
|
|
|
if (context.nodeId && object instanceof Component)
|
|
{
|
|
var id = context.nodeId;
|
|
object.htmlId = scope.getHtmlId (id);
|
|
object.className = '_'+ id +' '+ object.className;
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
,setProperty: function (object, data, value)
|
|
{
|
|
object[data] = value;
|
|
}
|
|
|
|
,preLink: function (scope)
|
|
{
|
|
var objects = scope.objects;
|
|
var links = this._links;
|
|
|
|
for (var i = links.length - 1; i >= 0; i--)
|
|
{
|
|
var link = links[i];
|
|
var object = objects[link.context.id];
|
|
var 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: function (context, object, objects, scope)
|
|
{
|
|
var objectProps = context.objectProps;
|
|
for (var prop in objectProps)
|
|
object[prop] = objects[objectProps[prop]];
|
|
|
|
var childs = context.childs;
|
|
for (var i = 0; i < childs.length; i++)
|
|
object.appendChild (objects[childs[i]]);
|
|
|
|
if (context.custom)
|
|
object.loadXml (scope, context.custom);
|
|
}
|
|
|
|
,connect: function (context, object, objects, scope)
|
|
{
|
|
var funcProps = context.funcProps;
|
|
for (var prop in funcProps)
|
|
{
|
|
var method = scope.getMethod (funcProps[prop], true);
|
|
|
|
if (method)
|
|
object[prop] = method;
|
|
}
|
|
|
|
var events = context.events;
|
|
for (var event in events)
|
|
{
|
|
var method = scope.getMethod (events[event]);
|
|
|
|
if (method)
|
|
object.on (event, method, scope.thisArg);
|
|
}
|
|
}
|
|
|
|
,free: function (scope)
|
|
{
|
|
var objects = scope.objects;
|
|
|
|
for (var i = objects.length; i--;)
|
|
{
|
|
var object = objects[i];
|
|
|
|
if (object instanceof VnObject)
|
|
{
|
|
object.disconnectByInstance (scope.thisArg);
|
|
object.unref ();
|
|
}
|
|
}
|
|
}
|
|
|
|
,_replaceFunc: function (token)
|
|
{
|
|
return token.charAt(1).toUpperCase ();
|
|
}
|
|
});
|