diff --git a/forms/account/address-list/address-list.js b/forms/account/address-list/address-list.js index 1be4719e..af8cf66d 100644 --- a/forms/account/address-list/address-list.js +++ b/forms/account/address-list/address-list.js @@ -19,21 +19,23 @@ Hedera.AddressList = new Class window.history.back(); } - ,onSetDefaultClick: function() { + ,onSetDefaultClick: function(event, addressId) { + if (event.defaultPrevented) return; + this.$('defaultAddress').value = addressId; Htk.Toast.showMessage(_('DefaultAddressModified')); } - ,onRemoveAddressClick: function(button, form) { + ,onRemoveAddressClick: function(form) { if (confirm(_('AreYouSureDeleteAddress'))) { form.set('isActive', false); form.refresh(); } } - ,onEditAddressClick: function(button, form) { + ,onEditAddressClick: function(id) { this.hash.set({ form: 'account/address', - address: form.get('id') + address: id }); } }); diff --git a/forms/account/address-list/ui.xml b/forms/account/address-list/ui.xml index e113593b..594f7b67 100644 --- a/forms/account/address-list/ui.xml +++ b/forms/account/address-list/ui.xml @@ -25,7 +25,7 @@ + on-click="this.onAddAddressClick()"/>
@@ -33,40 +33,38 @@ id="default-address" column="defaultAddressFk" form="user-form"/> - + -
+
+ val="{{address.id}}" + tip="_SetAsDefault" + name="test"/>

- {{iter.nickname}} + {{address.nickname}}

- {{iter.street}} + {{address.street}}

- {{iter.postalCode}}, {{iter.city}} + {{address.postalCode}}, {{address.city}}

-
+
+ tip="_RemoveAddress" + on-click="this.onRemoveAddressClick($.address)"/> + tip="_EditAddress" + on-click="this.onEditAddressClick(address.id)"/>
diff --git a/forms/account/address/address.js b/forms/account/address/address.js index db430998..163c57aa 100644 --- a/forms/account/address/address.js +++ b/forms/account/address/address.js @@ -8,9 +8,9 @@ Hedera.Address = new Class({ new Sql.Function({schema: 'account', name: 'myUser_getId'})); }, - onStatusChange: function(form) { - if (form.ready && this.$('address').value == 0) - form.insertRow(); + onStatusChange: function() { + if (this.$('iter').ready && this.$('address').value == 0) + this.$('iter').insertRow(); }, onOperationsDone: function() { diff --git a/forms/account/address/style.css b/forms/account/address/style.css index ea43b06e..26d66c87 100644 --- a/forms/account/address/style.css +++ b/forms/account/address/style.css @@ -1,10 +1,8 @@ -.address -{ +.address { padding: 1em; } -.address .box -{ +.address .box { max-width: 30em; padding: 3em; } diff --git a/forms/account/address/ui.xml b/forms/account/address/ui.xml index 4bc07a23..b0aef267 100644 --- a/forms/account/address/ui.xml +++ b/forms/account/address/ui.xml @@ -2,13 +2,13 @@ - + + on-operations-done="this.onOperationsDone()"> SELECT a.id, a.street, a.nickname, a.city, a.postalCode, a.provinceFk, p.countryFk FROM myAddress a @@ -29,11 +29,11 @@ + on-click="this.onReturnClick()"/> + on-click="this.onAcceptClick()"/>
diff --git a/forms/account/conf/style.css b/forms/account/conf/style.css index d5680773..899d1666 100644 --- a/forms/account/conf/style.css +++ b/forms/account/conf/style.css @@ -1,23 +1,18 @@ -.conf -{ +.conf { padding: 1em; } -.conf .box -{ +.conf .box { max-width: 30em; padding: 3em; } -.pass-change -{ +.pass-change { max-width: 15em; padding: 2.5em; } -.pass-info -{ +.pass-info { width: 15em; } -.pass-info ul -{ +.pass-info ul { list-style-type: none; } diff --git a/forms/account/conf/ui.xml b/forms/account/conf/ui.xml index a59f12d5..c21aa80f 100644 --- a/forms/account/conf/ui.xml +++ b/forms/account/conf/ui.xml @@ -27,11 +27,11 @@ + on-click="this.onAddressesClick()"/> + on-click="this.onPassChangeClick()"/>
@@ -98,10 +98,10 @@ placeholder="_Repeat password"/>
- -
diff --git a/forms/admin/access-log/access-log.js b/forms/admin/access-log/access-log.js index 8efbd52a..aa58beda 100644 --- a/forms/admin/access-log/access-log.js +++ b/forms/admin/access-log/access-log.js @@ -1,6 +1,5 @@ -Hedera.AccessLog = new Class -({ +Hedera.AccessLog = new Class({ Extends: Hedera.Form }); diff --git a/forms/admin/access-log/style.css b/forms/admin/access-log/style.css index 9a6b0cfe..604b0438 100644 --- a/forms/admin/access-log/style.css +++ b/forms/admin/access-log/style.css @@ -1,36 +1,29 @@ -.access-log -{ +.access-log { padding: 1em; } -.access-log .box -{ +.access-log .box { max-width: 25em; margin: 0 auto; } -.access-log .form -{ +.access-log .form { padding: 2em; } -.access-log .form > p -{ +.access-log .form > p { font-size: 1.2em; margin: .1em 0; } /* List */ -.access-log .list -{ +.access-log .list { margin-top: 1em; } -.access-log .item -{ +.access-log .item { display: block; padding: 1em; border-bottom: 1px solid #DDD; } -.access-log .item > p -{ +.access-log .item > p { margin: .1em 0; } diff --git a/forms/admin/access-log/ui.xml b/forms/admin/access-log/ui.xml index 33cd2340..9747a1ff 100644 --- a/forms/admin/access-log/ui.xml +++ b/forms/admin/access-log/ui.xml @@ -2,11 +2,13 @@ - + - SELECT Id_Cliente, Cliente, Telefono, movil - FROM vn2008.Clientes WHERE Id_Cliente = #user + SELECT u.id, u.nickname, u.email, c.phone + FROM account.user u + LEFT JOIN vn.client c ON c.id = u.id + WHERE u.id = #user @@ -22,18 +24,10 @@
-

- -

-

- -

-

- -

-

- -

+

#{{$.userForm.id}}

+

{{$.userForm.nickname}}

+

{{$.userForm.email}}

+

{{$.userForm.phone}}

@@ -58,12 +52,12 @@

- + {{Vn.Value.format(iter.stamp, _('%a, %e %b %Y at %T'))}}

- - - - + {{iter.platform}} - + {{iter.browser}} + {{iter.version}}

diff --git a/forms/admin/connections/connections.js b/forms/admin/connections/connections.js index ff058c12..7faab429 100644 --- a/forms/admin/connections/connections.js +++ b/forms/admin/connections/connections.js @@ -4,8 +4,8 @@ Hedera.Connections = new Class({ ,_timeoutId: null - ,onModelStatusChange: function(model) { - if (!model.ready) + ,onModelStatusChange: function() { + if (!this.$('sessions').ready) return; if (this._timeoutId) @@ -22,15 +22,15 @@ Hedera.Connections = new Class({ this.$('sessions').refresh(); } - ,onAccessLogClick: function(button, form) { + ,onAccessLogClick: function(userId) { this.hash.set({ form: 'admin/access-log' - ,user: form.get('userId') + ,user: userId }); } - ,onChangeUserClick: function(button, form) { - this.gui.supplantUser(form.get('user'), + ,onChangeUserClick: function(userName) { + this.gui.supplantUser(userName, this._onUserSupplant.bind(this)); } diff --git a/forms/admin/connections/style.css b/forms/admin/connections/style.css index 1a87896b..4e846da9 100644 --- a/forms/admin/connections/style.css +++ b/forms/admin/connections/style.css @@ -1,14 +1,11 @@ -.connections -{ +.connections { padding: 1em; } -.connections .box -{ +.connections .box { max-width: 25em; margin: 0 auto; } -.action-bar .connections-sum -{ +.action-bar .connections-sum { padding: .4em; background-color: #1e88e5; border-radius: 0.1em; @@ -17,23 +14,19 @@ /* List */ -.connections .item -{ +.connections .item { display: block; padding: 1em; border-bottom: 1px solid #DDD; } -.connections .item > button -{ +.connections .item > button { float: right; margin: 0; } -.connections .item > p -{ +.connections .item > p { margin: .1em 0; } -.connections .item > p.important -{ +.connections .item > p.important { font-size: 1.2em; text-overflow: ellipsis; white-space: nowrap; diff --git a/forms/admin/connections/ui.xml b/forms/admin/connections/ui.xml index 283d8a58..47d9a8b1 100644 --- a/forms/admin/connections/ui.xml +++ b/forms/admin/connections/ui.xml @@ -6,7 +6,7 @@ + on-click="this.onRefreshClick()"/>
- + SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate, a.platform, a.browser, a.version, u.name user @@ -36,28 +39,25 @@
+ on-click="this.onAccessLogClick(iter.userId)"/> + on-click="this.onChangeUserClick(iter.user)"/>

+ {{iter.nickname}}

- - - + {{Vn.Value.format(iter.stamp, '%a, %T')}} - + {{Vn.Value.format(iter.lastUpdate, '%T')}}

- - - - + {{iter.platform}} - + {{iter.browser}} + {{iter.version}}

diff --git a/forms/admin/items/ui.xml b/forms/admin/items/ui.xml index 41fbf780..2130b16d 100644 --- a/forms/admin/items/ui.xml +++ b/forms/admin/items/ui.xml @@ -42,18 +42,16 @@ editable="true" conn="conn"/>

- + {{iter.longName}}

- - - + {{iter.value5}} {{iter.value6}} {{iter.value7}}

- # + {{iter.id}}

- + {{iter.image}}

diff --git a/forms/admin/links/links.js b/forms/admin/links/links.js index f19f14a1..4f761884 100644 --- a/forms/admin/links/links.js +++ b/forms/admin/links/links.js @@ -1,11 +1,5 @@ -Hedera.Links = new Class -({ +Hedera.Links = new Class({ Extends: Hedera.Form - - ,repeaterFunc: function (res, form) - { - res.$('link').href = form.get ('link'); - } }); diff --git a/forms/admin/links/ui.xml b/forms/admin/links/ui.xml index 79540fd9..e82d3135 100644 --- a/forms/admin/links/ui.xml +++ b/forms/admin/links/ui.xml @@ -4,7 +4,7 @@
- + SELECT image, name, description, link FROM link @@ -12,17 +12,18 @@ - +

- + {{iter.name}}

- + {{iter.description}}

diff --git a/forms/admin/queries/queries.js b/forms/admin/queries/queries.js index cc2bb678..5beee6b1 100644 --- a/forms/admin/queries/queries.js +++ b/forms/admin/queries/queries.js @@ -1,102 +1,91 @@ -Hedera.Queries = new Class -({ +Hedera.Queries = new Class({ Extends: Hedera.Form - ,activate: function () - { + ,activate: function() { this.$('result-index').value = 0; } - ,clean: function () - { - if (this._grid) - { - this.$('grid-holder').removeChild (this._grid.node); - this._grid.unref (); + ,clean: function() { + if (this._grid) { + this.$('grid-holder').removeChild(this._grid.node); + this._grid.unref(); this._grid = null; } } - ,_onExecuteClick: function () - { - this.clean (); + ,onExecuteClick: function() { + this.clean(); - var model = new Db.Model ({ + var model = new Db.Model({ conn: this.conn, query: this.$('sql').value, resultIndex: this.$('result-index').value, updatable: this.$('updatable').value }); - model.on ('status-changed', this._onModelChange, this); + model.on('status-changed', this.onModelChange, this); } - ,_onCleanClick: function () - { - this.clean (); + ,onCleanClick: function() { + this.clean(); } - ,_onModelChange: function (model, status) - { - if (status !== Db.Model.Status.LOADING) - { - model.disconnect ('status-changed', this._onModelChange, this); - model.unref (); + ,onModelChange: function(model, status) { + if (status !== Db.Model.Status.LOADING) { + model.disconnect('status-changed', this.onModelChange, this); + model.unref(); } if (status !== Db.Model.Status.READY) return; - Htk.Toast.showMessage (_('Query executed!')); + Htk.Toast.showMessage(_('Query executed!')); var gridHolder = this.$('grid-holder'); if (gridHolder.firstChild) - gridHolder.removeChilds (gridHolder.firstChild); + gridHolder.removeChilds(gridHolder.firstChild); - var grid = new Htk.Grid (); + var grid = new Htk.Grid(); var columns = model.columns; - for (var i = 0; i < columns.length; i++) - { + for (var i = 0; i < columns.length; i++) { var c = columns[i]; - switch (c.type) - { + switch (c.type) { case Db.Conn.Type.BOOLEAN: - var column = new Htk.ColumnCheck (); + var column = new Htk.ColumnCheck(); break; case Db.Conn.Type.INTEGER: - var column = new Htk.ColumnSpin (); + var column = new Htk.ColumnSpin(); break; case Db.Conn.Type.DOUBLE: - var column = new Htk.ColumnSpin ({digits: 2}); + var column = new Htk.ColumnSpin({digits: 2}); break; case Db.Conn.Type.DATE: - var column = new Htk.ColumnDate ({format: '%a, %e %b %Y'}); + var column = new Htk.ColumnDate({format: '%a, %e %b %Y'}); break; case Db.Conn.Type.DATE_TIME: - var column = new Htk.ColumnDate ({format: '%a, %e %b %Y, %T'}); + var column = new Htk.ColumnDate({format: '%a, %e %b %Y, %T'}); break; case Db.Conn.Type.STRING: default: - var column = new Htk.ColumnText (); + var column = new Htk.ColumnText(); } - column.setProperties ({ + column.setProperties({ title: c.name, editable: this.$('updatable').value, columnIndex: i }); - grid.appendColumn (column); + grid.appendColumn(column); } grid.model = model; - gridHolder.appendChild (grid.node); + gridHolder.appendChild(grid.node); this._grid = grid; - } }); diff --git a/forms/admin/queries/style.css b/forms/admin/queries/style.css index e3c925ca..ffd2d78b 100644 --- a/forms/admin/queries/style.css +++ b/forms/admin/queries/style.css @@ -1,25 +1,20 @@ -.queries -{ +.queries { padding: 1em; } -.queries .box -{ +.queries .box { max-width: 40em; margin: 0 auto; } -.queries .form -{ +.queries .form { box-sizing: border-box; padding: 2em; } -.queries textarea -{ +.queries textarea { display: block; width: 100%; height: 8em; } -.queries .result -{ +.queries .result { margin-top: 1em; overflow: auto; } diff --git a/forms/admin/queries/ui.xml b/forms/admin/queries/ui.xml index 6d1b6745..863eccad 100644 --- a/forms/admin/queries/ui.xml +++ b/forms/admin/queries/ui.xml @@ -6,11 +6,11 @@ + on-click="this.onExecuteClick()"/> + on-click="this.onCleanClick()"/>
diff --git a/forms/admin/users/style.css b/forms/admin/users/style.css index 30caf897..d9adf788 100644 --- a/forms/admin/users/style.css +++ b/forms/admin/users/style.css @@ -1,32 +1,25 @@ -.users -{ +.users { padding: 1em; } -.users .box -{ +.users .box { max-width: 30em; margin: 0 auto; } -.users-box -{ +.users-box { padding: 1em; border-bottom: 1px solid #DDD; } -.users-box > button -{ +.users-box > button { float: right; margin: 0; } -.users-box > p -{ +.users-box > p { margin: .2em 0; } -.users-box > p.important -{ +.users-box > p.important { font-size: 1.2em; } -.users-box > .disabled -{ +.users-box > .disabled { float: right; color: white; background-color: #F66; @@ -38,8 +31,7 @@ /* Topbar */ -.action-bar .htk-search-entry -{ +.action-bar .htk-search-entry { margin: .8em .6em; } diff --git a/forms/admin/users/ui.xml b/forms/admin/users/ui.xml index 1ec2f274..ff7b437d 100644 --- a/forms/admin/users/ui.xml +++ b/forms/admin/users/ui.xml @@ -31,18 +31,14 @@
+ tip="_Access log" + on-click="this.onAccessLogClick(iter.id)"/> + tip="_Impersonate user" + on-click="this.onChangeUserClick(iter.name)"/> Disabled diff --git a/forms/admin/users/users.js b/forms/admin/users/users.js index ccca530c..00992713 100644 --- a/forms/admin/users/users.js +++ b/forms/admin/users/users.js @@ -2,10 +2,10 @@ Hedera.Users = new Class({ Extends: Hedera.Form - ,onAccessLogClick: function(button, form) { + ,onAccessLogClick: function(userId) { this.hash.set({ - 'form': 'admin/access-log' - ,'user': form.get('id') + form: 'admin/access-log' + ,user: userId }); } @@ -17,8 +17,8 @@ Hedera.Users = new Class({ 'block' : 'none'; } - ,onChangeUserClick: function(button, form) { - this.gui.supplantUser(form.get('name'), + ,onChangeUserClick: function(userName) { + this.gui.supplantUser(userName, this.onUserSupplant.bind(this)); } diff --git a/forms/admin/visits/ui.xml b/forms/admin/visits/ui.xml index 15fd4568..d27bf247 100644 --- a/forms/admin/visits/ui.xml +++ b/forms/admin/visits/ui.xml @@ -6,11 +6,11 @@ + on-click="this.onRefreshClick()"/> + on-click="this.onSessionsClick()"/>
diff --git a/forms/admin/visits/visits.js b/forms/admin/visits/visits.js index fd4c40b0..7c1a7674 100644 --- a/forms/admin/visits/visits.js +++ b/forms/admin/visits/visits.js @@ -1,22 +1,18 @@ -Hedera.Visits = new Class -({ +Hedera.Visits = new Class({ Extends: Hedera.Form - ,activate: function () - { - this.$('from').value = new Date (); - this.$('to').value = new Date (); + ,activate: function() { + this.$('from').value = new Date(); + this.$('to').value = new Date(); } - ,onRefreshClick: function () - { - this.$('visits').refresh (); + ,onRefreshClick: function() { + this.$('visits').refresh(); } - ,onSessionsClick: function () - { - this.hash.set ({'form': 'admin/connections'}); + ,onSessionsClick: function() { + this.hash.set({form: 'admin/connections'}); } }); diff --git a/forms/cms/home/home.js b/forms/cms/home/home.js index 85750bdc..e29f8765 100644 --- a/forms/cms/home/home.js +++ b/forms/cms/home/home.js @@ -1,9 +1,5 @@ Hedera.Home = new Class({ Extends: Hedera.Form - - ,onStartOrderClick: function() { - this.hash.set({form: 'ecomerce/catalog'}); - } }); diff --git a/forms/cms/home/ui.xml b/forms/cms/home/ui.xml index 2ec3e7c2..0fc7e62b 100644 --- a/forms/cms/home/ui.xml +++ b/forms/cms/home/ui.xml @@ -7,7 +7,7 @@ class="start-order" icon="add_shopping_cart" tip="_Start order" - on-click="onStartOrderClick"/> + on-click="this.hash.set({form: 'ecomerce/catalog'})"/>
@@ -25,8 +25,7 @@
diff --git a/forms/news/new/new.js b/forms/news/new/new.js index 99a3466b..02c79053 100644 --- a/forms/news/new/new.js +++ b/forms/news/new/new.js @@ -54,9 +54,9 @@ Hedera.New = new Class({ this.editor.setContent(newHtml); }, - onStatusChange: function(form) { + onStatusChange: function() { if (this.$('new-id').value == 0) - form.insertRow(); + this.$('iter').insertRow(); }, onOperationsDone: function() { diff --git a/forms/news/new/style.css b/forms/news/new/style.css index 85608368..92b661a9 100644 --- a/forms/news/new/style.css +++ b/forms/news/new/style.css @@ -1,23 +1,18 @@ -.new -{ +.new { padding: 1em; } -.new .box -{ +.new .box { max-width: 38em; padding: 2em; } /* Form */ -.new textarea -{ - min-height: 20em; +.new textarea { + min-height: 500px; } - -.new .foot -{ +.new .foot { text-align: center; margin-top: 1em; } diff --git a/forms/news/new/ui.xml b/forms/news/new/ui.xml index d3d63835..3b1fe869 100644 --- a/forms/news/new/ui.xml +++ b/forms/news/new/ui.xml @@ -2,12 +2,12 @@ - + + on-operations-done="this.onOperationsDone()"> SELECT id, title, text, tag, priority FROM news WHERE id = #new @@ -19,7 +19,7 @@ - +

AddEditNew

@@ -28,11 +28,11 @@ + on-click="this.onReturnClick()"/> + on-click="this.onAcceptClick()"/>
diff --git a/forms/news/news/news.js b/forms/news/news/news.js index 9da4dc08..54c5d1da 100644 --- a/forms/news/news/news.js +++ b/forms/news/news/news.js @@ -1,30 +1,25 @@ -Hedera.News = new Class -({ +Hedera.News = new Class({ Extends: Hedera.Form - ,editNew: function (newId) - { - this.hash.set ({ - 'form': 'news/new' - ,'new': newId + ,editNew: function(newId) { + this.hash.set({ + form: 'news/new', + new: newId }); } - ,onEditClick: function (button, form) - { - this.editNew (button.value); + ,onEditClick: function(newId) { + this.editNew(newId); } - ,onDeleteClick: function (button, form) - { - if (confirm (_('ReallyDelete'))) - form.deleteRow (); + ,onDeleteClick: function(form) { + if (confirm(_('ReallyDelete'))) + form.deleteRow(); } - ,onAddClick: function () - { - this.editNew (0); + ,onAddClick: function() { + this.editNew(0); } }); diff --git a/forms/news/news/ui.xml b/forms/news/news/ui.xml index 9e8f7109..b2302a67 100644 --- a/forms/news/news/ui.xml +++ b/forms/news/news/ui.xml @@ -6,7 +6,7 @@ + on-click="this.onAddClick()"/>
@@ -22,17 +22,13 @@
+ tip="_EditNew" + on-click="this.onEditClick(iter.id)"/> + on-click="this.onDeleteClick($.iter)"/> + on-click="this.onPreviewClick()"/>
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); +}