diff --git a/forms/reports/shelves/shelves.js b/forms/reports/shelves/shelves.js
index 483ebdfb..221f37df 100644
--- a/forms/reports/shelves/shelves.js
+++ b/forms/reports/shelves/shelves.js
@@ -1,16 +1,13 @@
-Hedera.Shelves = new Class
-({
+Hedera.Shelves = new Class({
Extends: Hedera.Form
- ,activate: function ()
- {
- this.$('date').value = new Date ();
+ ,activate: function() {
+ this.$('date').value = new Date();
this.$('useIds').value = false;
}
- ,onConfigChange: function ()
- {
+ ,onConfigChange: function() {
var fields = [
'realm'
,'family'
@@ -24,11 +21,10 @@ Hedera.Shelves = new Class
];
for (var i = 0; i < fields.length; i++)
- this.$(fields[i]).value = this.$('config').get (fields[i]);
+ this.$(fields[i]).value = this.$('config').get(fields[i]);
}
- ,onPreviewClick: function ()
- {
+ ,onPreviewClick: function() {
var fields = [
'family'
,'warehouse'
@@ -42,12 +38,12 @@ Hedera.Shelves = new Class
,'date'
];
- var batch = new Sql.Batch ();
+ var batch = new Sql.Batch();
for (var i = 0; i < fields.length; i++)
- batch.addValue (fields[i], this.$(fields[i]).value);
+ batch.addValue(fields[i], this.$(fields[i]).value);
- this.gui.openReport ('shelves-report', batch);
+ this.gui.openReport('shelves-report', batch);
}
});
diff --git a/forms/reports/shelves/ui.xml b/forms/reports/shelves/ui.xml
index e1fa2a94..cf6f7de0 100644
--- a/forms/reports/shelves/ui.xml
+++ b/forms/reports/shelves/ui.xml
@@ -16,7 +16,7 @@
+ on-click="this.onPreviewClick()"/>
@@ -27,8 +27,8 @@
id="config"
placeholder="_Select config"
model="configs-model"
- on-changed="onConfigChange"
- on-ready="onConfigChange"/>
+ on-changed="this.onConfigChange()"
+ on-ready="this.onConfigChange()"/>
diff --git a/js/hedera/form.js b/js/hedera/form.js
index b28fa327..3e73b5cc 100644
--- a/js/hedera/form.js
+++ b/js/hedera/form.js
@@ -1,6 +1,5 @@
-module.exports = new Class
-({
+module.exports = new Class({
Extends: Vn.Object
,isOpen: false
@@ -31,28 +30,27 @@ module.exports = new Class
return;
var builder = new Vn.Builder();
- builder.signalData = this;
- builder.add('conn', this.conn);
- builder.loadXml('forms/'+ this.formInfo.path +'/ui.xml');
+ builder.compileFile('forms/'+ this.formInfo.path +'/ui.xml');
- var res = this.builder = builder.load();
- this.node = res.$('form');
- res.link(this);
+ var scope = this.builder = builder.load(null, this);
+ scope.link({conn: this.conn});
- var models = res.getByTagName('db-model');
+ this.node = scope.$('form');
+
+ var models = scope.getByTagName('db-model');
for (var i = 0; i < models.length; i++)
models[i].conn = this.conn;
- var queries = res.getByTagName('db-query');
+ var queries = scope.getByTagName('db-query');
for (var i = 0; i < queries.length; i++)
queries[i].conn = this.conn;
if (this.node) {
this.gui.setForm(this.node);
- this.gui.setTitle(res.$('title'));
- this.gui.setActions(res.$('actions'));
+ this.gui.setTitle(scope.$('title'));
+ this.gui.setActions(scope.$('actions'));
this.activate();
}
diff --git a/js/hedera/report.js b/js/hedera/report.js
index 9129715b..ada4f97a 100644
--- a/js/hedera/report.js
+++ b/js/hedera/report.js
@@ -1,14 +1,12 @@
-module.exports = new Class
-({
+module.exports = new Class({
Extends: Vn.Object
- ,initialize: function (moduleInfo, gui)
- {
+ ,initialize: function(moduleInfo, gui) {
this.info = moduleInfo;
this.gui = gui;
this.conn = gui.conn;
- this.parent (null);
+ this.parent(null);
}
/**
@@ -16,88 +14,80 @@ module.exports = new Class
*
* @param {string} objectId The object identifier
* @return {Object} The object, or %null if not found
- **/
- ,$: function (objectId)
- {
- if (this.builderResult)
- return this.builderResult.getById (objectId);
+ */
+ ,$: function(objectId) {
+ if (this.scope)
+ return this.scope.getById(objectId);
return null;
}
- ,open: function (batch)
- {
+ ,open: function(batch) {
this.batch = batch;
- this.createWindow ();
+ this.createWindow();
}
- ,print: function ()
- {
- this.window.print ();
+ ,print: function() {
+ this.window.print();
}
- ,includeCss: function (path)
- {
+ ,includeCss: function(path) {
var basePath = location.protocol +'//'+ location.host;
basePath += location.port ? ':'+ location.port : '';
- basePath += location.pathname.substring (0,
- location.pathname.lastIndexOf ('/'));
+ basePath += location.pathname.substring(0,
+ location.pathname.lastIndexOf('/'));
- var link = this.doc.createElement ('link');
+ var link = this.doc.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
- link.href = basePath +'/'+ path + Vn.getVersion ();
+ link.href = basePath +'/'+ path + Vn.getVersion();
- var head = this.doc.getElementsByTagName ('head')[0];
- head.appendChild (link);
+ var head = this.doc.getElementsByTagName('head')[0];
+ head.appendChild(link);
}
- ,createWindow: function ()
- {
- var reportWindow = window.open (
+ ,createWindow: function() {
+ var reportWindow = window.open(
'js/hedera/report.html', '_blank',
'height=650, width=950, resizable=yes, fullscreen=no,'+
'titlebar=no, menubar=no, toolbar=no, location=no, scrollbars=yes'
);
- if (!reportWindow)
- {
- Htk.Toast.showError (
+ if (!reportWindow) {
+ Htk.Toast.showError(
_('Please unlock popups and try again'));
return false;
}
- reportWindow.addEventListener ('load',
- this._onWindowLoad.bind (this));
+ reportWindow.addEventListener('load',
+ this._onWindowLoad.bind(this));
this.window = reportWindow;
return true;
}
- ,_onWindowLoad: function ()
- {
+ ,_onWindowLoad: function() {
this.doc = this.window.document
- this.includeCss ('reports/'+ this.info.path +'/style.css');
+ this.includeCss('reports/'+ this.info.path +'/style.css');
- var printButton = this.doc.getElementById ('print');
- printButton.addEventListener ('click', this.print.bind (this));
- Vn.Node.setText (printButton, _('Print'));
+ var printButton = this.doc.getElementById('print');
+ printButton.addEventListener('click', this.print.bind(this));
+ Vn.Node.setText(printButton, _('Print'));
- this.onWindowCreate ();
+ this.onWindowCreate();
}
- ,onWindowCreate: function ()
- {
- var builder = new Vn.Builder ();
- builder.signalData = this;
- builder.add ('batch', this.batch);
- builder.add ('conn', this.conn);
- builder.loadXml ('reports/'+ this.info.path +'/ui.xml');
+ ,onWindowCreate: function() {
+ var builder = new Vn.Builder();
+ builder.compileFile('reports/'+ this.info.path +'/ui.xml');
- var res = this.builderResult = builder.load ();
- res.link ();
+ var scope = this.scope = builder.load(this.doc, this);
+ scope.link({
+ batch: this.batch,
+ conn: this.conn
+ });
- this.doc.body.appendChild (res.$('report'));
+ this.doc.body.appendChild(scope.$('report'));
}
});
diff --git a/js/htk/component.js b/js/htk/component.js
index 9f6465fb..2a0c8a8c 100644
--- a/js/htk/component.js
+++ b/js/htk/component.js
@@ -1,49 +1,42 @@
-var Widget = require ('./widget');
+const Widget = require('./widget');
-module.exports = new Class
-({
+module.exports = new Class({
Extends: Widget
- ,builder: null
+ ,scope: null
- ,builderInit: function (path)
- {
- var builder = new Vn.Builder ();
- builder.signalData = this;
- builder.loadXml (path, this.doc);
- this.builderResultInit (builder);
+ ,builderInit: function(path) {
+ const builder = new Vn.Builder();
+ builder.compileFile(path);
+ this.builderResultInit(builder);
}
- ,builderInitString: function (xmlString)
- {
- var builder = new Vn.Builder ();
- builder.signalData = this;
- builder.loadFromString (xmlString, this.doc);
- this.builderResultInit (builder);
+ ,builderInitString: function(xmlString) {
+ const builder = new Vn.Builder();
+ builder.compileString(xmlString);
+ this.builderResultInit(builder);
}
- ,builderResultInit: function (builder)
- {
- var res = this.builder = builder.load ();
- this._node = res.$('main');
- res.link ();
+ ,builderResultInit: function(builder) {
+ const scope = this.scope = builder.load(this.doc, this);
+ scope.link();
+
+ this._node = scope.$('main');
}
- ,$: function (id)
- {
- if (this.builder)
- return this.builder.getById (id);
+ ,$: function(id) {
+ if (this.scope)
+ return this.scope.getById(id);
return null;
}
- ,_destroy: function ()
- {
- if (this.builder)
- this.builder.unref ();
+ ,_destroy: function() {
+ if (this.scope)
+ this.scope.unref();
- this.parent ();
+ this.parent();
}
});
diff --git a/js/htk/field/radio.js b/js/htk/field/radio.js
index 103aec58..2ee3128e 100644
--- a/js/htk/field/radio.js
+++ b/js/htk/field/radio.js
@@ -1,75 +1,83 @@
-var RadioGroup = require ('./radio-group');
+var RadioGroup = require('./radio-group');
-module.exports = new Class
-({
+module.exports = new Class({
Extends: Htk.Field
,Tag: 'htk-radio'
- ,Properties:
- {
- tip:
- {
+ ,Properties: {
+ tip: {
type: String
- ,set: function (x)
- {
+ ,set: function(x) {
if (x)
this.node.title = _(x);
}
},
- radioGroup:
- {
+ val: {
+ type: String
+ ,get: function() {
+ return this._val;
+ }
+ ,set: function(x) {
+ this._val = x;
+ this.node.value = !x ? '' : x;
+ this._onRadioGroupChange();
+ }
+ },
+ name: {
+ type: String
+ ,get: function() {
+ return this.node.name;
+ }
+ ,set: function(x) {
+ this.node.name = x;
+ }
+ },
+ radioGroup: {
type: RadioGroup
- ,set: function (x)
- {
+ ,get: function() {
+ return this._radioGroup;
+ }
+ ,set: function(x) {
if (this._radioGroup)
- this._radioGroup.removeButton (this);
+ this._radioGroup.removeButton(this);
- this.link ({_radioGroup: x}, {'changed': this._onRadioGroupChange});
+ this.link({_radioGroup: x}, {'changed': this._onRadioGroupChange});
this.node.name = x.name;
x.buttons.push(this);
- this._onRadioGroupChange ();
- }
- ,get: function ()
- {
- return this._radioGroup;
+ this._onRadioGroupChange();
}
}
}
,_radioGroup: null
- ,render: function ()
- {
- var radio = Vn.Browser.createRadio ('', this.doc);
+ ,render: function() {
+ var radio = Vn.Browser.createRadio('', this.doc);
radio.checked = false;
- radio.addEventListener ('change', this._onChange.bind (this));
+ radio.addEventListener('change', this._onChange.bind(this));
this._node = radio;
}
- ,_onChange: function ()
- {
+ ,_onChange: function() {
+ console.log(this._val);
if (this.node.checked && this._radioGroup)
- this._radioGroup.value = this.value;
+ this._radioGroup.value = this._val || this.value;
}
- ,_onRadioGroupChange: function ()
- {
- if (this._radioGroup.value && this._radioGroup.value == this.value)
- this.node.checked = true;
- else
- this.node.checked = false;
+ ,_onRadioGroupChange: function() {
+ const value = this._radioGroup.value;
+ this.node.checked =
+ value && (value == this._val || value == this.value);
}
- ,putValue: function (value)
- {
+ ,putValue: function(value) {
if (!value)
this.node.value = '';
else
this.node.value = value;
}
- ,setEditable: function (editable)
- {
+ ,setEditable: function(editable) {
this.node.disabled = !editable;
}
});
diff --git a/js/htk/node-builder.js b/js/htk/node-builder.js
index fa28a1c0..952682f6 100644
--- a/js/htk/node-builder.js
+++ b/js/htk/node-builder.js
@@ -1,26 +1,22 @@
-module.exports = new Class
-({
+module.exports = new Class({
Extends: Vn.Object
,doc: null
- ,initialize: function (props)
- {
+ ,initialize: function(props) {
this.doc = document;
- this.parent (props);
+ this.parent(props);
}
- ,createElement: function (tagName)
- {
- return document.createElement (tagName);
+ ,createElement: function(tagName) {
+ return document.createElement(tagName);
}
- ,createTextNode: function (text)
- {
- return document.createTextNode (text);
+ ,createTextNode: function(text) {
+ return document.createTextNode(text);
}
- ,render: function () {}
+ ,render: function() {}
});
diff --git a/js/htk/repeater.js b/js/htk/repeater.js
index 183e01c2..86aa0348 100644
--- a/js/htk/repeater.js
+++ b/js/htk/repeater.js
@@ -9,13 +9,12 @@ module.exports = new Class({
{
/**
* The source data model.
- **/
+ */
model:
{
type: Db.Model
,set: function(x) {
- this.link({_model: x},
- {
+ this.link({_model: x}, {
'status-changed': this._onModelChange
,'row-deleted': this._onRowDelete
,'row-updated': this._onRowUpdate
@@ -30,7 +29,7 @@ module.exports = new Class({
}
/**
* The identifier for internal iterator.
- **/
+ */
,formId:
{
type: String
@@ -44,7 +43,7 @@ module.exports = new Class({
/**
* {Function (Vn.BuilderResult, Db.Form)} Function to call after every
* box rendering.
- **/
+ */
,renderer:
{
type: Function
@@ -57,7 +56,7 @@ module.exports = new Class({
}
/**
* Wether to show the model status.
- **/
+ */
,showStatus:
{
type: Boolean
@@ -71,7 +70,7 @@ module.exports = new Class({
}
/**
* Message that should be displayed when source model is not ready.
- **/
+ */
,emptyMessage:
{
type: String
@@ -91,12 +90,12 @@ module.exports = new Class({
div.appendChild(this._container);
}
- ,loadXml: function(builderResult, node) {
- this.parent(builderResult, node);
+ ,loadXml: function(scope, node) {
+ this.parent(scope, node);
+ this._parentScope = scope;
var builder = this._builder = new Vn.Builder();
- builder.setParent(builderResult);
- builder.loadXmlFromNode(node.firstElementChild, null, [this._formId]);
+ builder.compileNode(node.firstElementChild, [this._formId]);
this._onModelChange();
}
@@ -118,20 +117,21 @@ module.exports = new Class({
model: this._model,
row: index
});
-
- this._builder.add(this._formId, set);
- var res = this._builder.load();
- res.link(null, [set.getObject()]);
+
+ var scope = this._builder.load(this.doc, null, this._parentScope);
+ scope.link([set.getObject()], {
+ [this._formId]: set
+ });
this._childsData.push({
- builder: res,
+ builder: scope,
set: set
});
if (this._renderer)
- this._renderer(res, set);
+ this._renderer(scope, set);
- return res.getMain();
+ return scope.getMain();
}
,_onModelChange: function() {
diff --git a/js/htk/style/variables.scss b/js/htk/style/variables.scss
index aa5a68f3..acd48645 100644
--- a/js/htk/style/variables.scss
+++ b/js/htk/style/variables.scss
@@ -1,2 +1,3 @@
-$color-hover-cd: rgba(255, 255, 255, .1);
\ No newline at end of file
+$color-primary: #8cc63f;
+$color-hover-cd: rgba(255, 255, 255, .1);
diff --git a/js/htk/widget.js b/js/htk/widget.js
index 612283e7..0e6cacdc 100644
--- a/js/htk/widget.js
+++ b/js/htk/widget.js
@@ -1,36 +1,30 @@
-var NodeBuilder = require ('./node-builder');
+var NodeBuilder = require('./node-builder');
-module.exports = new Class
-({
+module.exports = new Class({
Extends: NodeBuilder
,Properties:
{
/**
* Main HTML node that represents the widget
- **/
- node:
- {
+ */
+ node: {
type: Object
- ,get: function ()
- {
- this.renderBase ();
+ ,get: function() {
+ this.renderBase();
return this._node;
}
},
/**
* CSS classes to be appendend to the node classes.
- **/
- class:
- {
+ */
+ class: {
type: String
- ,set: function (x)
- {
+ ,set: function(x) {
this._cssClass = x;
- this._refreshClass ();
+ this._refreshClass();
}
- ,get: function ()
- {
+ ,get: function() {
return this._node.className;
}
}
@@ -38,36 +32,31 @@ module.exports = new Class
,_node: null
- ,initialize: function (props)
- {
+ ,initialize: function(props) {
this.doc = document;
- this.renderBase ();
- this.parent (props);
+ this.renderBase();
+ this.parent(props);
}
- ,createRoot: function (tagName)
- {
- return this._node = this.createElement (tagName);
+ ,createRoot: function(tagName) {
+ return this._node = this.createElement(tagName);
}
- ,renderBase: function ()
- {
+ ,renderBase: function() {
if (this._node)
return;
- this.render ();
- this._refreshClass ();
+ this.render();
+ this._refreshClass();
}
- ,_refreshClass: function ()
- {
+ ,_refreshClass: function() {
if (this._node && this._cssClass)
this._node.className = this._cssClass +' '+ this._node.className;
}
- ,remove: function ()
- {
- Vn.Node.remove (this._node);
+ ,remove: function() {
+ Vn.Node.remove(this._node);
}
});
diff --git a/js/sql/batch.js b/js/sql/batch.js
index 409423cb..45bdff87 100644
--- a/js/sql/batch.js
+++ b/js/sql/batch.js
@@ -1,6 +1,6 @@
-var Object = require ('./object');
-var Value = require ('./value');
+var Object = require('./object');
+var Value = require('./value');
/**
* A map container for many Sql.Object
@@ -14,12 +14,10 @@ module.exports = new Class
blocked:
{
type: Boolean
- ,set: function (x)
- {
+ ,set: function(x) {
this._blocked = x;
}
- ,get: function ()
- {
+ ,get: function() {
return this._blocked;
}
}
@@ -28,75 +26,64 @@ module.exports = new Class
,objects: {}
,_blocked: false
- ,loadXml: function (builder, node)
- {
- this.parent (builder, node);
+ ,loadXml: function(scope, node) {
+ this.parent(scope, node);
var childs = node.childNodes;
for (var i = 0; i < childs.length; i++)
- if (childs[i].tagName && childs[i].tagName.toLowerCase () == 'item')
- {
+ if (childs[i].tagName && childs[i].tagName.toLowerCase() == 'item') {
var object;
- var id = childs[i].getAttribute ('name');
+ var id = childs[i].getAttribute('name');
- if (id)
- {
- if (object = builder.getById (childs[i].getAttribute ('param')))
- this.addParam (id, object);
- else if (object = builder.getById (childs[i].getAttribute ('object')))
- this.addObject (id, object);
+ if (id) {
+ if (object = scope.getById(childs[i].getAttribute('param')))
+ this.addParam(id, object);
+ else if (object = scope.getById(childs[i].getAttribute('object')))
+ this.addObject(id, object);
}
}
}
- ,get: function (id)
- {
+ ,get: function(id) {
if (this.objects[id])
return this.objects[id];
return null;
}
- ,add: function (id)
- {
+ ,add: function(id) {
if (!this.objects[id])
this.objects[id] = null;
}
- ,_addObject: function (id, object)
- {
- this.remove (id);
+ ,_addObject: function(id, object) {
+ this.remove(id);
this.objects[id] = object;
- object.on ('changed', this.emitChanged, this);
- this.emitChanged ();
+ object.on('changed', this.emitChanged, this);
+ this.emitChanged();
}
- ,addObject: function (id, object)
- {
- this._addObject (id, object.ref ());
+ ,addObject: function(id, object) {
+ this._addObject(id, object.ref());
}
- ,addValue: function (id, value)
- {
- this._addObject (id,
- new Value ({value: value}));
+ ,addValue: function(id, value) {
+ this._addObject(id,
+ new Value({value: value}));
}
- ,addValues: function (values)
- {
+ ,addValues: function(values) {
for (var id in values)
- this.addValue (id, values[id]);
+ this.addValue(id, values[id]);
}
- ,addParam: function (id, param)
- {
- this._addObject (id,
- new Value ({param: param}));
+ ,addParam: function(id, param) {
+ this._addObject(id,
+ new Value({param: param}));
}
- ,getValue: function (id)
- {
+ ,getValue: function(id) {
var object = this.objects[id];
if (object instanceof Value)
@@ -105,65 +92,54 @@ module.exports = new Class
return null;
}
- ,addParams: function (params)
- {
+ ,addParams: function(params) {
for (var id in params)
- this.addParam (id, params[id]);
+ this.addParam(id, params[id]);
}
- ,remove: function (id)
- {
- if (this.objects[id])
- {
- this._unrefObject (this.objects[id]);
+ ,remove: function(id) {
+ if (this.objects[id]) {
+ this._unrefObject(this.objects[id]);
delete this.objects[id];
}
}
- ,block: function ()
- {
+ ,block: function() {
this._blocked = true;
}
- ,unblock: function ()
- {
+ ,unblock: function() {
this._blocked = false;
}
- ,emitChanged: function ()
- {
+ ,emitChanged: function() {
if (!this._blocked)
- this.signalEmit ('changed');
+ this.signalEmit('changed');
}
- ,changed: function ()
- {
- this.signalEmit ('changed');
+ ,changed: function() {
+ this.signalEmit('changed');
}
- ,isReady: function ()
- {
+ ,isReady: function() {
for (var id in this.objects)
- if (!(this.objects[id] && this.objects[id].isReady ()))
+ if (!(this.objects[id] && this.objects[id].isReady()))
return false;
return true;
}
- ,_unrefObject: function (object)
- {
- if (object)
- {
- object.disconnect ('changed', this.emitChanged, this);
- object.unref ();
+ ,_unrefObject: function(object) {
+ if (object) {
+ object.disconnect('changed', this.emitChanged, this);
+ object.unref();
}
}
- ,_destroy: function ()
- {
+ ,_destroy: function() {
for (var id in this.objects)
- this._unrefObject (this.objects[id]);
+ this._unrefObject(this.objects[id]);
- this.parent ();
+ this.parent();
}
});
diff --git a/js/vn/builder.js b/js/vn/builder.js
index ff38cce1..13e41a77 100644
--- a/js/vn/builder.js
+++ b/js/vn/builder.js
@@ -1,100 +1,62 @@
-
-var Object = require('./object');
+const VnObject = require('./object');
+const Scope = require('./scope');
+const kebabToCamel = require('./string-util').kebabToCamel;
/**
* Creates a object from a XML specification.
*/
module.exports = new Class({
- Extends: Object
- ,_addedMap: {}
+ Extends: VnObject
,_contexts: null
-
- ,add: function(id, object) {
- 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(result, objectId) {
- var index = this._contextMap[objectId];
-
- if (index !== undefined)
- return result.objects[index];
-
- var object = this._addedMap[objectId];
-
- if (object !== undefined)
- return object;
-
- if (this._parentResult)
- return this._parentResult.getById(objectId);
-
- return null;
- }
-
- ,getByTagName: function(result, tagName) {
- 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 [];
- }
/**
* Compiles an XML file.
*
- * @path String The XML path
- * @dstDocument Document The document used to create the nodes
- * @return %true on success, %false othersise
+ * @param {String} path The XML path
+ * @return {Boolean} %true on success, %false othersise
*/
- ,loadXml: function(path, dstDocument) {
+ ,compileFile: function(path) {
this._path = path;
- return this.loadFromXmlDoc(Vn.getXml(path), dstDocument);
- }
-
- ,loadFromString: function(xmlString, dstDocument) {
+ return this.compileDocument(Vn.getXml(path));
+ }
+
+ /**
+ * Compiles an XML string.
+ *
+ * @param {String} xmlString The XML string
+ * @return {Boolean} %true on success, %false othersise
+ */
+ ,compileString: function(xmlString) {
var parser = new DOMParser();
- var xmlDoc = parser.parseFromString(xmlString, 'text/xml');
- return this.loadFromXmlDoc(xmlDoc, dstDocument);
- }
-
- ,loadFromXmlDoc: function(xmlDoc, dstDocument, scope) {
- if (!xmlDoc)
- return false;
+ var doc = parser.parseFromString(xmlString, 'text/xml');
+ return this.compileDocument(doc);
+ }
- this._compileInit(dstDocument, scope);
+ /**
+ * Compiles a XML document.
+ *
+ * @param {Document} doc The DOM document
+ * @return {Boolean} %true on success, %false othersise
+ */
+ ,compileDocument: function(doc, exprArgs) {
+ if (!doc)
+ return false;
- var docElement = xmlDoc.documentElement;
+ this._preCompile(exprArgs);
+ var docElement = doc.documentElement;
if (docElement.tagName !== 'vn') {
- this._showError('Malformed XML');
+ this.showError('The toplevel tag should be named \'vn\'');
this._contexts = null;
return false;
}
var childs = docElement.childNodes;
-
if (childs)
for (var i = 0; i < childs.length; i++)
- this._compileNode(childs[i]);
+ this._compile(childs[i]);
- this._compileEnd();
+ this._postCompile();
return true;
}
@@ -102,87 +64,171 @@ module.exports = new Class({
* Compiles a single DOM node.
*
* @path Node The DOM node
- * @dstDocument Document The document used to create the nodes
* @return %true on success, %false othersise
*/
- ,loadXmlFromNode: function(node, dstDocument, scope) {
- this._compileInit(dstDocument, scope);
- this._mainContext = this._compileNode(node).id;
- this._compileEnd();
+ ,compileNode: function(node, exprArgs) {
+ this._preCompile(exprArgs);
+ this._mainContext = this._compile(node).id;
+ this._postCompile();
return true;
}
+
+ /**
+ * Called before starting to compile nodes.
+ */
+ ,_preCompile: function(exprArgs) {
+ this._path = null;
+ this._tags = {};
+ this._contexts = [];
+ this._contextMap = {};
+ this._links = [];
+ this._mainContext = null;
+
+ this._baseExprArgs = ['_', '$'];
+ if (exprArgs)
+ this._baseExprArgs = this._baseExprArgs.concat(exprArgs);
+
+ this._baseEventArgs = this._baseExprArgs.concat(['$event']);
+
+ this._exprArgs = this._baseExprArgs.join(',');
+ this._eventArgs = this._baseEventArgs.join(',');
+ }
- ,load: function() {
+ /**
+ * Called after all nodes have been compiled.
+ */
+ ,_postCompile: function() {}
+
+ /**
+ * Compiles a node.
+ */
+ ,_compile: function(node) {
+ let context = null;
+ let tagName = null;
+ const isElement = node.nodeType === Node.ELEMENT_NODE;
+
+ if (isElement)
+ tagName = node.tagName.toLowerCase();
+ else if (node.nodeType !== Node.TEXT_NODE
+ || /^[\n\r\t]*$/.test(node.textContent))
+ return null;
+
+ context =
+ this.textCompile(node, tagName)
+ || this.objectCompile(node, tagName)
+ || this.elementCompile(node, tagName);
+
+ context.id = this._contexts.length;
+
+ if (isElement) {
+ var nodeId = node.getAttribute('id');
+
+ if (nodeId)
+ this._contextMap[kebabToCamel(nodeId)] = context.id;
+
+ var tags = this._tags[tagName];
+
+ if (!tags)
+ this._tags[tagName] = tags = [];
+
+ tags.push(context.id);
+ }
+
+ this._contexts.push(context);
+ return context;
+ }
+
+ ,getMain: function(scope) {
+ return scope.objects[this._mainContext];
+ }
+
+ ,getByTagName: function(scope, tagName) {
+ var tags = this._tags[tagName];
+
+ if (tags) {
+ var arr = new Array(tags.length);
+
+ for (var i = 0; i < tags.length; i++)
+ arr[i] = scope.objects[tags[i]];
+
+ return arr;
+ }
+
+ return [];
+ }
+
+ ,load: function(dstDocument, thisArg, parentScope) {
if (this._contexts === null)
return null;
-
- var contexts = this._contexts;
- var len = contexts.length;
- var objects = new Array(len);
+
+ const contexts = this._contexts;
+ const len = contexts.length;
+ const objects = new Array(len);
+ const doc = dstDocument ? dstDocument : document;
for (var i = 0; i < len; i++) {
var context = contexts[i];
if (context.tagName)
- objects[i] = this.elementInstantiate(context);
+ objects[i] = this.elementInstantiate(doc, context);
else if (context.klass)
- objects[i] = this.objectInstantiate(context);
+ objects[i] = this.objectInstantiate(doc, context);
else
- objects[i] = this.textInstantiate(context);
+ objects[i] = this.textInstantiate(doc, context);
}
- return new BuilderResult(this, objects);
+ return new Scope(this, objects, thisArg, parentScope);
}
- ,link: function(result, self, scope) {
- var objects = result.objects;
-
- for (var i = this._links.length - 1; i >= 0; i--) {
- var l = this._links[i];
- var addedObject = this._addedMap[l.objectId];
+ ,link: function(scope, exprScope) {
+ const objects = scope.objects;
+ const links = this._links;
- 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);
+ // Pre-link
+
+ for (var 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);
}
- this.linkExpr(result, self, scope);
+ // Post-link
- var contexts = this._contexts;
+ const baseExprScope = [
+ _,
+ scope._$
+ ].concat(exprScope);
+
+ this.linkExpr(scope, baseExprScope);
+
+ const contexts = this._contexts;
for (var i = 0; i < contexts.length; i++) {
- var context = contexts[i];
- var object = objects[i];
+ const context = contexts[i];
+ const object = objects[i];
if (context.tagName)
- this.elementLink(context, object, objects, result);
+ this.elementLink(context, object, objects, scope, baseExprScope);
else if (context.klass)
- this.objectLink(context, object, objects, result);
+ this.objectLink(context, object, objects, scope, baseExprScope);
}
}
- ,fnExpr(expr) {
- return new Function(this._scopeArgs,
- '"use strict"; return ' + expr + ';'
- );
- }
-
- ,matchExpr(value) {
- const match = /^{{(.*)}}$/.exec(value);
- if (!match) return null;
- return this.fnExpr(match[1]);
- }
-
- ,linkExpr(result, self, scope) {
+ ,linkExpr(scope, baseScope, exprScope) {
const contexts = this._contexts;
- const objects = result.objects;
- let args = [_]
+ const objects = scope.objects;
- if (scope) args = args.concat(scope);
+ exprScope = baseScope.concat(exprScope);
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
@@ -193,7 +239,7 @@ module.exports = new Class({
for (expr of context.exprs) {
let value = undefined;
try {
- value = expr.apply(self, args);
+ value = expr.apply(scope.thisArg, exprScope);
} catch (e) {
console.warn('Expression error:', e.message);
continue;
@@ -212,7 +258,7 @@ module.exports = new Class({
for (const prop in dynProps) {
let value = undefined;
try {
- value = dynProps[prop].apply(self, args);
+ value = dynProps[prop].apply(scope.thisArg, exprScope);
} catch (e) {
console.warn('Expression error:', e.message);
continue;
@@ -227,82 +273,80 @@ module.exports = new Class({
}
}
- ,_compileInit: function(dstDocument, scope) {
- this._path = null;
- this._tags = {};
- this._contexts = [];
- this._contextMap = {};
- this._links = [];
- this._mainContext = null;
- this._doc = dstDocument ? dstDocument : document;
-
- this._scope = ['_'];
- if (scope)
- this._scope = this._scope.concat(scope);
- this._scopeArgs = this._scope.join(',');
+ ,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);
}
- ,_compileEnd: function() {
- for (var i = this._links.length - 1; i >= 0; i--) {
- var l = this._links[i];
- var contextId = this._contextMap[l.objectId];
+ ,_addLink: function(context, prop, objectId) {
+ this._links.push({
+ context
+ ,prop
+ ,objectId: kebabToCamel(objectId)
+ });
+ }
+
+ ,fnExpr(expr) {
+ return new Function(this._exprArgs,
+ '"use strict"; return ' + expr + ';'
+ );
+ }
+
+ ,matchExpr(value) {
+ const match = /^{{(.*)}}$/.exec(value);
+ if (!match) return null;
+ return this.fnExpr(match[1]);
+ }
+
+ ,_translateValue: function(value) {
+ var chr = value.charAt(0);
+
+ if (chr === '_')
+ return _(value.substr(1));
+ else if (chr === '\\' && value.charAt(1) === '_')
+ return value.substr(1);
- if (contextId != undefined) {
- if (l.prop)
- l.context.objectProps[l.prop] = contextId;
- else
- l.context.childs.push(contextId);
-
- this._links.splice(i, 1);
- } else {
- var object = this._addedMap[l.objectId];
-
- if (!object && this._parentResult)
- object = this._parentResult.getById(l.objectId);
-
- if (object) {
- l.context.props[l.prop] = object;
- this._links.splice(i, 1);
- }
+ return value;
+ }
+
+ ,_getMethod: function(value) {
+ let method;
+
+ if (this.isIdentifier(value)) {
+ // XXX: Compatibility with old events
+ method = value;
+ } else {
+ try {
+ method = new Function(this._eventArgs,
+ '"use strict"; return ' + value + ';'
+ );
+ } catch (err) {
+ this.showError(`Method: ${err.message}: ${value}`);
}
}
+
+ return method;
}
-
- ,_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
- || /^[\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 (!tags)
- this._tags[tagName] = tags = [];
-
- tags.push(context.id);
- }
-
- this._contexts.push(context);
- return context;
+ ,_isEvent: function(attribute) {
+ return /^on-\w+/.test(attribute);
}
+ ,isIdentifier: function(value) {
+ return /^[a-zA-Z_$][\w$]*$/.test(value);
+ }
+
+ ,_replaceFunc: function(token) {
+ return token.charAt(1).toUpperCase();
+ }
+
+ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TextNode
+
/**
* Creates a text node context.
*/
@@ -328,9 +372,11 @@ module.exports = new Class({
return null;
}
- ,textInstantiate: function(context) {
- return this._doc.createTextNode(context.exprs ? '' : context.text);
+ ,textInstantiate: function(doc, context) {
+ return doc.createTextNode(context.exprs ? '' : context.text);
}
+
+ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Vn.Object
/**
* Creates a object context.
@@ -342,7 +388,6 @@ module.exports = new Class({
return null;
var props = {};
- var dynProps = {};
var objectProps = {};
var childs = [];
var events = {};
@@ -350,7 +395,8 @@ module.exports = new Class({
var context = {
klass: klass,
props: props,
- dynProps: dynProps,
+ dynProps: {},
+ funcProps: {},
objectProps: objectProps,
childs: childs,
events: events,
@@ -369,7 +415,7 @@ module.exports = new Class({
if (handler)
events[attribute.substr(3)] = handler;
} else if (!/^(id|property)$/.test(attribute)) {
- this.propCompile(context, klass, props, dynProps,
+ this.propCompile(context, klass, props,
node, attribute, value);
}
}
@@ -387,7 +433,7 @@ module.exports = new Class({
this._addLink(context, null, child.getAttribute('object'));
} else if (childTagName === 'custom') {
context.custom = child;
- } else if (childContext = this._compileNode(child)) {
+ } else if (childContext = this._compile(child)) {
var prop = isElement ? child.getAttribute('property') : null;
if (prop) {
@@ -401,19 +447,20 @@ module.exports = new Class({
return context;
}
- ,propCompile: function(context, klass, props, dynProps, node, attribute, value) {
- var isLink = false;
- var newValue = null;
- var propName = attribute.replace(/-./g, this._replaceFunc);
- var propInfo = klass.Properties[propName];
+ ,propCompile: function(context, klass, props, node, attribute, value) {
+ let isLink = false;
+ let propError = false;
+ let newValue = null;
+ const propName = attribute.replace(/-./g, this._replaceFunc);
+ const propInfo = klass.Properties[propName];
if (!propInfo) {
- this._showError('Attribute \'%s\' not valid for tag \'%s\'',
+ this.showError('Attribute \'%s\' not valid for tag \'%s\'',
attribute, node.tagName);
return;
}
if (!value) {
- this._showError('Attribute \'%s\' empty on tag \'%s\'',
+ this.showError('Attribute \'%s\' empty on tag \'%s\'',
attribute, node.tagName);
return;
}
@@ -421,7 +468,7 @@ module.exports = new Class({
const expr = this.matchExpr(value);
if (expr) {
- dynProps[propName] = expr;
+ context.dynProps[propName] = expr;
} else {
switch (propInfo.type) {
case Boolean:
@@ -434,48 +481,88 @@ module.exports = new Class({
newValue = this._translateValue(value);
break;
case Function:
- var method = this._getMethod(value);
- newValue = method ? method.bind(this.signalData) : null;
+ context.funcProps[propName] = this._getMethod(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)
props[propName] = newValue;
- else
- this._showError('Attribute \'%s\' invalid for tag \'%s\'',
+ else if (propError)
+ this.showError('Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
}
- ,objectInstantiate: function(context) {
+ ,objectInstantiate: function(doc, context) {
return new context.klass();
}
- ,objectLink: function(context, object, objects, res) {
+ ,objectLink: function(context, object, objects, scope, exprScope) {
object.setProperties(context.props);
- var objectProps = context.objectProps;
- for (var prop in objectProps)
+ const objectProps = context.objectProps;
+ for (const prop in objectProps)
object[prop] = objects[objectProps[prop]];
- var childs = context.childs;
- for (var i = 0; i < childs.length; i++)
+ const childs = context.childs;
+ for (let 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], this.signalData);
+
+ const funcProps = context.funcProps;
+ for (const prop in funcProps) {
+ let method;
+ const handler = funcProps[prop];
+
+ if (typeof handler === 'string') {
+ // XXX: Compatibility with old expressions
+ method = scope.thisArg[handler];
+ if (!method)
+ this.showError(`Function '${handler}' not found`);
+ method = method.bind(scope.thisArg);
+ } else {
+ method = function() {
+ handler.apply(scope.thisArg, exprScope);
+ };
+ }
+
+ if (method)
+ object[prop] = method;
+ }
+
+ const events = context.events;
+ for (const event in events) {
+ let listener;
+ const handler = events[event];
+
+ if (typeof handler === 'string') {
+ // XXX: Compatibility with old expressions
+ listener = scope.thisArg[handler];
+ if (!listener)
+ this.showError(`Function '${handler}' not found`);
+ } else {
+ listener = function() {
+ handler.apply(scope.thisArg, exprScope.concat(arguments));
+ };
+ }
+
+ if (listener)
+ object.on(event, listener, scope.thisArg);
+ }
if (context.custom)
- object.loadXml(res, context.custom);
+ object.loadXml(scope, context.custom);
}
+
+ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Element
/**
* Creates a HTML node context.
@@ -492,17 +579,17 @@ module.exports = new Class({
for (var i = 0; i < a.length; i++) {
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
- const expr = this.matchExpr(value);
- if (expr) {
- dynProps[attribute] = expr;
- } else if (this._isEvent(attribute)) {
+ if (this._isEvent(attribute)) {
var handler = this._getMethod(value);
-
- if (handler)
- events[attribute.substr(3)] = handler;
- } else if (attribute !== 'id')
- attributes[attribute] = this._translateValue(value);
+ if (handler) events[attribute.substr(3)] = handler;
+ } else if (attribute !== 'id') {
+ const expr = this.matchExpr(value);
+ if (expr)
+ dynProps[attribute] = expr;
+ else
+ attributes[attribute] = this._translateValue(value);
+ }
}
var childContext;
@@ -510,7 +597,7 @@ module.exports = new Class({
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
- if (childContext = this._compileNode(childNodes[i]))
+ if (childContext = this._compile(childNodes[i]))
childs.push(childContext.id);
return {
@@ -522,18 +609,18 @@ module.exports = new Class({
};
}
- ,elementInstantiate: function(context) {
- return this._doc.createElement(context.tagName);
+ ,elementInstantiate: function(doc, context) {
+ return doc.createElement(context.tagName);
}
- ,elementLink: function(context, object, objects) {
- var attributes = context.attributes;
- for (var attribute in attributes)
+ ,elementLink: function(context, object, objects, scope, exprScope) {
+ const attributes = context.attributes;
+ for (const attribute in attributes)
object.setAttribute(attribute, attributes[attribute]);
- var childs = context.childs;
+ const childs = context.childs;
for (var i = 0; i < childs.length; i++) {
- var child = objects[childs[i]];
+ let child = objects[childs[i]];
if (child instanceof Htk.Widget)
child = child.node;
@@ -541,98 +628,24 @@ module.exports = new Class({
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];
+ const events = context.events;
+ for (const event in events) {
+ let listener;
+ const handler = events[event];
+ if (typeof handler === 'string') {
+ // XXX: Compatibility with old expressions
+ listener = scope.thisArg[handler];
+ if (!listener)
+ this.showError(`Function '${handler}' not found`);
+ listener = listener.bind(scope.thisArg);
+ } else {
+ listener = function(e) {
+ handler.apply(scope.thisArg, exprScope.concat(e));
+ };
+ }
- 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
- });
- }
-
- ,_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 method = this.signalData[value];
- else
- var method = window[value];
-
- if (method === undefined)
- this._showError('Function \'%s\' not found', value);
-
- return method;
- }
-
- ,_isEvent: function(attribute) {
- return /^on-\w+/.test(attribute);
- }
-
- ,_replaceFunc: function(token) {
- return token.charAt(1).toUpperCase();
+ if (listener)
+ object.addEventListener(event, listener);
+ }
}
});
-
-var BuilderResult = new Class({
- Extends: Object
-
- ,initialize: function(builder, objects) {
- this.builder = builder;
- this.objects = objects;
- }
-
- ,getMain: function() {
- return this.builder.getMain(this);
- }
-
- ,$: function(objectId) {
- return this.builder.getById(this, objectId);
- }
-
- ,getById: function(objectId) {
- return this.builder.getById(this, objectId);
- }
-
- ,getByTagName: function(tagName) {
- return this.builder.getByTagName(this, tagName);
- }
-
- ,link: function(self, scope) {
- this.builder.link(this, self, scope);
- }
-
- ,_destroy: function() {
- var objects = this.objects;
-
- for (var i = 0; i < objects.length; i++)
- if (objects[i] instanceof Object)
- objects[i].unref();
-
- this.parent();
- }
-});
-
diff --git a/js/vn/scope.js b/js/vn/scope.js
new file mode 100644
index 00000000..d9684b93
--- /dev/null
+++ b/js/vn/scope.js
@@ -0,0 +1,60 @@
+const VnObject = require('./object');
+const kebabToCamel = require('./string-util').kebabToCamel;
+
+module.exports = new Class({
+ Extends: VnObject
+
+ ,initialize: function(builder, objects, thisArg, parentScope) {
+ this.builder = builder;
+ this.objects = objects;
+ this.thisArg = thisArg;
+ this.parentScope = parentScope;
+
+ if (!thisArg && parentScope)
+ this.thisArg = parentScope.thisArg;
+ }
+
+ ,link: function(exprScope, extraObjects) {
+ var contextMap = this.builder._contextMap;
+ var objectMap = this.parentScope ? Object.create(this.parentScope._$) : {};
+ this._$ = objectMap;
+
+ for (var id in extraObjects)
+ objectMap[id] = extraObjects[id];
+ for (var id in contextMap)
+ objectMap[id] = this.objects[contextMap[id]];
+
+ this.builder.link(this, exprScope);
+ }
+
+ ,getMain: function() {
+ return this.builder.getMain(this);
+ }
+
+ ,$: function(objectId) {
+ if (!objectId) return null;
+ return this._$[kebabToCamel(objectId)];
+ }
+
+ ,getById: function(objectId) {
+ return this.$(objectId);
+ }
+
+ ,getByTagName: function(tagName) {
+ return this.builder.getByTagName(this, tagName);
+ }
+
+ ,getHtmlId: function(nodeId) {
+ return 'vn-'+ this.uid +'-'+ nodeId;
+ }
+
+ ,_destroy: function() {
+ var objects = this.objects;
+
+ for (var i = 0; i < objects.length; i++)
+ if (objects[i] instanceof VnObject)
+ objects[i].unref();
+
+ this.parent();
+ }
+});
diff --git a/js/vn/string-util.js b/js/vn/string-util.js
new file mode 100644
index 00000000..ccd01c7b
--- /dev/null
+++ b/js/vn/string-util.js
@@ -0,0 +1,27 @@
+module.exports = {
+ kebabToCamel: kebabToCamel,
+ kebabToPascal: kebabToPascal
+};
+
+/**
+ * Converts a kebab-case (hyphenized) string to camelCase (lowerCamelCase).
+ *
+ * @param {String} string The kebab-case string
+ * @return {String} The string parsed to camelCase
+ */
+function kebabToCamel(string) {
+ function replaceFunc(token) {
+ return token.charAt(1).toUpperCase();
+ }
+ return string.replace(/-./g, replaceFunc);
+}
+/**
+ * Converts a kebab-case (hyphenized) string to PascalCase (UpperCamelCase).
+ *
+ * @param {String} string The kebab-case string
+ * @return {String} The string parsed to PascalCase
+ */
+function kebabToPascal(string) {
+ string = string.charAt(0).toUpperCase() + string.substr(1);
+ return kebabToCamel(string);
+}