/** * Creates a object from a XML specification. **/ Vn.Builder = new Class ({ Extends: Vn.Object ,objectMap: {} ,tags: {} ,loadXml: function (xmlDoc) { if (!xmlDoc) return false; var docElement = xmlDoc.documentElement; if (docElement.tagName !== 'vn') return false; this.contexts = []; var childs = docElement.childNodes; if (childs) for (var i = 0; i < childs.length; i++) this.loadNode (childs[i], null); this.resolveProperties (); delete this.contexts; return true; } ,loadXmlFromNode: function (node) { this.contexts = []; var mainNode = this.loadNode (node); this.resolveProperties (); delete this.contexts; return mainNode; } ,add: function (id, object) { this.objectMap[id] = object; } ,loadNode: function (node) { var customNode; var htmlNode = null; var tagName = null; if (node.tagName) tagName = node.tagName.toLowerCase (); if (tagName === 't') { htmlNode = document.createTextNode (_(node.firstChild.textContent)); } else if (!tagName) { htmlNode = document.importNode (node, false); } else if ((customNode = this.loadCustomNode (node, null))) { if (customNode instanceof Htk.Widget) htmlNode = customNode.getNode (); } else { htmlNode = document.createElement (tagName); var a = node.attributes; for (var i = 0; i < a.length; i++) { var nodeName = a[i].nodeName; var nodeValue = a[i].nodeValue; if (/^on-\w+/.test (nodeName)) { var method = this.getMethod (nodeValue); htmlNode.addEventListener ( nodeName.substr (3), method.bind (this.signalData)); } else if (nodeName === 'id') { this.objectMap[nodeValue] = htmlNode; } else htmlNode.setAttribute (nodeName, this.translateValue (nodeValue)); } var childs = node.childNodes; if (childs) for (var i = 0; i < childs.length; i++) { var htmlChild = this.loadNode (childs[i]); if (htmlChild) htmlNode.appendChild (htmlChild); } } return htmlNode; } ,loadCustomNode: function (node, parentContext) { if (!node.tagName) return null; var tagName = node.tagName.toLowerCase (); var klass = Vn.customTags[tagName]; if (!klass) return null; var customNode = new klass (); if (!this.tags[tagName]) this.tags[tagName] = []; this.tags[tagName].push (customNode); var context = { node: node ,parent: parentContext ,object: customNode ,klass: klass }; this.contexts.push (context); var nodeId = node.getAttribute ('id'); if (nodeId) this.objectMap[nodeId] = customNode; var childs = node.childNodes; if (childs) for (var i = 0; i < childs.length; i++) this.loadCustomNode (childs[i], context); return customNode; } ,translateValue: function (value) { var chr = value.charAt (0); if (chr == '_') return _(value.substr (1)); else if (chr == '\\' && value.charAt (1) == '_') return value.substr (1); return value; } ,getMethod: function (value) { if (this.signalData) var methodName = 'this.signalData.'+ value; else var methodName = value; var method; try { method = eval (methodName); } catch (e) { method = null; } if (method == null) console.warn ('Vn.Builder: Function \'%s\' not found', value); return method; } ,replaceFunc: function (token) { return token.charAt(1).toUpperCase (); } ,resolveProperties: function () { for (var i = 0; i < this.contexts.length; i++) { var c = this.contexts[i]; var a = c.node.attributes; for (var j = 0; j < a.length; j++) this.setAttribute (c, a[j].nodeName, a[j].nodeValue); if (c.parent) { var parentProperty = c.node.getAttribute ('property'); if (!parentProperty) parentProperty = c.parent.klass.Child; if (parentProperty) this.setProperty (c.parent, parentProperty, c.object); if (c.klass.Parent) this.setProperty (c, c.klass.Parent, c.parent.object); } c.object.loadXml (this, c.node); } } ,setAttribute: function (c, attribute, value) { if (/^on-\w+/.test (attribute)) { var method = this.getMethod (value); if (method) c.object.on (attribute.substr (3), method, this.signalData); } else if (!/^(id|property)$/.test (attribute)) { this.setProperty (c, attribute, value) } } ,setProperty: function (c, attribute, value) { var propName = attribute.replace (/-./g, this.replaceFunc); var prop = c.klass.Properties[propName]; if (!prop) { console.warn ('Vn.Builder: Attribute \'%s\' not valid for tag \'%s\'', attribute, c.node.tagName); return; } if (!value) return; switch (prop.type) { case Boolean: value = (/^(true|1)$/i).test (value); break; case Number: value = 0 + new Number (value); break; case String: value = this.translateValue (value); break; case Function: { var method = this.getMethod (value); value = method ? method.bind (this.signalData) : null; break; } default: if (prop.type instanceof Function) { if (typeof value == 'string') value = this.get (value); if (!(value instanceof prop.type)) return; } else if (prop.enumType) value = prop.enumType[value]; } if (value !== undefined) c.object[propName] = value; else console.warn ('Vn.Builder: Empty attribute \'%s\' on tag \'%s\'', attribute, c.node.tagName); } ,setParent: function (parentBuilder) { this.parentBuilder = parentBuilder; if (parentBuilder) this.signalData = parentBuilder.signalData; } ,$: function (objectId) { return this.get (objectId); } ,get: function (objectId) { var object = this.objectMap[objectId]; if (object) return object; if (this.parentBuilder) return this.parentBuilder.get (objectId); return null; } ,getObjects: function (tagName) { if (this.tags[tagName]) return this.tags[tagName]; return []; } ,_destroy: function () { for (var tag in this.tags) { var objects = this.tags[tag]; for (var i = 0; i < objects.length; i++) objects[i].unref (); } this.parent (); } });