Ahora Vn.Builder preprocesa los XML haciendo que la carga repetida (Ej: Htk.Repeater) sea más eficiente

This commit is contained in:
Juan Ferrer Toribio 2015-11-09 18:11:51 +01:00
parent 7b4ff3b815
commit 0cf90bb53e
6 changed files with 614 additions and 929 deletions

View File

@ -114,6 +114,7 @@ Vn.Catalog = new Class
this.hideMenu (); this.hideMenu ();
var realms = this.$('realms-model'); var realms = this.$('realms-model');
return;
if (!realms.ready) if (!realms.ready)
return; return;
@ -350,7 +351,7 @@ Vn.Catalog = new Class
Vn.Filter = new Class Vn.Filter = new Class
({ ({
Extends: Htk.Widget Extends: Htk.Field
,Tag: 'vn-filter' ,Tag: 'vn-filter'
,Child: 'model' ,Child: 'model'
,Properties: ,Properties:
@ -403,8 +404,8 @@ Vn.Filter = new Class
this.node.className = 'vn-filter'; this.node.className = 'vn-filter';
this._select = new Htk.Select (); this._select = new Htk.Select ();
this._select.on ('mousedown', this._onClick.bind (this)); this._select.on ('mousedown', this._onClick, this);
this._select.on ('changed', this._onChange.bind (this)); this._select.on ('changed', this._onChange, this);
this.node.appendChild (this._select.getNode ()); this.node.appendChild (this._select.getNode ());
this._ul = document.createElement ('ul'); this._ul = document.createElement ('ul');
@ -422,10 +423,11 @@ Vn.Filter = new Class
,_onCloseClick: function (li) ,_onCloseClick: function (li)
{ {
Vn.Node.remove (li); Vn.Node.remove (li);
this.value = undefined;
} }
,_onChange: function () ,_onChange: function ()
{ {
if (this._select.value === null if (this._select.value === null
|| this._select.value === undefined) || this._select.value === undefined)
return; return;
@ -446,7 +448,9 @@ Vn.Filter = new Class
var text = document.createTextNode (label); var text = document.createTextNode (label);
li.appendChild (text); li.appendChild (text);
setTimeout (this._onTimeout.bind (this)); setTimeout (this._onTimeout.bind (this));
this.value = this._select.value;
} }
,_onTimeout: function () ,_onTimeout: function ()

View File

@ -1,53 +1,61 @@
<vn> <vn>
<vn-group> <vn-group>
<!-- <sql-filter-item type="LIKE" id="op-name">
<sql-filter-item type="EQUAL" id="op-realm"> <sql-field name="Article"/>
<sql-field name="reino_id" target="t"/> <sql-search-tags param="search"/>
<sql-set param="realm"/> </sql-filter-item>
</sql-filter-item> <sql-filter-item type="EQUAL" id="op-realm">
<sql-filter-item type="EQUAL" id="op-type"> <sql-field name="reino_id" target="t"/>
<sql-field name="tipo_id"/> <sql-value param="realm"/>
<sql-set param="type"/> </sql-filter-item>
</sql-filter-item> <sql-filter-item type="EQUAL" id="op-type">
<sql-filter-item type="EQUAL" id="op-color"> <sql-field name="tipo_id"/>
<sql-field name="Color"/> <sql-value param="type"/>
<sql-set param="color"/> </sql-filter-item>
</sql-filter-item> <sql-filter-item type="EQUAL" id="op-color">
<sql-filter-item type="EQUAL" id="op-origin"> <sql-field name="Color"/>
<sql-field name="id_origen"/> <sql-value param="color"/>
<sql-set param="origin"/> </sql-filter-item>
</sql-filter-item> <sql-filter-item type="EQUAL" id="op-origin">
<sql-filter-item type="EQUAL" id="op-category"> <sql-field name="id_origen"/>
<sql-field name="Categoria"/> <sql-value param="origin"/>
<sql-set param="category"/> </sql-filter-item>
</sql-filter-item> <sql-filter-item type="EQUAL" id="op-category">
--> <sql-field name="Categoria"/>
<sql-value param="category"/>
</sql-filter-item>
<sql-filter-item type="EQUAL" id="op-producer">
<sql-field name="producer_id"/>
<sql-value param="producer"/>
</sql-filter-item>
</vn-group> </vn-group>
<vn-group> <vn-group>
<vn-param id="search"/> <vn-param id="search"/>
<vn-param id="realm" on-changed="onTypeChange"/> <vn-param id="realm" on-changed="onTypeChange"/>
<vn-param id="type" on-changed="onTypeChange"/> <vn-param id="type" on-changed="onTypeChange"/>
<vn-param id="color"/>
<vn-param id="origin"/>
<vn-param id="category"/>
<vn-param id="producer"/>
</vn-group>
<vn-group>
<vn-hash-param key="realm" param="realm"/> <vn-hash-param key="realm" param="realm"/>
<vn-hash-param key="type" param="type"/> <vn-hash-param key="type" param="type"/>
<sql-filter type="AND" id="filter"> <sql-filter type="AND" id="filter">
<sql-filter-item type="LIKE" id="op-name"> <pointer object="op-name"/>
<sql-field name="Article"/> <pointer object="op-realm"/>
<sql-search-tags param="search"/> <pointer object="op-type"/>
</sql-filter-item> <pointer object="op-color"/>
<!-- <pointer object="op-origin"/>
<list property="operands"> <pointer object="op-category"/>
<pointer object="op-realm"/> <pointer object="op-producer"/>
<pointer object="op-type"/>
<pointer object="op-color"/>
<pointer object="op-origin"/>
<pointer object="op-category"/>
</list>
-->
</sql-filter> </sql-filter>
<db-query id="basket-lines"> <db-query id="basket-lines">
SELECT item_id, warehouse_id, SUM(amount) amount <custom>
FROM basket_item SELECT item_id, warehouse_id, SUM(amount) amount
GROUP BY warehouse_id FROM basket_item
GROUP BY warehouse_id
</custom>
</db-query> </db-query>
<db-model result-index="2" id="items-model"> <db-model result-index="2" id="items-model">
<custom> <custom>
@ -57,11 +65,11 @@
SELECT Id_Article item_id FROM vn2008.Articles SELECT Id_Article item_id FROM vn2008.Articles
WHERE #filter; WHERE #filter;
CALL bionic_calc (); CALL bionic_calc ();
SELECT a.Id_Article item_id, t.available, t.price, p.producer, SELECT a.Id_Article item_id, t.available, t.price, p.name producer,
a.Foto, a.Article, a.Categoria, a.Medida, a.Tallos, a.Color, o.Abreviatura a.Foto, a.Article, a.Categoria, a.Medida, a.Tallos, a.Color, o.Abreviatura
FROM tmp.bionic_item t FROM tmp.bionic_item t
JOIN vn2008.Articles a ON a.Id_Article = t.item_id JOIN vn2008.Articles a ON a.Id_Article = t.item_id
LEFT JOIN vn2008.Articles_producer p ON p.Id_Article = a.Id_Article LEFT JOIN vn2008.producer p ON p.producer_id = a.producer_id
LEFT JOIN vn2008.Origen o ON a.id_origen = o.id LEFT JOIN vn2008.Origen o ON a.id_origen = o.id
WHERE t.available > 0 WHERE t.available > 0
ORDER BY a.Article, a.Medida ORDER BY a.Article, a.Medida
@ -177,8 +185,8 @@
<htk-text form="item" column="Abreviatura"/> <htk-text form="item" column="Abreviatura"/>
</p> </p>
<div class="aval-price"> <div class="aval-price">
<htk-text form="item" column="available"/> <htk-text form="item" column="available"/>
<t>from</t> <t>from</t>
<span class="price"> <span class="price">
<htk-text form="item" column="price" format="%.2d€"/> <htk-text form="item" column="price" format="%.2d€"/>
</span> </span>
@ -226,59 +234,83 @@
</option> </option>
</select> </select>
<vn-filter placeholder="_Realm"> <vn-filter placeholder="_Realm">
<db-model property="model" auto-load="false" result-index="1"> <db-model
CALL item_available (); id="realms-model"
SELECT DISTINCT r.id, r.reino property="model"
FROM vn2008.reinos r on-status-changed="onTypeChange">
JOIN vn2008.Tipos t ON t.reino_id = r.id <custom>
JOIN vn2008.Articles a ON a.tipo_id = t.tipo_id SELECT id, reino, color FROM vn2008.reinos
JOIN tmp.item_available i ON i.item_id = a.Id_Article WHERE display != FALSE ORDER BY reino
WHERE /*#filter </custom>
AND*/ r.display
ORDER BY r.reino
</db-model> </db-model>
</vn-filter> </vn-filter>
<vn-filter placeholder="_Subtype"> <vn-filter placeholder="_Subtype" param="type">
<db-model property="model" auto-load="false" result-index="1"> <db-model
CALL item_available (); id="types-model"
SELECT DISTINCT t.tipo_id, LEFT(t.Tipo, 18) type property="model"
FROM vn2008.Tipos t auto-load="false"
JOIN vn2008.Articles a ON a.tipo_id = t.tipo_id result-index="1"
JOIN tmp.item_available i ON i.item_id = a.Id_Article on-status-changed="refreshTitle">
/*WHERE #filter*/ <custom>
ORDER BY t.Tipo CALL item_available ();
SELECT DISTINCT t.tipo_id, LEFT(t.Tipo, 18) type
FROM vn2008.Tipos t
JOIN vn2008.Articles a ON a.tipo_id = t.tipo_id
JOIN tmp.item_available i ON i.item_id = a.Id_Article
/*WHERE #filter*/
ORDER BY t.Tipo
</custom>
</db-model> </db-model>
</vn-filter> </vn-filter>
<vn-filter placeholder="_Color"> <vn-filter placeholder="_Color" param="color">
<db-model property="model" auto-load="false" result-index="1"> <db-model property="model" auto-load="false" result-index="1">
CALL item_available (); <custom>
SELECT DISTINCT c.Id_Tinta, c.name CALL item_available ();
FROM vn2008.Tintas c SELECT DISTINCT c.Id_Tinta, c.name
JOIN vn2008.Articles a ON a.Color = c.Id_Tinta FROM vn2008.Tintas c
JOIN tmp.item_available i ON i.item_id = a.Id_Article JOIN vn2008.Articles a ON a.Color = c.Id_Tinta
/*WHERE #filter*/ JOIN tmp.item_available i ON i.item_id = a.Id_Article
ORDER BY c.name /*WHERE #filter*/
ORDER BY c.name
</custom>
</db-model> </db-model>
</vn-filter> </vn-filter>
<vn-filter placeholder="_Origin"> <vn-filter placeholder="_Origin" param="origin">
<db-model property="model" auto-load="false" result-index="1"> <db-model property="model" auto-load="false" result-index="1">
CALL item_available (); <custom>
SELECT DISTINCT o.id, o.Origen, o.Abreviatura CALL item_available ();
FROM vn2008.Origen o SELECT DISTINCT o.id, o.Origen, o.Abreviatura
JOIN vn2008.Articles a ON a.id_origen = o.id FROM vn2008.Origen o
JOIN tmp.item_available i ON i.item_id = a.Id_Article JOIN vn2008.Articles a ON a.id_origen = o.id
/*WHERE #filter*/ JOIN tmp.item_available i ON i.item_id = a.Id_Article
ORDER BY o.Origen /*WHERE #filter*/
ORDER BY o.Origen
</custom>
</db-model> </db-model>
</vn-filter> </vn-filter>
<vn-filter placeholder="_Category"> <vn-filter placeholder="_Category" param="category">
<db-model property="model" auto-load="false" result-index="1"> <db-model property="model" auto-load="false" result-index="1">
CALL item_available (); <custom>
SELECT DISTINCT a.Categoria, a.Categoria category CALL item_available ();
FROM vn2008.Articles a SELECT DISTINCT a.Categoria, a.Categoria category
JOIN tmp.item_available i ON i.item_id = a.Id_Article FROM vn2008.Articles a
/*WHERE #filter*/ JOIN tmp.item_available i ON i.item_id = a.Id_Article
ORDER BY a.Categoria /*WHERE #filter*/
ORDER BY a.Categoria
</custom>
</db-model>
</vn-filter>
<vn-filter placeholder="_Producer" param="producer">
<db-model property="model" auto-load="false" result-index="1">
<custom>
CALL item_available ();
SELECT DISTINCT p.producer_id, p.name
FROM vn2008.producer p
JOIN vn2008.Articles a ON a.producer_id = p.producer_id
JOIN tmp.item_available i ON i.item_id = a.Id_Article
/*WHERE #filter*/
ORDER BY p.name
</custom>
</db-model> </db-model>
</vn-filter> </vn-filter>
</div> </div>

View File

@ -12,13 +12,13 @@ Htk.Repeater = new Class
{ {
this.link ({_model: x}, this.link ({_model: x},
{ {
'status-changed': this.onModelChange 'status-changed': this._onModelChange
,'row-deleted': this.onRowDelete ,'row-deleted': this._onRowDelete
,'row-updated': this.onRowUpdate ,'row-updated': this._onRowUpdate
,'row-inserted': this.onRowInsert ,'row-inserted': this._onRowInsert
}); });
this.onModelChange (); this._onModelChange ();
} }
,get: function () ,get: function ()
{ {
@ -62,7 +62,10 @@ Htk.Repeater = new Class
,initialize: function (props) ,initialize: function (props)
{ {
var div = this.createElement ('div'); var div = this.createElement ('div');
div.className = 'htk-repeater';
this._container = document.createElement ('div');
this._container.className = 'htk-repeater';
div.appendChild (this._container);
this.parent (props); this.parent (props);
} }
@ -75,25 +78,25 @@ Htk.Repeater = new Class
builder.setParent (builderResult); builder.setParent (builderResult);
builder.loadXmlFromNode (node.firstElementChild); builder.loadXmlFromNode (node.firstElementChild);
this.onModelChange (); this._onModelChange ();
} }
,getChild: function (index) ,getChild: function (index)
{ {
return this.node.childNodes[index]; return this._container.childNodes[index];
} }
,getBuilder: function (index) ,getBuilder: function (index)
{ {
return this.childsData[index].builder; return this._childsData[index].builder;
} }
,getForm: function (index) ,getForm: function (index)
{ {
return this.childsData[index].form; return this._childsData[index].form;
} }
,buildBox: function (index) ,_buildBox: function (index)
{ {
var form = new Db.Form ({ var form = new Db.Form ({
model: this._model, model: this._model,
@ -103,61 +106,64 @@ Htk.Repeater = new Class
this._builder.add (this._formId, form); this._builder.add (this._formId, form);
var res = this._builder.load (); var res = this._builder.load ();
this.childsData.push ({ this._childsData.push ({
builder: res, builder: res,
form: form form: form
}); });
if (this._renderer) if (this._renderer)
this._renderer (res, form); this._renderer (res, form);
this.node.appendChild (res.getMain ()); return res.getMain ();
} }
,onModelChange: function () ,_onModelChange: function ()
{ {
if (!this._model || !this._builder) if (!this._model || !this._builder)
return; return;
Vn.Node.removeChilds (this.node); this.node.removeChild (this._container);
this.freeChildsData (); Vn.Node.removeChilds (this._container);
this.childsData = [];
this._freeChildsData ();
this._childsData = [];
switch (this._model.status) switch (this._model.status)
{ {
case Db.Model.Status.READY: case Db.Model.Status.READY:
{ {
for (var i = 0; i < this._model.numRows; i++) for (var i = 0; i < this._model.numRows; i++)
this.buildBox (i); this._container.appendChild (this._buildBox (i));
this.showNoRecordsFound (); this._showNoRecordsFound ();
break; break;
} }
case Db.Model.Status.LOADING: case Db.Model.Status.LOADING:
this.showMessage (_('Loading'), 'loader-black.gif'); this._showMessage (_('Loading'), 'loader-black.gif');
break; break;
case Db.Model.Status.CLEAN: case Db.Model.Status.CLEAN:
this.showMessage (this.emptyMessage, 'refresh.svg'); this._showMessage (this.emptyMessage, 'refresh.svg');
break; break;
case Db.Model.Status.ERROR: case Db.Model.Status.ERROR:
this.showMessage (_('ErrorLoadingData'), 'error.svg'); this._showMessage (_('ErrorLoadingData'), 'error.svg');
break; break;
} }
this.node.appendChild (this._container);
this.signalEmit ('change'); this.signalEmit ('change');
} }
,showNoRecordsFound: function (count) ,_showNoRecordsFound: function (count)
{ {
if (this._model.numRows == 0) if (this._model.numRows === 0)
this.showMessage (_('EmptyList'), 'clean.svg'); this.showMessage (_('EmptyList'), 'clean.svg');
} }
,showMessage: function (message, src) ,_showMessage: function (message, src)
{ {
var div = document.createElement ('div'); var div = document.createElement ('div');
div.className = 'message'; div.className = 'message';
this.node.appendChild (div); this._container.appendChild (div);
var img = document.createElement ('img'); var img = document.createElement ('img');
img.alt = ''; img.alt = '';
@ -167,35 +173,42 @@ Htk.Repeater = new Class
div.appendChild (document.createTextNode (message)); div.appendChild (document.createTextNode (message));
} }
,onRowDelete: function (model, row) ,_onRowDelete: function (model, row)
{ {
Vn.Node.remove (this.node.childNodes[row]); Vn.Node.remove (this._container.childNodes[row]);
this.showNoRecordsFound (); this._unrefChildData (row);
this._showNoRecordsFound ();
} }
,onRowUpdate: function (model, row, columns) ,_onRowUpdate: function (model, row, columns)
{ {
// this.form[row].signalEmit ('iter-changed'); // this.form[row].signalEmit ('iter-changed');
} }
,onRowInsert: function (model, row) ,_onRowInsert: function (model, row)
{ {
this.buildBox (row); var box = this._buildBox (row);
this._container.appendChild (box);
} }
,freeChildsData: function () ,_freeChildsData: function ()
{ {
if (this.childsData) if (this._childsData)
for (var i = 0; i < this.childsData.length; i++) for (var i = 0; i < this._childsData.length; i++)
{ this._unrefChildData (i);
this.childsData[i].form.unref (); }
this.childsData[i].builder.unref ();
} ,_unrefChildData: function (index)
{
var childData = this._childsData[index];
childData.form.unref ();
childData.builder.unref ();
} }
,destroy: function () ,destroy: function ()
{ {
this.freeChildsData (); this._freeChildsData ();
this.parent (); this.parent ();
} }
}); });

View File

@ -1,575 +0,0 @@
/**
* Creates a object from a XML specification.
**/
Vn.Builder = new Class
({
Extends: Vn.Object
,addedMap: {}
,add: function (id, object)
{
this.addedMap[id] = object;
}
,setParent: function (parentBuilder)
{
this.parentBuilder = parentBuilder;
if (parentBuilder && !this.signalData)
this.signalData = parentBuilder.builder.signalData;
}
,getMain: function (result)
{
return result.objects[this.mainContext.id];
}
,getById: function (objectId, result)
{
var index = this.contextMap[objectId];
if (index !== undefined)
return result.objects[index];
else if (this.addedMap[objectId])
return this.addedMap[objectId];
else if (this.parentBuilder)
this.parentBuilder.getById (objectId);
return null;
}
,getByTagName: function (tagName, result)
{
var tags = this.tags[tagName];
if (tags)
{
var arr = new Array (tags.length);
for (var i = 0; i < tags.length; i++)
arr[i] = result.objects[tags[i]];
return arr;
}
return [];
}
,loadXml: function (path, dstDocument)
{
var xmlDoc = Vn.getXml (path);
if (!xmlDoc)
return false;
var docElement = xmlDoc.documentElement;
if (docElement.tagName !== 'vn')
return false;
this._compileInit (dstDocument);
var childs = docElement.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
this._compileNode (childs[i]);
this._compileEnd ();
return true;
}
,loadXmlFromNode: function (node, dstDocument)
{
this._compileInit (dstDocument);
this.mainContext = this._compileNode (node);
this._compileEnd ();
}
,load: function ()
{
var contexts = this.contexts;
var len = contexts.length;
var objects = new Array (len);
for (var i = 0; i < len; i++)
{
var context = contexts[i];
objects[i] =
this.textInstantiate (context)
|| this.objectInstantiate (context)
|| this.elementInstantiate (context);
}
var res = new BuilderResult (this, objects);
var addedObject;
for (var i = this.propLinks.length - 1; i >= 0; i--)
{
var l = this.propLinks[i];
if (addedObject = this.addedMap[l.value])
objects[l.context.id][l.prop] = addedObject;
}
for (var i = this.childLinks.length - 1; i >= 0; i--)
{
var l = this.childLinks[i];
if (addedObject = this.addedMap[l.value])
objects[l.context.id].appendChild (addedObject);
}
for (var i = 0; i < len; i++)
{
var context = contexts[i];
var object = objects[i];
this.objectLink (context, object, objects, res) ||
this.elementLink (context, object, objects, res);
}
return res;
}
,_compileInit: function (dstDocument)
{
this.tags = {};
this.contexts = [];
this.contextMap = {};
this.propLinks = [];
this.childLinks = [];
this.mainContext = null;
this.document = dstDocument ? dstDocument : document;
}
,_compileEnd: function (node)
{
var contextId;
for (var i = this.propLinks.length - 1; i >= 0; i--)
{
var l = this.propLinks[i];
if (contextId = this.contextMap[l.value])
{
l.context.objectProps[l.prop] = contextId;
this.propLinks.splice (i, 1);
}
else if (this.parentBuilder)
{
var object = this.parentBuilder.getById (l.value);
if (object)
pl.context.props[pl.prop] = object;
}
}
for (var i = this.childLinks.length - 1; i >= 0; i--)
{
var l = this.childLinks[i];
if (contextId = this.contextMap[l.value])
{
l.context.childs.push (contextId);
this.childLinks.splice (i, 1);
}
}
}
,_compileNode: function (node)
{
var context = null;
var tagName = null;
if (node.nodeType === Node.ELEMENT_NODE)
tagName = node.tagName.toLowerCase ();
else if (node.nodeType !== Node.TEXT_NODE
|| /^\s*$/.test (node.textContent))
return null;
var context =
this.textCompile (node, tagName)
|| this.objectCompile (node, tagName)
|| this.elementCompile (node, tagName);
context.id = this.contexts.length;
if (tagName)
{
var nodeId = node.getAttribute ('id');
if (nodeId)
this.contextMap[nodeId] = context.id;
var tags = this.tags[tagName];
if (!tags)
this.tags[tagName] = tags = [];
tags.push (context.id);
}
this.contexts.push (context);
return context;
}
/**
* Creates a text node context.
**/
,textCompile: function (node, tagName)
{
if (!tagName)
var text = node.textContent;
else if (tagName === 't')
var text = _(node.firstChild.textContent);
else
return null;
return {text: text};
}
,textInstantiate: function (context)
{
if (!context.text)
return null;
return this.document.createTextNode (context.text);
}
/**
* Creates a object context.
**/
,objectCompile: function (node, tagName)
{
var handler;
var props = {};
var objectProps = {};
var childs = [];
var events = {};
var klass = Vn.customTags[tagName];
if (!klass)
return null;
var context = {
klass: klass,
props: props,
events: events,
objectProps: objectProps,
childs: childs
};
var a = node.attributes;
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if ((handler = this._getEventHandler (attribute, value)))
{
events[attribute.substr (3)] = handler;
}
else if (!/^(id|property)$/.test (attribute))
{
this.propCompile (context, klass, props,
node, attribute, value);
}
}
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
{
var child = childNodes[i];
var childTagName = null;
if (child.tagName)
childTagName = child.tagName.toLowerCase ();
if (childTagName === 'pointer')
{
this.childLinks.push ({
context: context,
objectId: child.getAttribute ('object')
});
}
else if (childTagName === 'custom')
{
context.custom = child;
}
else
{
var childContext = this._compileNode (child);
if (!childContext)
continue;
var prop = null;
if (child.getAttribute)
prop = child.getAttribute ('property');
if (prop)
objectProps[prop] = childContext.id;
else
childs.push (childContext.id);
}
}
return context;
}
,propCompile: function (context, klass, props, node, attribute, value)
{
var newValue = null;
var propName = attribute.replace (/-./g, this._replaceFunc);
var propInfo = klass.Properties[propName];
if (!propInfo)
{
console.warn ('Vn.Builder: Attribute \'%s\' not valid for tag \'%s\'',
attribute, node.tagName);
return;
}
if (!value)
{
console.warn ('Vn.Builder: Attribute \'%s\' empty on tag \'%s\'',
attribute, node.tagName);
return;
}
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:
newValue = this._getMethod (value);
break;
default:
if (propInfo.enumType)
newValue = propInfo.enumType[value];
break;
}
if (newValue !== null && newValue !== undefined)
{
props[propName] = newValue;
}
else if (propInfo.type instanceof Function)
{
this.propLinks.push ({
context: context,
prop: attribute,
value: value
});
}
else
console.warn ('Vn.Builder: Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
,objectInstantiate: function (context)
{
if (!context.klass)
return null;
return new context.klass (context.props);
}
,objectLink: function (context, object, objects, res)
{
if (!context.klass)
return null;
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]]);
var events = context.events;
for (var event in events)
object.on (event,
events[event].bind (this.signalData));
if (context.custom)
object.loadXml (res, context.custom);
}
/**
* Creates a HTML node context.
**/
,elementCompile: function (node, tagName)
{
var handler;
var events = {};
var childs = [];
var htmlNode = this.document.createElement (tagName);
var a = node.attributes;
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if ((handler = this._getEventHandler (attribute, value)))
{
events[attribute.substr (3)] = handler;
}
else if (attribute !== 'id')
htmlNode.setAttribute (attribute,
this._translateValue (value));
}
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
{
var childContext = this._compileNode (childNodes[i]);
if (childContext)
childs.push (childContext.id);
}
return {
node: htmlNode,
events: events,
childs: childs
};
}
,elementInstantiate: function (context)
{
if (!context.node)
return null;
return context.node.cloneNode (false);
}
,elementLink: function (context, object, objects)
{
if (!context.node)
return null;
var childs = context.childs;
for (var i = 0; i < childs.length; i++)
{
var child = objects[childs[i]];
if (child instanceof Htk.Widget)
object.appendChild (child.getNode ());
else if (child instanceof Node)
object.appendChild (child);
}
var events = context.events;
for (var event in events)
object.addEventListener (event,
events[event].bind (this.signalData));
}
,_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;
}
,_getEventHandler: function (attribute, value)
{
if (!(/^on-\w+/.test (attribute)))
return null;
return this._getMethod (value);
}
,_replaceFunc: function (token)
{
return token.charAt(1).toUpperCase ();
}
});
var BuilderResult = new Class
({
Extends: Vn.Object
,initialize: function (builder, objects)
{
this.builder = builder;
this.objects = objects;
}
,getMain: function ()
{
return this.builder.getMain (this);
}
,$: function (objectId)
{
return this.builder.getById (objectId, this);
}
,getById: function (objectId)
{
return this.builder.getById (objectId, this);
}
,getByTagName: function (tagName)
{
return this.builder.getByTagName (tagName, this);
}
,_destroy: function ()
{
var objects = this.objects;
for (var i = 0; i < objects.length; i++)
if (objects[i].unref)
objects[i].unref ();
this.parent ();
}
});

View File

@ -1,274 +1,491 @@
/** /**
* Creates a object from a XML specification. * Creates a object from a XML specification.
**/ **/
Vn.BuilderOld = new Class Vn.Builder = new Class
({ ({
Extends: Vn.Object Extends: Vn.Object
,objectMap: {} ,_addedMap: {}
,tags: {} ,_contexts: null
,load: function () ,add: function (id, object)
{ {
return this; this._addedMap[id] = object;
}
,setParent: function (parentResult)
{
this._parentResult = parentResult;
if (parentResult && !this.signalData)
this.signalData = parentResult.builder.signalData;
}
,getMain: function (result)
{
return result.objects[this._mainContext];
}
,getById: function (objectId, result)
{
var index = this._contextMap[objectId];
if (index !== undefined)
return result.objects[index];
else if (this._parentResult)
return this._parentResult.getById (objectId);
return null;
}
,getByTagName: function (tagName, result)
{
var tags = this._tags[tagName];
if (tags)
{
var arr = new Array (tags.length);
for (var i = 0; i < tags.length; i++)
arr[i] = result.objects[tags[i]];
return arr;
}
return [];
} }
,loadXml: function (xmlDoc) ,loadXml: function (path, dstDocument)
{ {
var xmlDoc = Vn.getXml (path);
if (!xmlDoc) if (!xmlDoc)
return false; return false;
this._compileInit (dstDocument);
this._path = path;
var docElement = xmlDoc.documentElement; var docElement = xmlDoc.documentElement;
if (docElement.tagName !== 'vn') if (docElement.tagName !== 'vn')
{
this._showError ('Malformed XML');
this._contexts = null;
return false; return false;
}
this.contexts = [];
var childs = docElement.childNodes; var childs = docElement.childNodes;
if (childs) if (childs)
for (var i = 0; i < childs.length; i++) for (var i = 0; i < childs.length; i++)
this.loadNode (childs[i], null); this._compileNode (childs[i]);
this.resolveProperties (); this._compileEnd ();
delete this.contexts;
return true; return true;
} }
,loadXmlFromNode: function (node) ,loadXmlFromNode: function (node, dstDocument)
{ {
this.contexts = []; this._compileInit (dstDocument);
var mainNode = this.loadNode (node); this._mainContext = this._compileNode (node).id;
this.resolveProperties (); this._compileEnd ();
delete this.contexts;
return mainNode;
} }
,add: function (id, object) ,load: function ()
{ {
this.objectMap[id] = object; if (this._contexts === null)
return null;
var contexts = this._contexts;
var len = contexts.length;
var objects = new Array (len);
for (var i = 0; i < len; i++)
{
var context = contexts[i];
if (context.tagName)
objects[i] = this.elementInstantiate (context);
else if (context.klass)
objects[i] = this.objectInstantiate (context);
else
objects[i] = this.textInstantiate (context);
}
var res = new BuilderResult (this, objects);
for (var i = this._links.length - 1; i >= 0; i--)
{
var l = this._links[i];
var addedObject = this._addedMap[l.objectId];
if (addedObject)
if (l.prop)
objects[l.context.id][l.prop] = addedObject;
else
objects[l.context.id].appendChild (addedObject);
else
this._showError ('Referenced unexistent object with id \'%s\'',
l.objectId);
}
for (var i = 0; i < len; i++)
{
var context = contexts[i];
var object = objects[i];
if (context.tagName)
this.elementLink (context, object, objects, res);
else if (context.klass)
this.objectLink (context, object, objects, res);
}
return res;
}
,_compileInit: function (dstDocument)
{
this._path = null;
this._tags = {};
this._contexts = [];
this._contextMap = {};
this._links = [];
this._mainContext = null;
this._document = dstDocument ? dstDocument : document;
}
,_compileEnd: function (node)
{
for (var i = this._links.length - 1; i >= 0; i--)
{
var l = this._links[i];
var contextId = this._contextMap[l.objectId]
if (contextId)
{
if (l.prop)
l.context.objectProps[l.prop] = contextId;
else
l.context.childs.push (contextId);
this._links.splice (i, 1);
}
else if (this._parentResult)
{
var object = this._parentResult.getById (l.objectId);
if (object)
pl.context.props[pl.prop] = object;
}
}
} }
,loadNode: function (node) ,_compileNode: function (node)
{ {
var customNode; var context = null;
var htmlNode = null;
var tagName = null; var tagName = null;
if (node.tagName) if (node.nodeType === Node.ELEMENT_NODE)
tagName = node.tagName.toLowerCase (); tagName = node.tagName.toLowerCase ();
else if (node.nodeType !== Node.TEXT_NODE
|| /^[\n\r\t]*$/.test (node.textContent))
return null;
var context =
this.textCompile (node, tagName)
|| this.objectCompile (node, tagName)
|| this.elementCompile (node, tagName);
context.id = this._contexts.length;
if (tagName)
{
var nodeId = node.getAttribute ('id');
if (nodeId)
this._contextMap[nodeId] = context.id;
var tags = this._tags[tagName];
if (tagName === 't') if (!tags)
{ this._tags[tagName] = tags = [];
htmlNode = document.createTextNode (_(node.firstChild.textContent));
} tags.push (context.id);
else if (!tagName)
{
htmlNode = document.importNode (node, false);
}
else if ((customNode = this.loadCustomNode (node, tagName, 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; this._contexts.push (context);
return context;
} }
,loadCustomNode: function (node, tagName, parentContext) /**
* Creates a text node context.
**/
,textCompile: function (node, tagName)
{ {
if (!tagName) if (!tagName)
var text = node.textContent;
else if (tagName === 't')
var text = _(node.firstChild.textContent);
else
return null; return null;
return {text: text};
}
,textInstantiate: function (context)
{
return this._document.createTextNode (context.text);
}
/**
* Creates a object context.
**/
,objectCompile: function (node, tagName)
{
var klass = Vn.customTags[tagName]; var klass = Vn.customTags[tagName];
if (!klass) if (!klass)
return null; return null;
var customNode = new klass (); var props = {};
var objectProps = {};
if (!this.tags[tagName]) var childs = [];
this.tags[tagName] = []; var events = {};
var handler;
this.tags[tagName].push (customNode);
var context = { var context = {
node: node klass: klass,
,parent: parentContext props: props,
,object: customNode objectProps: objectProps,
,klass: klass childs: childs,
,custom: null events: events,
custom: null
}; };
this.contexts.push (context);
var a = node.attributes;
var nodeId = node.getAttribute ('id');
for (var i = 0; i < a.length; i++)
if (nodeId)
this.objectMap[nodeId] = customNode;
var childs = node.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
{ {
var childTagName = childs[i].tagName; var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if (!childTagName)
continue;
childTagName = childTagName.toLowerCase (); if (handler = this._getEventHandler (attribute, value))
var customChild = this.loadCustomNode (
childs[i], childTagName, context);
if (!customChild && childTagName === 'custom')
context.custom = childs[i];
}
return customNode;
}
,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.custom)
c.object.loadXml (this, c.custom);
if (c.parent)
{ {
var parentProperty = c.node.getAttribute ('property'); events[attribute.substr (3)] = handler;
}
if (parentProperty) else if (!/^(id|property)$/.test (attribute))
this.setProperty (c.parent, parentProperty, c.object); {
else this.propCompile (context, klass, props,
c.parent.object.appendChild (c.object); node, attribute, value);
} }
} }
}
var childNodes = node.childNodes;
,setAttribute: function (c, attribute, value)
{ if (childNodes)
if (/^on-\w+/.test (attribute)) for (var i = 0; i < childNodes.length; i++)
{ {
var method = this.getMethod (value); var child = childNodes[i];
if (method) if (child.nodeType !== Node.ELEMENT_NODE)
c.object.on (attribute.substr (3), method, this.signalData); continue;
}
else if (!/^(id|property)$/.test (attribute)) var childContext;
{ var childTagName = child.tagName.toLowerCase ();
this.setProperty (c, attribute, value)
if (childTagName === 'pointer')
{
this._addLink (context, null, child.getAttribute ('object'));
}
else if (childTagName === 'custom')
{
context.custom = child;
}
else if (childContext = this._compileNode (child))
{
var prop = child.getAttribute ('property');
if (prop)
objectProps[prop] = childContext.id;
else
childs.push (childContext.id);
}
} }
return context;
} }
,setProperty: function (c, attribute, value) ,propCompile: function (context, klass, props, node, attribute, value)
{ {
var propName = attribute.replace (/-./g, this.replaceFunc); var newValue = null;
var prop = c.klass.Properties[propName]; var propName = attribute.replace (/-./g, this._replaceFunc);
var propInfo = klass.Properties[propName];
if (!prop) if (!propInfo)
{ {
console.warn ('Vn.Builder: Attribute \'%s\' not valid for tag \'%s\'', this._showError ('Attribute \'%s\' not valid for tag \'%s\'',
attribute, c.node.tagName); attribute, node.tagName);
return; return;
} }
if (!value) if (!value)
return;
switch (prop.type)
{ {
case Boolean: this._showError ('Attribute \'%s\' empty on tag \'%s\'',
value = (/^(true|1)$/i).test (value); attribute, node.tagName);
break; return;
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) switch (propInfo.type)
c.object[propName] = value; {
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:
newValue = this._getMethod (value);
break;
default:
if (propInfo.enumType)
newValue = propInfo.enumType[value];
break;
}
if (newValue !== null && newValue !== undefined)
props[propName] = newValue;
else if (propInfo.type instanceof Function)
this._addLink (context, attribute, value);
else else
console.warn ('Vn.Builder: Empty attribute \'%s\' on tag \'%s\'', this._showError ('Attribute \'%s\' invalid for tag \'%s\'',
attribute, c.node.tagName); attribute, node.tagName);
}
,objectInstantiate: function (context)
{
return new context.klass (context.props);
}
,objectLink: function (context, object, objects, res)
{
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]]);
var events = context.events;
for (var event in events)
object.on (event,
events[event].bind (this.signalData));
if (context.custom)
object.loadXml (res, context.custom);
}
/**
* Creates a HTML node context.
**/
,elementCompile: function (node, tagName)
{
var attributes = {};
var childs = [];
var events = {};
var handler;
var a = node.attributes;
for (var i = 0; i < a.length; i++)
{
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if (handler = this._getEventHandler (attribute, value))
events[attribute.substr (3)] = handler;
else if (attribute !== 'id')
attributes[attribute] = this._translateValue (value);
}
var childContext;
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
if (childContext = this._compileNode (childNodes[i]))
childs.push (childContext.id);
return {
tagName: tagName,
attributes: attributes,
childs: childs,
events: events
};
}
,elementInstantiate: function (context)
{
return this._document.createElement (context.tagName);
}
,elementLink: function (context, object, objects)
{
var attributes = context.attributes;
for (var attribute in attributes)
object.setAttribute (attribute, attributes[attribute]);
var childs = context.childs;
for (var i = 0; i < childs.length; i++)
{
var child = objects[childs[i]];
if (child instanceof Htk.Widget)
object.appendChild (child.getNode ());
else if (child instanceof Node)
object.appendChild (child);
}
var events = context.events;
for (var event in events)
object.addEventListener (event,
events[event].bind (this.signalData));
}
,_showError: function (error)
{
var path = this._path ? this._path : 'Node';
var logArgs = ['Vn.Builder: %s: '+ error, path];
for (var i = 1; i < arguments.length; i++)
logArgs.push (arguments[i]);
console.warn.apply (null, logArgs);
}
,_addLink: function (context, prop, objectId)
{
this._links.push ({
context: context
,prop: prop
,objectId: objectId
});
} }
//+++++++++++++++++++++++++++++++++++++++++++ Utilities ,_translateValue: function (value)
,translateValue: function (value)
{ {
var chr = value.charAt (0); var chr = value.charAt (0);
if (chr == '_') if (chr === '_')
return _(value.substr (1)); return _(value.substr (1));
else if (chr == '\\' && value.charAt (1) == '_') else if (chr === '\\' && value.charAt (1) === '_')
return value.substr (1); return value.substr (1);
return value; return value;
} }
,getMethod: function (value) ,_getMethod: function (value)
{ {
if (this.signalData) if (this.signalData)
var methodName = 'this.signalData.'+ value; var methodName = 'this.signalData.'+ value;
@ -285,69 +502,64 @@ Vn.BuilderOld = new Class
method = null; method = null;
} }
if (method == null) if (method === null)
console.warn ('Vn.Builder: Function \'%s\' not found', this._showError ('Function \'%s\' not found', value);
value);
return method; return method;
} }
,getEventHandler: function (attribute, value) ,_getEventHandler: function (attribute, value)
{ {
if (!(/^on-\w+/.test (attribute))) if (!(/^on-\w+/.test (attribute)))
return null; return null;
return this.getMethod (value); return this._getMethod (value);
} }
,replaceFunc: function (token) ,_replaceFunc: function (token)
{ {
return token.charAt(1).toUpperCase (); return token.charAt(1).toUpperCase ();
} }
});
,setParent: function (parentBuilder)
var BuilderResult = new Class
({
Extends: Vn.Object
,initialize: function (builder, objects)
{ {
this.parentBuilder = parentBuilder; this.builder = builder;
this.objects = objects;
if (parentBuilder) }
this.signalData = parentBuilder.signalData;
,getMain: function ()
{
return this.builder.getMain (this);
} }
,$: function (objectId) ,$: function (objectId)
{ {
return this.getById (objectId); return this.builder.getById (objectId, this);
} }
,getById: function (objectId) ,getById: function (objectId)
{ {
var object = this.objectMap[objectId]; return this.builder.getById (objectId, this);
if (object)
return object;
if (this.parentBuilder)
return this.parentBuilder.get (objectId);
return null;
} }
,getByTagName: function (tagName) ,getByTagName: function (tagName)
{ {
if (this.tags[tagName]) return this.builder.getByTagName (tagName, this);
return this.tags[tagName];
return [];
} }
,_destroy: function () ,_destroy: function ()
{ {
for (var tag in this.tags) var objects = this.objects;
{
var objects = this.tags[tag]; for (var i = 0; i < objects.length; i++)
if (objects[i] instanceof Vn.Object)
objects[i].unref ();
for (var i = 0; i < objects.length; i++)
objects[i].unref ();
}
this.parent (); this.parent ();
} }
}); });

View File

@ -16,7 +16,6 @@ Vn.includeLib ('vn',
,'hash-param' ,'hash-param'
,'node' ,'node'
,'builder' ,'builder'
,'builder-new'
,'http-request' ,'http-request'
]); ]);