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) object.htmlId = scope.getHtmlId (context.nodeId); 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 (); } });