diff --git a/forms/account/address-list/address-list.js b/forms/account/address-list/address-list.js index 5a73cee4..a75df3b8 100644 --- a/forms/account/address-list/address-list.js +++ b/forms/account/address-list/address-list.js @@ -9,7 +9,7 @@ Hedera.AddressList = new Class } ,onAddAddressClick: function() { - this.hash.set({ + this.hash.setAll({ form: 'account/address', address: 0 }); @@ -33,7 +33,7 @@ Hedera.AddressList = new Class } ,onEditAddressClick: function(id) { - this.hash.set({ + this.hash.setAll({ form: 'account/address', address: id }); diff --git a/forms/account/address/address.js b/forms/account/address/address.js index 2e77b3f0..649deaee 100644 --- a/forms/account/address/address.js +++ b/forms/account/address/address.js @@ -9,20 +9,12 @@ Hedera.Address = new Class({ }, onStatusChange: function() { - if (this.$.iter.ready && this.$.address.value == 0) + if (this.$.iter.ready && this.hash.$.address == 0) this.$.iter.insertRow(); }, onOperationsDone: function() { Htk.Toast.showMessage(_('AddressChangedSuccessfully')); - this.onReturnClick(); - }, - - onAcceptClick: function() { - this.$.iter.performOperations(); - }, - - onReturnClick: function() { - window.history.back(); + window.history.back() } }); diff --git a/forms/account/address/ui.xml b/forms/account/address/ui.xml index 6a1b5aff..9b9eab0f 100644 --- a/forms/account/address/ui.xml +++ b/forms/account/address/ui.xml @@ -1,24 +1,21 @@ - - + + + SELECT a.id, a.street, a.nickname, a.city, a.postalCode, a.provinceFk, p.countryFk FROM myAddress a LEFT JOIN vn.province p ON p.id = a.provinceFk WHERE a.id = #address - - - - - @@ -29,11 +26,11 @@ + on-click="window.history.back()"/> + on-click="$.iter.performOperations()"/>
@@ -63,11 +60,11 @@
- SELECT id, country FROM vn.country @@ -80,15 +77,10 @@ placeholder="_Province" column="provinceFk" form="iter"> - + SELECT id, name FROM vn.province - WHERE countryFk = #country + WHERE countryFk = #countryFk ORDER BY name - - - - -
diff --git a/forms/account/conf/conf.js b/forms/account/conf/conf.js index 143e4f50..e15c2ca7 100644 --- a/forms/account/conf/conf.js +++ b/forms/account/conf/conf.js @@ -9,7 +9,7 @@ Hedera.Conf = new Class({ if (this.hash.$.verificationToken) this.onPassChangeClick(); } - + ,onPassChangeClick: function() { this.$.oldPassword.value = ''; this.$.newPassword.value = ''; @@ -75,9 +75,5 @@ Hedera.Conf = new Class({ ,onPassInfoClick: function() { this.$.passwordInfo.show(); } - - ,onAddressesClick: function() { - this.hash.set({form: 'account/address-list'}); - } }); diff --git a/forms/account/conf/ui.xml b/forms/account/conf/ui.xml index d8f3119a..28136d09 100644 --- a/forms/account/conf/ui.xml +++ b/forms/account/conf/ui.xml @@ -27,7 +27,7 @@ + on-click="$.hash.setAll({form: 'account/address-list'})"/> - - - - - - SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role - FROM account.user u - JOIN account.role r ON r.id = u.role - LEFT JOIN vn.client c ON c.id = u.id - WHERE u.id = #user - - - - - - + + + SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role + FROM account.user u + JOIN account.role r ON r.id = u.role + LEFT JOIN vn.client c ON c.id = u.id + WHERE u.id = #user @@ -25,29 +16,22 @@
-

-

# -

-

-

-

+

+

# -

+

+

+

- - - SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies - FROM visitUser u - JOIN visitAccess c ON c.id = u.accessFk - JOIN visitAgent a ON a.id = c.agentFk - WHERE u.userFk = #user - ORDER BY u.stamp DESC - LIMIT 8 - - - - - - + + SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies + FROM visitUser u + JOIN visitAccess c ON c.id = u.accessFk + JOIN visitAgent a ON a.id = c.agentFk + WHERE u.userFk = #user + ORDER BY u.stamp DESC + LIMIT 8
diff --git a/forms/admin/connections/connections.js b/forms/admin/connections/connections.js index 2bacd32c..ef09f125 100644 --- a/forms/admin/connections/connections.js +++ b/forms/admin/connections/connections.js @@ -25,7 +25,7 @@ Hedera.Connections = new Class({ } ,_onUserSupplant: function() { - this.hash.set({form: 'ecomerce/orders'}); + this.hash.setAll({form: 'ecomerce/orders'}); } ,sessionsFunc: function() { diff --git a/forms/admin/connections/ui.xml b/forms/admin/connections/ui.xml index 6482b5c4..65b25336 100644 --- a/forms/admin/connections/ui.xml +++ b/forms/admin/connections/ui.xml @@ -23,17 +23,15 @@ property="model" id="sessions" on-status-changed="this.onModelStatusChange()"> - - SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate, - a.platform, a.browser, a.version, u.name user - FROM userSession s - JOIN visitUser vu ON vu.id = s.userVisitFk - JOIN visitAccess ac ON ac.id = vu.accessFk - JOIN visitAgent a ON a.id = ac.agentFk - JOIN visit v ON v.id = a.visitFk - JOIN account.user u ON u.id = vu.userFk - ORDER BY lastUpdate DESC - + SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate, + a.platform, a.browser, a.version, u.name user + FROM userSession s + JOIN visitUser vu ON vu.id = s.userVisitFk + JOIN visitAccess ac ON ac.id = vu.accessFk + JOIN visitAgent a ON a.id = ac.agentFk + JOIN visit v ON v.id = a.visitFk + JOIN account.user u ON u.id = vu.userFk + ORDER BY lastUpdate DESC - - - -

Items

- +
- + SELECT i.id, i.longName, i.size, i.category, i.value5, i.value6, i.value7, i.image, im.updated @@ -22,14 +18,9 @@ LEFT JOIN image im ON im.collectionFk = 'catalog' AND im.name = i.image - WHERE i.longName LIKE CONCAT('%', #filter, '%') - OR i.id = #filter + WHERE i.longName LIKE CONCAT('%', #search, '%') + OR i.id = #search ORDER BY i.longName LIMIT 50 - - - - -
diff --git a/forms/admin/users/ui.xml b/forms/admin/users/ui.xml index 722187fb..922e88ff 100644 --- a/forms/admin/users/ui.xml +++ b/forms/admin/users/ui.xml @@ -1,34 +1,22 @@ - - - -

User management

- +
- - - +
- - - +
@@ -50,27 +54,19 @@ class="box htk-list" form-id="iter" empty-message="_Select date interval"> - - - SELECT browser, - MIN(CAST(version AS DECIMAL(4,1))) minVersion, - MAX(CAST(version AS DECIMAL(4,1))) maxVersion, - MAX(c.stamp) lastVisit, - COUNT(DISTINCT c.id) visits, - SUM(a.firstAccessFk = c.id AND v.firstAgentFk = a.id) newVisits - FROM visitUser e - JOIN visitAccess c ON c.id = e.accessFk - JOIN visitAgent a ON a.id = c.agentFk - JOIN visit v ON v.id = a.visitFk - WHERE c.stamp BETWEEN TIMESTAMP(#from,'00:00:00') AND TIMESTAMP(#to,'23:59:59') - GROUP BY browser ORDER BY visits DESC - - - - - - - + + SELECT browser, + MIN(CAST(version AS DECIMAL(4,1))) minVersion, + MAX(CAST(version AS DECIMAL(4,1))) maxVersion, + MAX(c.stamp) lastVisit, + COUNT(DISTINCT c.id) visits, + SUM(a.firstAccessFk = c.id AND v.firstAgentFk = a.id) newVisits + FROM visitUser e + JOIN visitAccess c ON c.id = e.accessFk + JOIN visitAgent a ON a.id = c.agentFk + JOIN visit v ON v.id = a.visitFk + WHERE c.stamp BETWEEN TIMESTAMP(#from,'00:00:00') AND TIMESTAMP(#to,'23:59:59') + GROUP BY browser ORDER BY visits DESC
diff --git a/forms/admin/visits/visits.js b/forms/admin/visits/visits.js index ffd35638..24d54cdb 100644 --- a/forms/admin/visits/visits.js +++ b/forms/admin/visits/visits.js @@ -3,16 +3,11 @@ Hedera.Visits = new Class({ Extends: Hedera.Form ,activate: function() { - this.$.from.value = new Date(); - this.$.to.value = new Date(); - } - - ,onRefreshClick: function() { - this.$.visits.refresh(); - } - - ,onSessionsClick: function() { - this.hash.set({form: 'admin/connections'}); + if (!this.hash.$.to) + this.hash.assign({ + from: new Date(), + to: new Date() + }); } }); diff --git a/forms/agencies/packages/packages.js b/forms/agencies/packages/packages.js index 14c16e14..d86d89c6 100644 --- a/forms/agencies/packages/packages.js +++ b/forms/agencies/packages/packages.js @@ -3,7 +3,7 @@ Hedera.Packages = new Class({ Extends: Hedera.Form ,onShowClick: function(column, agencyId) { - this.hash.set({ + this.hash.setAll({ form: 'agencies/provinces', agency: agencyId }); diff --git a/forms/agencies/provinces/provinces.js b/forms/agencies/provinces/provinces.js index 723fc15c..b441f598 100644 --- a/forms/agencies/provinces/provinces.js +++ b/forms/agencies/provinces/provinces.js @@ -1,6 +1,5 @@ -Hedera.Provinces = new Class -({ +Hedera.Provinces = new Class({ Extends: Hedera.Form }); diff --git a/forms/agencies/provinces/ui.xml b/forms/agencies/provinces/ui.xml index d9a3bcbf..46fd7c04 100644 --- a/forms/agencies/provinces/ui.xml +++ b/forms/agencies/provinces/ui.xml @@ -1,23 +1,12 @@ - - - -

ByProvince

- - - CALL vn2008.desglose_volume(#agency) - - - - - - + + CALL vn2008.desglose_volume(#agency) diff --git a/forms/cms/home/ui.xml b/forms/cms/home/ui.xml index 417b8131..77424b3c 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="this.hash.set({form: 'ecomerce/catalog'})"/> + on-click="$.hash.setAll({form: 'ecomerce/catalog'})"/>
diff --git a/forms/ecomerce/basket/basket.js b/forms/ecomerce/basket/basket.js index 9dd2b623..e967ef52 100644 --- a/forms/ecomerce/basket/basket.js +++ b/forms/ecomerce/basket/basket.js @@ -6,7 +6,7 @@ Hedera.Basket = new Class({ this.close(); this.isOpen = true; - Hedera.BasketChecker.check(this.conn, + Hedera.BasketChecker.check(this.conn, this.hash, this.onBasketCheck.bind(this)); } @@ -21,7 +21,7 @@ Hedera.Basket = new Class({ ,onConfigureClick: function() { Htk.Toast.showWarning(_('RememberReconfiguringImpact')); - this.hash.set({form: 'ecomerce/checkout'}); + this.hash.setAll({form: 'ecomerce/checkout'}); } ,onDeleteClick: function(form) { diff --git a/forms/ecomerce/basket/ui.xml b/forms/ecomerce/basket/ui.xml index be46122f..6d05ae55 100644 --- a/forms/ecomerce/basket/ui.xml +++ b/forms/ecomerce/basket/ui.xml @@ -10,11 +10,11 @@ + on-click="this.hash.setAll({form: 'ecomerce/catalog'})"/> + on-click="this.hash.setAll({form: 'ecomerce/confirm'})"/>
diff --git a/forms/ecomerce/catalog/catalog.js b/forms/ecomerce/catalog/catalog.js index 3c7b052b..5b5f91a1 100644 --- a/forms/ecomerce/catalog/catalog.js +++ b/forms/ecomerce/catalog/catalog.js @@ -8,8 +8,8 @@ Hedera.Catalog = new Class({ this.close(); this.isOpen = true; - if (!localStorage.getItem('hederaGuest')) { - Hedera.BasketChecker.check(this.conn, + if (!localStorage.getItem('hederaGuest')) { + Hedera.BasketChecker.check(this.conn, this.hash, this.onBasketCheck.bind(this)); } else { var query = 'CALL mybasket_configureForGuest'; @@ -42,36 +42,24 @@ Hedera.Catalog = new Class({ ,onFilterChange: function() { const $ = this.$; - const params = [ - 'search', - 'realm', - 'type', - 'color', - 'origin', - 'category', - 'producer' - ]; - const lot = {}; - for (const param of params) - lot[param] = this.$[param].value; + const hash = this.hash; - if (lot.search != null || lot.type != null) { + if (hash.search != null || hash.type != null) { const filter = new Sql.Operation({ type: Sql.Operation.Type.AND }); - const exprs = filter.exprs; let idSearch = false; - if (lot.search != null) { - idSearch = /^\d+$/.test(lot.search); - exprs.add(idSearch ? $.idOp : $.nameOp); + if (hash.search != null) { + idSearch = /^\d+$/.test(hash.search); + filter.push(idSearch ? $.idOp : $.nameOp); } if (!idSearch) { - if (lot.realm != null) - exprs.add($.realmOp); - if (lot.type != null) - exprs.add($.typeOp); + if (hash.realm != null) + filter.push($.realmOp); + if (hash.type != null) + filter.push($.typeOp); const tags = [ 'color', @@ -80,28 +68,21 @@ Hedera.Catalog = new Class({ 'producer' ]; for (const tag of tags) - if (lot[tag] != null) - exprs.add($[`${tag}Op`]); + if (hash[tag] != null) + filter.push($[`${tag}Op`]); } - const batch = new Sql.Batch(); - batch.addObject('filter', filter); - batch.block(); - $.items.batch = batch; + const lot = new Vn.Lot(); + lot.set('filter', filter); + $.items.lot = lot; } else - $.items.batch = null; + $.items.lot = null; } ,onRealmChange: function() { const hasRealm = this.$.realm.value != null; this.$.filters.style.display = hasRealm ? 'block' : 'none'; this.$.realmMsg.style.display = hasRealm ? 'none' : 'block'; - this.onFilterChange(); - this.refreshTitle(); - } - - ,onTypeChange: function() { - this.onFilterChange(); this.refreshTitle(); } @@ -234,14 +215,14 @@ Hedera.Catalog = new Class({ if (this.isGuest()) return; - this.hash.set({form: 'ecomerce/basket'}); + this.hash.setAll({form: 'ecomerce/basket'}); } ,onConfigureClick: function() { if (this.isGuest()) return; - this.hash.set({form: 'ecomerce/checkout'}); + this.hash.setAll({form: 'ecomerce/checkout'}); } ,onAddItemClick: function(event, form) { @@ -280,7 +261,6 @@ Hedera.Catalog = new Class({ ,onConfirmClick: function() { var sql = ''; - var batch = new Sql.Batch(); var query = new Sql.String({query: 'CALL myBasket_addItem(#warehouse, #item, #amount);'}); var amountSum = 0; @@ -288,10 +268,12 @@ Hedera.Catalog = new Class({ var amount = this.items[warehouse]; amountSum += amount; - batch.addValue('warehouse', warehouse); - batch.addValue('item', this.$.cardItem.value); - batch.addValue('amount', amount); - sql += query.render(batch); + const params = { + warehouse: warehouse, + item: this.$.cardItem.value, + amount: amount + } + sql += query.render(params); } if (amountSum > 0) { @@ -336,7 +318,7 @@ Vn.Filter = new Class({ model: { type: Db.Model ,set: function(x) { - x.batch = this._batch; + x.lot = this._lot; this._model = x; this._select.model = x; } @@ -358,7 +340,7 @@ Vn.Filter = new Class({ type: Sql.Filter ,set: function(x) { this._filter = x; - this._batch.addObject('filter', x); + this._lot.set('filter', x); } ,get: function() { return this._filter; @@ -382,7 +364,7 @@ Vn.Filter = new Class({ this._ul = this.createElement('ul'); this.node.appendChild(this._ul); - this._batch = new Sql.Batch(); + this._lot = new Vn.Lot(); this.parent(props); } @@ -418,9 +400,9 @@ Vn.Filter = new Class({ } ,_changeValue: function(newValue) { - this._batch.block(); + this._lot.block(); this.value = newValue; - this._batch.unblock(); + this._lot.unblock(); } ,_onTimeout: function() { diff --git a/forms/ecomerce/catalog/style.css b/forms/ecomerce/catalog/style.css index 1ec8cdef..0714e225 100644 --- a/forms/ecomerce/catalog/style.css +++ b/forms/ecomerce/catalog/style.css @@ -157,7 +157,7 @@ font-size: 1em; text-overflow: ellipsis; overflow: hidden; - max-height: 2.8em; + max-height: 3em; } .item-info > p { margin: 0; @@ -247,7 +247,7 @@ flex-direction: column; width: 240px; - height: 405px; + height: 410px; overflow: hidden; } .grid-view .item-box:hover { diff --git a/forms/ecomerce/catalog/ui.xml b/forms/ecomerce/catalog/ui.xml index cacab56f..8826ba01 100644 --- a/forms/ecomerce/catalog/ui.xml +++ b/forms/ecomerce/catalog/ui.xml @@ -22,18 +22,11 @@
- - - - - - - - - - - - + + + + + - - - - - +
@@ -383,7 +372,7 @@ SELECT i.description, o.name origin FROM vn.item i @@ -421,7 +410,7 @@ SELECT l.name, it.value FROM vn.itemTag it @@ -445,7 +434,7 @@ property="model" result-index="1" on-status-changed-after="onCardLoad" - batch="card-batch"> + lot="card-lot"> CALL myBasket_calcCatalogFromItem(#item); SELECT l.warehouseFk, w.name warehouse, p.`grouping`, p.price, p.priceKg, p.rate, l.available diff --git a/forms/ecomerce/checkout/checkout.js b/forms/ecomerce/checkout/checkout.js index 0343ccb0..74ffae95 100644 --- a/forms/ecomerce/checkout/checkout.js +++ b/forms/ecomerce/checkout/checkout.js @@ -40,12 +40,13 @@ Hedera.Checkout = new Class({ date.setDate(date.getDate() + addDays); } - - this.$.date.value = date; - this.$.method.value = row.deliveryMethod; - this.$.agency.value = row.agencyModeFk; - this.$.address.value = row.addressFk; + this.$.lot.assign({ + date: date, + method: row.deliveryMethod, + agency: row.agencyModeFk, + address: row.addressFk + }); this.autoStepLocked = false; }, @@ -57,15 +58,8 @@ Hedera.Checkout = new Class({ this.disableButtons(true); var query = 'CALL myBasket_configure(#date, #method, #agency, #address)'; - - var batch = new Sql.Batch(); - batch.addParam('method', this.$.method); - batch.addParam('date', this.$.date); - batch.addParam('agency', this.$.agency); - batch.addParam('address', this.$.address); - this.conn.execQuery(query, - this.onBasketConfigured.bind(this), batch); + this.onBasketConfigured.bind(this), this.$.lot.$); }, onBasketConfigured: function(resultSet) { @@ -79,14 +73,14 @@ Hedera.Checkout = new Class({ else Htk.Toast.showMessage(_('OrderStarted')); - this.hash.set({form: 'ecomerce/catalog'}); + this.hash.setAll({form: 'ecomerce/catalog'}); }, onCancelClick: function() { if (this.$.orderForm.numRows > 0) window.history.back(); else - this.hash.set({form: 'ecomerce/orders'}); + this.hash.setAll({form: 'ecomerce/orders'}); }, agencySteps: ['method', 'date', 'address', 'agency', 'confirm-delivery'], @@ -163,7 +157,7 @@ Hedera.Checkout = new Class({ }, onAddressClick: function(addressId) { - this.$.address.value = addressId; + this.$.lot.set('address', addressId); this.goNextStep(); }, @@ -171,7 +165,7 @@ Hedera.Checkout = new Class({ if (this.selectedNode) Vn.Node.removeClass(this.selectedNode, 'selected'); - var row = this.$.addresses.search('id', this.$.address.value); + var row = this.$.addresses.search('id', this.$.lot.$.address); if (row != -1) { var builder = this.$.repeater.getBuilder(row); diff --git a/forms/ecomerce/checkout/ui.xml b/forms/ecomerce/checkout/ui.xml index ec6db8da..434cc3b7 100644 --- a/forms/ecomerce/checkout/ui.xml +++ b/forms/ecomerce/checkout/ui.xml @@ -1,9 +1,6 @@ - - - - + SELECT deliveryMethod, agencyModeFk, addressFk, defaultAgencyFk @@ -19,6 +16,7 @@ CALL vn.zone_getAgency(#address, #date); @@ -30,15 +28,10 @@ AND a.isVisible ORDER BY a.description; DROP TEMPORARY TABLE tmp.zoneGetAgency; - - - - - - CALL vn.zone_getAgency(#address, #date); @@ -50,12 +43,6 @@ AND a.isVisible ORDER BY a.description; DROP TEMPORARY TABLE tmp.zoneGetAgency; - - - - - -
diff --git a/forms/ecomerce/confirm/confirm.js b/forms/ecomerce/confirm/confirm.js index 3218a47a..36ec2fee 100644 --- a/forms/ecomerce/confirm/confirm.js +++ b/forms/ecomerce/confirm/confirm.js @@ -6,7 +6,7 @@ Hedera.Confirm = new Class({ this.close(); this.isOpen = true; - Hedera.BasketChecker.check(this.conn, + Hedera.BasketChecker.check(this.conn, this.hash, this.onBasketCheck.bind(this)); }, @@ -139,10 +139,13 @@ Hedera.Confirm = new Class({ else var payAmount = this.$.totalAmount.value; - var tpv = new Hedera.Tpv({conn: this.conn}); + var tpv = new Hedera.Tpv({ + conn: this.conn, + hash: this.hash + }); tpv.pay(payAmount, this.$.orderForm.$.companyFk); } else - this.hash.set({'form': 'ecomerce/orders'}); + this.hash.setAll({'form': 'ecomerce/orders'}); } }); diff --git a/forms/ecomerce/orders/orders.js b/forms/ecomerce/orders/orders.js index 81dd4562..47346e9d 100644 --- a/forms/ecomerce/orders/orders.js +++ b/forms/ecomerce/orders/orders.js @@ -3,7 +3,10 @@ Hedera.Orders = new Class({ Extends: Hedera.Form, activate: function() { - this.tpv = new Hedera.Tpv({conn: this.conn}); + this.tpv = new Hedera.Tpv({ + conn: this.conn, + hash: this.hash + }); this.tpv.check(this._onTpvCheck.bind(this)); }, @@ -13,7 +16,7 @@ Hedera.Orders = new Class({ }, onBasketClick: function() { - this.hash.set({form: 'ecomerce/basket'}); + this.hash.setAll({form: 'ecomerce/basket'}); }, repeaterFunc: function(res, form) { diff --git a/forms/ecomerce/ticket/ticket.js b/forms/ecomerce/ticket/ticket.js index 686d541e..c845fa4b 100644 --- a/forms/ecomerce/ticket/ticket.js +++ b/forms/ecomerce/ticket/ticket.js @@ -6,9 +6,8 @@ Hedera.Ticket = new Class({ if (!ticket.value) return; - var batch = new Sql.Batch(); - batch.addValue('ticket', ticket.value); - this.conn.execQuery('CALL myTicket_logAccess(#ticket)', null, batch); + var params = {ticket: ticket.value}; + this.conn.execQuery('CALL myTicket_logAccess(#ticket)', null, params); }, onTicketReady: function(form) { diff --git a/forms/ecomerce/ticket/ui.xml b/forms/ecomerce/ticket/ui.xml index aaf31dc5..2a3c4687 100644 --- a/forms/ecomerce/ticket/ui.xml +++ b/forms/ecomerce/ticket/ui.xml @@ -1,18 +1,10 @@ - - - - - - - - - - - CALL myTicket_get(#ticket) - - - + + + + + CALL myTicket_get(#ticket) +

OrderDetail

@@ -20,7 +12,7 @@ + on-click="this.onPrintClick()"/>
@@ -67,7 +59,7 @@ + lot="params"> CALL myTicket_getRows(#ticket) @@ -108,7 +100,7 @@ + lot="params"> CALL myTicket_getServices(#ticket) @@ -133,7 +125,7 @@ + lot="params"> CALL myTicket_getPackages(#ticket) diff --git a/forms/news/new/new.js b/forms/news/new/new.js index 6dfa57c3..d3b68aec 100644 --- a/forms/news/new/new.js +++ b/forms/news/new/new.js @@ -55,7 +55,7 @@ Hedera.New = new Class({ }, onStatusChange: function() { - if (this.$.newId.value == 0) + if (this.hash.$.new == 0) this.$.iter.insertRow(); }, @@ -71,10 +71,6 @@ Hedera.New = new Class({ onAcceptClick: function() { this.$.iter.set('text', this.editor.getContent()); this.$.iter.performOperations(); - }, - - onReturnClick: function() { - this.hash.set({form: 'news/news'}); } }); }); diff --git a/forms/news/new/ui.xml b/forms/news/new/ui.xml index 982aed46..b9b54964 100644 --- a/forms/news/new/ui.xml +++ b/forms/news/new/ui.xml @@ -1,22 +1,14 @@ - - - - SELECT id, title, text, tag, priority, image - FROM news WHERE id = #new - - - - - - + SELECT id, title, text, tag, priority, image + FROM news WHERE id = #new @@ -28,7 +20,7 @@ + on-click="this.hash.setAll({form: 'news/news'});"/> Tag - - SELECT name, description FROM newsTag - ORDER BY description - + SELECT name, description FROM newsTag + ORDER BY description
diff --git a/forms/news/news/news.js b/forms/news/news/news.js index 54c5d1da..39d4f041 100644 --- a/forms/news/news/news.js +++ b/forms/news/news/news.js @@ -3,7 +3,7 @@ Hedera.News = new Class({ Extends: Hedera.Form ,editNew: function(newId) { - this.hash.set({ + this.hash.setAll({ form: 'news/new', new: newId }); diff --git a/forms/reports/items-form/items-form.js b/forms/reports/items-form/items-form.js index c2f4d85d..defdf2e3 100644 --- a/forms/reports/items-form/items-form.js +++ b/forms/reports/items-form/items-form.js @@ -3,18 +3,15 @@ Hedera.ItemsForm = new Class({ Extends: Hedera.Form ,activate: function() { - this.$.warehouse.value = 7; - this.$.realm.value = null; + this.$.lot.assign({ + warehouse: 7, + realm: null + }); } ,onPreviewClick: function() { - var batch = new Sql.Batch(); - batch.addValues({ - warehouse: this.$.warehouse.value - ,realm: this.$.realm.value - ,rate: this.$.rate.value - }); - this.gui.openReport('items-report', batch); + this.$.lot.set('rate', this.$.rate.value); + this.gui.openReport('items-report', this.$.lot); } }); diff --git a/forms/reports/items-form/ui.xml b/forms/reports/items-form/ui.xml index b735cf7a..8b6462db 100644 --- a/forms/reports/items-form/ui.xml +++ b/forms/reports/items-form/ui.xml @@ -1,4 +1,5 @@ +

Item list

@@ -12,25 +13,19 @@
- - + - - SELECT id, name FROM vn2008.warehouse - WHERE reserve ORDER BY name - + SELECT id, name FROM vn2008.warehouse + WHERE reserve ORDER BY name
- - + - - SELECT id, reino FROM vn2008.reinos - WHERE display != FALSE ORDER BY reino - + SELECT id, reino FROM vn2008.reinos + WHERE display != FALSE ORDER BY reino
diff --git a/forms/reports/shelves/shelves.js b/forms/reports/shelves/shelves.js index 7387b7c1..86cbe00b 100644 --- a/forms/reports/shelves/shelves.js +++ b/forms/reports/shelves/shelves.js @@ -3,49 +3,18 @@ Hedera.Shelves = new Class({ Extends: Hedera.Form ,activate: function() { - this.$.date.value = new Date(); - this.$.useIds.value = false; + this.$.lot.assign({ + date: new Date(), + useIds: false + }); } ,onConfigChange: function() { - const fields = [ - 'realm' - ,'family' - ,'warehouse' - ,'shelf' - ,'namePrefix' - ,'maxAmount' - ,'reportTitle' - ,'showPacking' - ,'stack' - ]; - const config = this.$.config.$; - - if (config) - for (const field of fields) - this.$[field].value = config[field]; + this.$.lot.assignLot(this.$.config); } ,onPreviewClick: function() { - var fields = [ - 'family' - ,'warehouse' - ,'shelf' - ,'namePrefix' - ,'maxAmount' - ,'reportTitle' - ,'showPacking' - ,'stack' - ,'useIds' - ,'date' - ]; - - var batch = new Sql.Batch(); - - for (const field of fields) - batch.addValue(field, this.$[field].value); - - this.gui.openReport('shelves-report', batch); + this.gui.openReport('shelves-report', this.$.lot); } }); diff --git a/forms/reports/shelves/ui.xml b/forms/reports/shelves/ui.xml index 8bb26e4f..376a1bff 100644 --- a/forms/reports/shelves/ui.xml +++ b/forms/reports/shelves/ui.xml @@ -1,14 +1,8 @@ - - - - SELECT c.id, c.name reportTitle, c.namePrefix, c.warehouse, c.family, - c.shelf, c.maxAmount, c.showPacking, c.stack, t.reino_id realm - FROM shelfConfig c - JOIN vn2008.Tipos t ON t.tipo_id = c.family - - - + + + +

Shelves

@@ -25,89 +19,84 @@ + on-ready="this.onConfigChange()"> + + SELECT c.id, c.name reportTitle, c.namePrefix, c.warehouse, c.family, + c.shelf, c.maxAmount, c.showPacking, c.stack, t.reino_id realm + FROM shelfConfig c + JOIN vn2008.Tipos t ON t.tipo_id = c.family + +
- +
- - - - SELECT id, reino FROM vn2008.reinos - WHERE display != FALSE ORDER BY reino - + + + SELECT id, reino FROM vn2008.reinos + WHERE display != FALSE ORDER BY reino
- - - - SELECT tipo_id, Tipo FROM vn2008.Tipos - WHERE reino_id = #realm ORDER BY Tipo - - - - - - + + + SELECT tipo_id, Tipo FROM vn2008.Tipos + WHERE reino_id = #realm ORDER BY Tipo
- - - - SELECT id, name FROM vn2008.warehouse - WHERE reserve ORDER BY name - + + + SELECT id, name FROM vn2008.warehouse + WHERE reserve ORDER BY name
- - - - SELECT id, name FROM shelf - + + + SELECT id, name FROM shelf
- +
- +
- +
diff --git a/js/db/connection.js b/js/db/connection.js index 06e40210..90b16d67 100644 --- a/js/db/connection.js +++ b/js/db/connection.js @@ -12,15 +12,13 @@ var Connection = new Class(); module.exports = Connection; -var Flag = -{ +var Flag = { NOT_NULL : 1 ,PRI_KEY : 2 ,AI : 512 | 2 | 1 }; -var Type = -{ +var Type = { BOOLEAN : 1 ,INTEGER : 3 ,DOUBLE : 4 @@ -53,10 +51,10 @@ Connection.implement({ * * @param {Sql.Stmt} stmt The statement * @param {Function} callback The function to call when operation is done - * @param {Sql.Batch} batch The batch used to set the parameters + * @param {Object} params The query params */ - ,execStmt: function(stmt, callback, batch) { - this.execSql(stmt.render(batch), callback); + ,execStmt: function(stmt, callback, params) { + this.execSql(stmt.render(params), callback); } /** @@ -64,10 +62,10 @@ Connection.implement({ * * @param {String} query The SQL statement * @param {Function} callback The function to call when operation is done - * @param {Sql.Batch} batch The batch used to set the parameters + * @param {Object} params The query params */ - ,execQuery: function(query, callback, batch) { - this.execStmt(new Sql.String({query: query}), callback, batch); + ,execQuery: function(query, callback, params) { + this.execStmt(new Sql.String({query: query}), callback, params); } /* diff --git a/js/db/db-lot.js b/js/db/db-lot.js new file mode 100644 index 00000000..c17e8dd9 --- /dev/null +++ b/js/db/db-lot.js @@ -0,0 +1,109 @@ + +var Connection = require('./connection'); +var Model = require('./model'); + +module.exports = new Class({ + Extends: Vn.Form + ,Tag: 'db-lot' + ,Properties: { + /** + * The connection used to execute the statement. + */ + conn: { + type: Connection + ,set: function(x) { + this.model.conn = x; + } + ,get: function() { + return this.model.conn; + } + }, + /** + * The model query. + */ + query: { + type: String + ,set: function(x) { + this.model.query = x; + } + ,get: function() { + return this.model.query; + } + }, + /** + * The model select statement. + */ + stmt: { + type: Sql.Stmt + ,set: function(x) { + this.model.stmt = x; + } + ,get: function() { + return this.model.stmt; + } + }, + /** + * The lot used to execute the statement. + */ + lot: { + type: Vn.Lot + ,set: function(x) { + this.model.lot = x; + } + ,get: function() { + return this.model.lot; + } + } + } + + ,initialize: function(props) { + this.model = new Model(); + this.parent(props); + } + + ,appendChild: function(child) { + if (child.nodeType === Node.TEXT_NODE) + this.query = child.textContent; + } + + ,refresh: function() { + if (this._model) + this._model.refresh(); + } + + ,performOperations: function() { + if (this._model) + this._model.performOperations(); + } + + /** + * Get the index of the column from its name. + * + * @param {string} columnName The column name + * @return {integer} The column index or -1 if column not exists + */ + ,getColumnIndex: function(columnName) { + return this._model ? + this._model.getColumnIndex(columnName) : -1; + } + + /** + * Gets a value from the form using the column index. + * + * @param {string} columnName The column index + * @return {Object} The value + */ + ,getByIndex: function(column) { + return this._model.getByIndex(this._row, column); + } + + /** + * Sets a value on the form using the column index. + * + * @param {string} columnName The column index + * @param {Object} value The new value + */ + ,setByIndex: function(column, value) { + return this._model.setByIndex(this._row, column, value); + } +}); diff --git a/js/db/db.js b/js/db/db.js index e1ba4ec9..073799ae 100644 --- a/js/db/db.js +++ b/js/db/db.js @@ -1,17 +1,17 @@ -require ('sql/sql'); +require('sql/sql'); Db = module.exports = { - Connection : require ('./connection') - ,Result : require ('./result') - ,ResultSet : require ('./result-set') - ,Model : require ('./model') - ,Iterator : require ('./iterator') - ,SimpleIterator : require ('./simple-iterator') - ,Form : require ('./form') - ,Param : require ('./param') - ,Query : require ('./query') - ,Calc : require ('./calc') - ,CalcSum : require ('./calc-sum') + Connection : require('./connection') + ,Result : require('./result') + ,ResultSet : require('./result-set') + ,Model : require('./model') + ,Iterator : require('./iterator') + ,SimpleIterator : require('./simple-iterator') + ,Form : require('./form') + ,Query : require('./query') + ,Calc : require('./calc') + ,CalcSum : require('./calc-sum') + ,Lot : require('./db-lot') }; diff --git a/js/db/form.js b/js/db/form.js index dce50d30..5df50d51 100644 --- a/js/db/form.js +++ b/js/db/form.js @@ -67,7 +67,7 @@ module.exports = new Class({ $: { type: Object ,get: function() { - return this._model.getObject(this._row); + return this._model && this._model.getObject(this._row); } } } @@ -85,13 +85,13 @@ module.exports = new Class({ this.lastRow = this._row; this._ready = ready; - this.signalEmit('status-changed'); + this.emit('status-changed'); if (this._row == -1) this.row = this.lastRow; if (ready) - this.signalEmit('ready'); + this.emit('ready'); this.iterChanged(); } diff --git a/js/db/iterator.js b/js/db/iterator.js index e0303ebd..98f3078e 100644 --- a/js/db/iterator.js +++ b/js/db/iterator.js @@ -44,10 +44,10 @@ module.exports = new Class({ } /** - * Emits the 'iter-changed' signal on the form. + * Emits the 'change' signal on the form. */ ,iterChanged: function() { - this.signalEmit('iter-changed'); + this.emit('change'); } /** @@ -90,6 +90,16 @@ module.exports = new Class({ return this._model.getObject(this._row); } + /** + * Sets a value on the form. + * + * @param {String} columnName The column name + */ + ,get: function(columnName) { + if (!this._model) return undefined; + return this._model.get(this._row, columnName); + } + /** * Sets a value on the form. * diff --git a/js/db/model.js b/js/db/model.js index 4b2e60ee..e163fcac 100644 --- a/js/db/model.js +++ b/js/db/model.js @@ -73,16 +73,16 @@ Model.implement({ } }, /** - * The batch used to execute the statement. + * The lot used to execute the statement. */ - batch: { - type: Sql.Batch + lot: { + type: Vn.LotIface ,set: function(x) { - this.link({_batch: x}, {'changed': this._autoLoad}); - this._autoLoad(); + this.link({_lot: x}, {'change': this._autoLoad}); + this._onLotChange(); } ,get: function() { - return this._batch; + return this._lot; } }, /** @@ -189,7 +189,7 @@ Model.implement({ ,_conn: null ,_resultIndex: 0 - ,_batch: null + ,_lot: null ,_stmt: null ,_status: Status.CLEAN ,data: null @@ -232,39 +232,69 @@ Model.implement({ this.query = query; } + ,_getLotParams: function() { + if (!this._stmt) + return null; + var holders = this._stmt.findHolders(); + if (!holders) + return null; + var lotParams = this._lot ? this._lot.params : {}; + + if (lotParams == null) + lotParams = {}; + var params = {}; + for (var i = 0; i < holders.length; i++) + params[holders[i]] = lotParams[holders[i]]; + return params; + } + + ,_onLotChange: function() { + var lotParams = this._getLotParams(); + if (!Vn.Value.equals(lotParams, this._lastLotParams)) + this._autoLoad(); + } + ,_autoLoad: function() { if (this.autoLoad) this.refresh(); else this.clean(); } + + ,_isReady: function(params) { + if (!this._stmt || !this._conn) + return false; + + var holders = this._stmt.findHolders(); + if (!holders) + return true; + + for (var i = 0; i < holders.length; i++) + if (params[holders[i]] === undefined) + return false; + + return true; + } /** * Refresh the model data reexecuting the query on the database. */ - ,refresh: function() { - var ready = false; - - if (this._stmt && this._conn) { - var ids = this._stmt.findHolders(); - - if (ids) { - if (this._batch && this._batch.isReady()) { - ready = true; + ,refresh: function(params) { + var lotParams = this._getLotParams(); + var myParams = {}; - for (var i = 0; i < ids.length; i++) - if (!this._batch.get(ids[i])) { - ready = false; - break; - } - } - } else - ready = true; - } + Object.assign(myParams, lotParams); + Object.assign(myParams, params); - if (ready) { + if (this._filter && (!params || params.filter === undefined)) + myParams.filter = this._filter; + + this._lastLotParams = lotParams; + + if (this._isReady(myParams)) { this._setStatus(Status.LOADING); - this._conn.execStmt(this._stmt, this._selectDone.bind(this), this._batch); + this._conn.execStmt(this._stmt, + this._selectDone.bind(this), myParams); } else this.clean(); } @@ -344,7 +374,7 @@ Model.implement({ this._updatable = this._mainTable !== null && this._requestedUpdatable; if (oldValue != this._updatable) - this.signalEmit('updatable-changed'); + this.emit('updatable-changed'); } ,_refreshMainTable: function() { @@ -492,7 +522,7 @@ Model.implement({ */ ,get: function(rowIndex, columnName) { if (this.checkRowExists(rowIndex)) - return this.data[rowIndex][columnName]; + return this.data[rowIndex][columnName]; } /** @@ -541,9 +571,9 @@ Model.implement({ && op.oldValues[columnName] === undefined) op.oldValues[columnName] = row[columnName]; - this.signalEmit('row-updated-before', rowIndex); + this.emit('row-updated-before', rowIndex); row[columnName] = value; - this.signalEmit('row-updated', rowIndex, [columnName]); + this.emit('row-updated', rowIndex, [columnName]); if (this.mode == Mode.ON_CHANGE && !(op.type & Operation.INSERT)) @@ -596,12 +626,12 @@ Model.implement({ op.type |= Operation.DELETE; if (!this._requestedMainTable) { - this.signalEmit('row-deleted-before', rowIndex); + this.emit('row-deleted-before', rowIndex); this.data.splice(rowIndex, 1); - this.signalEmit('row-deleted', rowIndex); + this.emit('row-deleted', rowIndex); this._refreshRowIndexes(rowIndex); } else { - this.signalEmit('row-updated-before', rowIndex); + this.emit('row-updated-before', rowIndex); if (!op.oldValues) op.oldValues = []; @@ -619,7 +649,7 @@ Model.implement({ updatedCols.push(i); } - this.signalEmit('row-updated', rowIndex, updatedCols); + this.emit('row-updated', rowIndex, updatedCols); } if (this.mode === Mode.ON_CHANGE) @@ -650,7 +680,7 @@ Model.implement({ var op = this._createOperation(rowIndex); op.type |= Operation.INSERT; - this.signalEmit('row-inserted', rowIndex); + this.emit('row-inserted', rowIndex); return rowIndex; } @@ -662,14 +692,14 @@ Model.implement({ var ops = this._operations; if (ops.length === 0) { - this.signalEmit('operations-done'); + this.emit('operations-done'); return; } var stmts = new Sql.MultiStmt(); var query = new Sql.String({query: 'START TRANSACTION'}); - stmts.addStmt(query); + stmts.push(query); for (var i = 0; i < ops.length; i++) { query = null; @@ -690,12 +720,12 @@ Model.implement({ for (var tableIndex in op.tables) { var stmt = this._createDmlQuery(op, parseInt(tableIndex)); - query.addStmt(stmt); + query.push(stmt); } } if (query) { - stmts.addStmt(query); + stmts.push(query); } else { console.warn('Db.Model: %s', _('ErrorSavingChanges')); return; @@ -703,7 +733,7 @@ Model.implement({ } var query = new Sql.String({query: 'COMMIT'}); - stmts.addStmt(query); + stmts.push(query); this._conn.execStmt(stmts, this._onOperationsDone.bind(this, ops)); @@ -764,8 +794,8 @@ Model.implement({ dmlQuery.addTarget(target); - multiStmt.addStmt(dmlQuery); - multiStmt.addStmt(select); + multiStmt.push(dmlQuery); + multiStmt.push(select); return multiStmt; } @@ -795,7 +825,7 @@ Model.implement({ if (op.type & Operation.DELETE) { resultSet.fetchResult(); } else if (op.type & (Operation.INSERT | Operation.UPDATE)) { - this.signalEmit('row-updated-before', row.index); + this.emit('row-updated-before', row.index); var updatedCols = []; var cols = this.columns; @@ -823,14 +853,14 @@ Model.implement({ } } - this.signalEmit('row-updated', row.index, updatedCols); + this.emit('row-updated', row.index, updatedCols); } } resultSet.fetchResult(); // if (isOperation) - this.signalEmit('operations-done'); + this.emit('operations-done'); } /** @@ -844,9 +874,9 @@ Model.implement({ if (op.type & Operation.DELETE && !(op.type & Operation.INSERT)) { this.data.splice(row.index, 0, row); - this.signalEmit('row-inserted', row.index); + this.emit('row-inserted', row.index); } else if (op.type & Operation.UPDATE) { - this.signalEmit('row-updated-before', row.index); + this.emit('row-updated-before', row.index); var updatedCols = []; var cols = this.columns; @@ -858,7 +888,7 @@ Model.implement({ updatedCols.push(i); } - this.signalEmit('row-updated', row.index, updatedCols); + this.emit('row-updated', row.index, updatedCols); } } @@ -1064,8 +1094,8 @@ Model.implement({ ,_setStatus: function(status) { this._status = status; - this.signalEmit('status-changed', status); - this.signalEmit('status-changed-after', status); + this.emit('status-changed', status); + this.emit('status-changed-after', status); } ,_createTarget: function(tableIndex) { @@ -1088,8 +1118,8 @@ Model.implement({ const column = this.columnMap[pk]; const equalOp = new Sql.Operation({type: Sql.Operation.Type.EQUAL}); - equalOp.exprs.add(new Sql.Field({name: column.orgname})); - where.exprs.add(equalOp); + equalOp.push(new Sql.Field({name: column.orgname})); + where.push(equalOp); let pkValue = null; @@ -1100,9 +1130,9 @@ Model.implement({ pkValue = op.row[pk]; if (pkValue) - equalOp.exprs.add(new Sql.Value({value: pkValue})); + equalOp.push(new Sql.Value({value: pkValue})); else if (column.flags & Connection.Flag.AI && !useOldValues) - equalOp.exprs.add(new Sql.Function({name: 'LAST_INSERT_ID'})); + equalOp.push(new Sql.Function({name: 'LAST_INSERT_ID'})); else return null; } diff --git a/js/db/param.js b/js/db/param.js deleted file mode 100644 index 032a7231..00000000 --- a/js/db/param.js +++ /dev/null @@ -1,105 +0,0 @@ - -var Form = require('./form'); - -module.exports = new Class({ - Extends: Vn.Param - ,Tag: 'db-param' - ,Parent: 'form' - ,Properties: - { - /** - * The form field referenced by this param. - */ - column: - { - type: String - ,set: function(x) { - this._columnName = x; - this.refresh(); - } - ,get: function() { - this._columnName; - } - }, - /** - * The form referenced by this param. - */ - form: - { - type: Form - ,set: function(x) { - this.link({_form: x}, - { - 'status-changed': this.onFormChange - ,'iter-changed': this.onIterChange - }); - - this.refresh(); - } - ,get: function() { - return this._form; - } - }, - /** - * Determines whether the link to the form is unidirectional, ie, a - * change in the form updates the parameter but not vice versa. - */ - oneWay: - { - type: Boolean - ,set: function(x) { - this._oneWay = x; - } - ,get: function() { - return this._oneWay; - } - } - } - - ,_columnName: null - ,_form: null - ,_formLock: false - ,_columnIndex: -1 - ,_oneWay: false - ,_formValue: null - - ,initialize: function(props) { - this.parent(props); - this.on('changed', this.onChange, this); - } - - ,refresh: function() { - if (this._form) { - this.onFormChange(); - this.onIterChange(); - } - } - - ,onFormChange: function() { - if (this._columnName != null) - this._columnIndex = this._form.getColumnIndex(this._columnName); - } - - ,onIterChange: function() { - if (this._oneWay && this.value != null) - return; - - this._formLock = true; - - var formValue; - - if (this._columnIndex !== -1) - formValue = this._form.getByIndex(this._columnIndex); - else - formValue = undefined; - - this.value = formValue; - this._formLock = false; - } - - ,onChange: function() { - if (!this._formLock && this._columnIndex != -1 && !this.oneWay) - this._form.setByIndex(this._columnIndex, this._value); - } -}); - diff --git a/js/db/query.js b/js/db/query.js index eca97c8e..d1d4561b 100644 --- a/js/db/query.js +++ b/js/db/query.js @@ -1,100 +1,78 @@ -var Connection = require ('./connection'); +var Connection = require('./connection'); -module.exports = new Class -({ +module.exports = new Class({ Extends: Vn.Object ,Tag: 'db-query' - ,Properties: - { + ,Properties: { /** * The connection used to execute the statement. */ - conn: - { + conn: { type: Connection - ,set: function (x) - { + ,set: function(x) { this._conn = x; - this.onChange (); + this.onChange(); } - ,get: function () - { + ,get: function() { return this._conn; } }, /** * The model query. */ - query: - { + query: { type: String - ,set: function (x) - { - this._stmt = new Sql.String ({query: x}); - this.onChange (); + ,set: function(x) { + this._stmt = new Sql.String({query: x}); + this.onChange(); } - ,get: function () - { - return this._stmt.render (null); + ,get: function() { + return this._stmt.render(null); } }, /** * The model select statement. */ - stmt: - { + stmt: { type: Sql.Stmt - ,set: function (x) - { + ,set: function(x) { this._stmt = x; - this.onChange (); + this.onChange(); } - ,get: function () - { + ,get: function() { return this._stmt; } }, /** - * The batch used to execute the statement. + * The lot used to execute the statement. */ - batch: - { - type: Sql.Batch - ,set: function (x) - { - this.link ({_batch: x}, {'changed': this.onChange}); - this.onChange (); + lot: { + type: Vn.LotIface + ,set: function(x) { + this.link({_lot: x}, {'change': this.onChange}); + this.onChange(); } - ,get: function () - { - return this._batch; + ,get: function() { + return this._lot; } }, /** * Wether to execute automatically de query que it's ready. */ - autoLoad: - { + autoLoad: { type: Boolean, value: false } } - ,initialize: function (props) - { - this.parent (props); - } - - ,appendChild: function (child) - { + ,appendChild: function(child) { if (child.nodeType === Node.TEXT_NODE) this.query = child.textContent; } - ,loadXml: function (builder, node) - { - this.parent (builder, node); + ,loadXml: function(builder, node) { + this.parent(builder, node); var query = node.firstChild.nodeValue; @@ -102,21 +80,18 @@ module.exports = new Class this.query = query; } - ,execute: function () - { - this._conn.execStmt (this._stmt, - this.onQueryDone.bind (this), this._batch); + ,execute: function() { + this._conn.execStmt(this._stmt, + this.onQueryDone.bind(this), this._lot); } - ,onQueryDone: function (resultSet) - { - this.signalEmit ('ready', resultSet); + ,onQueryDone: function(resultSet) { + this.emit('ready', resultSet); } - ,onChange: function () - { - if (this.autoLoad && this._conn && this._stmt && this._batch) - this.execute (); + ,onChange: function() { + if (this.autoLoad && this._conn && this._stmt && this._lot) + this.execute(); } }); diff --git a/js/hedera/app.js b/js/hedera/app.js index faded048..0c325c4a 100644 --- a/js/hedera/app.js +++ b/js/hedera/app.js @@ -16,7 +16,7 @@ module.exports = new Class({ ,initialize: function() { window.onerror = this._onWindowError.bind(this); window.onunload = this._onWindowUnload.bind(this); - Vn.Hash.initialize(); + this._hash = new Vn.Hash({window: window}); var conn = new Db.Connection(); this.link({_conn: conn}, {'error': this._onConnError}); @@ -28,7 +28,10 @@ module.exports = new Class({ if (this.tryAutoLogin()) return; - var login = this._login = new Login({conn: this._conn}); + var login = this._login = new Login({ + conn: this._conn, + hash: this._hash + }); login.on('login', this._onLogin, this); login.show(); } @@ -39,7 +42,10 @@ module.exports = new Class({ if (this._gui) return; - var gui = this._gui = new Gui({conn: this._conn}); + var gui = this._gui = new Gui({ + conn: this._conn, + hash: this._hash + }); gui.on('logout', this._onLogout, this); gui.show(); } @@ -164,6 +170,7 @@ module.exports = new Class({ this._freeGui(); this.deinitAutoLogin(); this._conn.unref(); + this._hash.unref(); } // Auto login functionality @@ -171,15 +178,17 @@ module.exports = new Class({ ,_firstLogin: true ,initAutoLogin: function() { - var isGuest = new Vn.HashParam({ + var isGuest = new Vn.Param({ + lot: this._hash, type: Boolean, - key: 'guest' + name: 'guest' }); this.link({_isGuest: isGuest}, {'changed': this._onGuestChange}); - var token = new Vn.HashParam({ + var token = new Vn.Param({ + lot: this._hash, type: String, - key: 'token' + name: 'token' }); this.link({_token: token}, {'changed': this._onTokenChange}); } diff --git a/js/hedera/basket-checker.js b/js/hedera/basket-checker.js index abde2365..624a76ff 100644 --- a/js/hedera/basket-checker.js +++ b/js/hedera/basket-checker.js @@ -1,6 +1,7 @@ module.exports = { - check: function(conn, callback) { + check: function(conn, hash, callback) { + this.hash = hash; conn.execQuery('CALL myBasket_check', this._onBasketCheck.bind(this, callback)); }, @@ -18,7 +19,7 @@ module.exports = { if (callback) callback(isOk); if (!isOk) - Vn.Hash.set({form: 'ecomerce/checkout'}); + this.hash.setAll({form: 'ecomerce/checkout'}); } }; diff --git a/js/hedera/form.js b/js/hedera/form.js index c49484f5..cac2aec8 100644 --- a/js/hedera/form.js +++ b/js/hedera/form.js @@ -15,25 +15,33 @@ module.exports = new Class({ ,loadUi: function() { if (!this.isOpen) return; + + const conn = this.conn; + const hash = this.hash; - var builder = new Vn.Builder(); + const builder = new Vn.Builder(); builder.compileFile('forms/'+ this.formInfo.path +'/ui.xml'); - var scope = this.builder = builder.load(null, this); + const scope = this.builder = builder.load(null, this); this.$ = scope.$; - scope.link(null, {conn: this.conn}); - + scope.link(null, {conn, hash}); this.node = scope.$.form; - var models = scope.getByTagName('db-model'); + const paramsLot = this.$.params; + if (paramsLot) { + paramsLot.source = hash; + this.params = paramsLot; + } - for (var i = 0; i < models.length; i++) - models[i].conn = this.conn; + function setConnection(tagName) { + const objects = scope.getByTagName(tagName); + for (let i = 0; i < objects.length; i++) + objects[i].conn = conn; + } - var queries = scope.getByTagName('db-query'); - - for (var i = 0; i < queries.length; i++) - queries[i].conn = this.conn; + const tags = ['db-model', 'db-query', 'db-lot']; + for (let i = 0; i < tags.length; i++) + setConnection(tags[i]); if (this.node) { this.gui.setForm(this.node); diff --git a/js/hedera/gui.js b/js/hedera/gui.js index 982c9ac8..a2fa2c63 100644 --- a/js/hedera/gui.js +++ b/js/hedera/gui.js @@ -64,8 +64,10 @@ module.exports = new Class({ window.addEventListener('scroll', this._onScrollHandler ); } - this.hash = Vn.Hash; - this.formParam = new Vn.HashParam({key: 'form'}); + this.formParam = new Vn.Param({ + lot: this.hash, + name: 'form', + }); this.formParam.on('changed', this._onFormChange, this); if (!localStorage.getItem('hederaCookies')) { @@ -101,7 +103,7 @@ module.exports = new Class({ } ,_onConnClose: function() { - this.signalEmit('logout'); + this.emit('logout'); } ,_onConnLoadChange: function(conn, isLoading) { @@ -206,7 +208,7 @@ module.exports = new Class({ var a = this.createElement('a'); if (row.path) { - a.href = Vn.Hash.make({form: row.path}); + a.href = this.hash.make({form: row.path}); this.menuOptions[row.path] = a; } @@ -445,14 +447,14 @@ module.exports = new Class({ //++++++++++++++++++++++++++++++++++++++++++++++++++++++ Reports - ,openReport: function(reportName, batch) { + ,openReport: function(reportName, lot) { this.loaderPush(); var module = new Module('reports', reportName); - module.addCallback(this._onReportLoad.bind(this, batch)); + module.addCallback(this._onReportLoad.bind(this, lot)); } - ,_onReportLoad: function(batch, module) { + ,_onReportLoad: function(lot, module) { this.loaderPop(); if (module.error) { @@ -461,7 +463,7 @@ module.exports = new Class({ } var report = new module.klass(module, this); - report.open(batch); + report.open(lot); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++ Supplant diff --git a/js/hedera/login.js b/js/hedera/login.js index e47cdf9c..e6d9829c 100644 --- a/js/hedera/login.js +++ b/js/hedera/login.js @@ -69,7 +69,7 @@ module.exports = new Class({ if (user) localStorage.setItem('hederaLastUser', user); - this.signalEmit('login'); + this.emit('login'); } else { this._focusUserInput(); throw error; diff --git a/js/hedera/report.js b/js/hedera/report.js index 6be4e667..772aed81 100644 --- a/js/hedera/report.js +++ b/js/hedera/report.js @@ -9,8 +9,8 @@ module.exports = new Class({ this.parent(null); } - ,open: function(batch) { - this.batch = batch; + ,open: function(lot) { + this.lot = lot; this.createWindow(); } @@ -70,7 +70,7 @@ module.exports = new Class({ var scope = this.scope = builder.load(this.doc, this); scope.link(null, { - batch: this.batch, + lot: this.lot, conn: this.conn }); this.$ = scope.$; diff --git a/js/hedera/social-bar.js b/js/hedera/social-bar.js index d082068c..0930812f 100644 --- a/js/hedera/social-bar.js +++ b/js/hedera/social-bar.js @@ -1,33 +1,25 @@ -module.exports = new Class -({ +module.exports = new Class({ Extends: Htk.Widget ,Tag: 'htk-social-bar' - ,Properties: - { - conn: - { + ,Properties: { + conn: { type: Db.Connection - ,set: function (x) - { + ,set: function(x) { this._conn = x; - this._refresh (); + this._refresh(); } - ,get: function () - { + ,get: function() { return this._conn; } }, - priority: - { + priority: { type: Number - ,set: function (x) - { + ,set: function(x) { this._priority = x; - this._refresh (); + this._refresh(); } - ,get: function () - { + ,get: function() { return this._priority; } } @@ -35,42 +27,37 @@ module.exports = new Class ,_priority: 0 - ,initialize: function () - { - var node = this.createRoot ('div'); + ,initialize: function() { + var node = this.createRoot('div'); node.className = 'htk-social-bar'; } - ,_refresh: function () - { + ,_refresh: function() { if (!this._conn || this._priority === null) return; - var batch = new Sql.Batch (); - batch.addValue ('priority', this._priority); + const params = {priority: this._priority}; var query = 'SELECT title, link, icon FROM social ' +'WHERE priority >= #priority ORDER BY priority'; - this._conn.execQuery (query, this._onQueryDone.bind (this), batch); + this._conn.execQuery(query, this._onQueryDone.bind(this), params); } - ,_onQueryDone: function (resultSet) - { - Vn.Node.removeChilds (this._node); - var res = resultSet.fetchResult (); + ,_onQueryDone: function(resultSet) { + Vn.Node.removeChilds(this._node); + var res = resultSet.fetchResult(); - while (res.next ()) - { - var a = this.createElement ('a'); - a.href = res.get ('link'); + while (res.next()) { + var a = this.createElement('a'); + a.href = res.get('link'); a.target = '_blank'; - this._node.appendChild (a); + this._node.appendChild(a); - var img = this.createElement ('img'); - img.src = 'image/social/'+ res.get ('icon'); - img.alt = res.get ('title'); - img.title = res.get ('title'); - a.appendChild (img); + var img = this.createElement('img'); + img.src = 'image/social/'+ res.get('icon'); + img.alt = res.get('title'); + img.title = res.get('title'); + a.appendChild(img); } } }); diff --git a/js/hedera/tpv.js b/js/hedera/tpv.js index 3dd6e795..0ad62275 100644 --- a/js/hedera/tpv.js +++ b/js/hedera/tpv.js @@ -6,16 +6,16 @@ module.exports = new Class({ ,tpvStatus: null ,check: function(callback) { - this.tpvOrder = Vn.Hash.$.tpvOrder; - this.tpvStatus = Vn.Hash.$.tpvStatus; + this.tpvOrder = this.hash.$.tpvOrder; + this.tpvStatus = this.hash.$.tpvStatus; if (this.tpvStatus) { - var batch = new Sql.Batch(); - batch.addValue('transaction', this.tpvOrder); - batch.addValue('status', this.tpvStatus); - + const params = { + transaction: this.tpvOrder, + status: this.tpvStatus + }; var query = 'CALL myTpvTransaction_end(#transaction, #status)'; - this.conn.execQuery(query, null, batch); + this.conn.execQuery(query, null, params); } if (callback) @@ -66,15 +66,14 @@ module.exports = new Class({ } ,retryPay: function() { - var batch = new Sql.Batch(); - batch.addValue('transaction', parseInt(this.tpvOrder)); + const params = {transaction: parseInt(this.tpvOrder)}; var query = 'SELECT t.amount, m.companyFk ' +'FROM myTpvTransaction t ' +'JOIN tpvMerchant m ON m.id = t.merchantFk ' +'WHERE t.id = #transaction'; this.conn.execQuery(query, - this._onRetryPayDone.bind(this), batch); + this._onRetryPayDone.bind(this), params); } ,_onRetryPayDone: function(resultSet) { diff --git a/js/htk/assistant.js b/js/htk/assistant.js index 1a17dc68..53865d9a 100644 --- a/js/htk/assistant.js +++ b/js/htk/assistant.js @@ -42,7 +42,7 @@ module.exports = new Class({ set: function(x) { this._stepsIndex = x; this.setStep(this._stepIndex); - this.signalEmit('steps-change'); + this.emit('steps-change'); }, get: function() { return this._stepsIndex; @@ -132,7 +132,7 @@ module.exports = new Class({ this._stepName = stepName; this.currentStep = step; step.show(); - this.signalEmit('step-change', stepIndex); + this.emit('step-change', stepIndex); if (this.stepFunc) this.stepFunc(step); diff --git a/js/htk/column.js b/js/htk/column.js index 782e68c5..b8e64bd3 100644 --- a/js/htk/column.js +++ b/js/htk/column.js @@ -90,7 +90,7 @@ module.exports = new Class({ } ,changed: function(tr, newValue) { - this.signalEmit('changed', tr.rowIndex - 1, newValue); + this.emit('changed', tr.rowIndex - 1, newValue); } }); diff --git a/js/htk/column/button.js b/js/htk/column/button.js index b35dc65a..1c4565ae 100644 --- a/js/htk/column/button.js +++ b/js/htk/column/button.js @@ -69,6 +69,6 @@ module.exports = new Class({ }, buttonClicked: function(value, tr, button) { - this.signalEmit('clicked', value, tr.rowIndex - 1, button); + this.emit('clicked', value, tr.rowIndex - 1, button); } }); diff --git a/js/htk/dialog.js b/js/htk/dialog.js index f8d0f30b..e2a392d4 100644 --- a/js/htk/dialog.js +++ b/js/htk/dialog.js @@ -147,7 +147,7 @@ Dialog.implement({ ,hide(response) { if (!this._isOpen) return; this.parent(); - this.signalEmit('response', response); + this.emit('response', response); } ,createButton: function(label, response) { diff --git a/js/htk/field.js b/js/htk/field.js index 0a7e42a1..4c651c7c 100644 --- a/js/htk/field.js +++ b/js/htk/field.js @@ -3,10 +3,10 @@ var Widget = require('./widget'); module.exports = new Class({ Extends: Widget + ,Implements: Vn.ParamIface ,Tag: 'htk-field' ,Child: 'param' - ,Properties: - { + ,Properties: { value: { type: String ,set: function(x) { @@ -18,21 +18,48 @@ module.exports = new Class({ this.valueChanged(x); this.putValue(x); + this._notifyChanges(); } - ,get: function(x) { + ,get: function() { return this._value; } }, param: { - type: Vn.Param + type: Vn.ParamIface ,set: function(x) { - this.link({_param: x}, {'changed': this.onParamChange}); - this.onParamChange(); + this._setParam(x); } ,get: function() { return this._param; } }, + lot: { + type: Vn.LotIface + ,set: function(x) { + this._setLot(x); + } + ,get: function() { + return this._lot; + } + }, + name: { + type: String + ,set: function(x) { + this._setName(x); + } + ,get: function() { + return this._name; + } + }, + oneWay: { + type: Boolean + ,set: function(x) { + this._oneWay = x; + } + ,get: function() { + return this._oneWay; + } + }, editable: { type: Boolean ,set: function(x) { @@ -48,21 +75,19 @@ module.exports = new Class({ form: { type: Db.Iterator ,set: function(x) { - this._form = x; - this.bindToForm(); + this.lot = x; } ,get: function() { - return this._form; + return this._lot; } }, column: { type: String ,set: function(x) { - this._paramName = x; - this.bindToForm(); + this.name = x; } ,get: function() { - return this._paramName; + return this._name; } }, conditionalFunc: { @@ -71,27 +96,20 @@ module.exports = new Class({ } } - ,_value: undefined - ,_param: null ,_editable: true - ,_blockParamChange: false - ,_blockValueChange: false + ,_lockField: false - ,onParamChange: function() { - if (!this._blockValueChange) { - this._blockParamChange = true; - this.value = this._param.value; - this._blockParamChange = false; - } - } - - ,bindToForm: function() { - if (this._form && this._paramName) - this.param = new Db.Param - ({ - form: this._form - ,column: this._paramName - }); + ,_setValue: function(newValue) { + if (!this._putValue(newValue)) + return; + + if (!this._lockField) + this.putValue(newValue); + + if (this.conditionalFunc) + this.conditionalFunc(this, newValue); + + this._notifyChanges(); } /** @@ -100,7 +118,7 @@ module.exports = new Class({ * * @param {Boolean} editable Whether the user is allowed to edit the entry */ - ,setEditable: function(editable) {} + ,setEditable: function() {} /** * Virtual method that must be implemented by class childs to put the value @@ -108,7 +126,7 @@ module.exports = new Class({ * * @param {Object} value The new value for the entry */ - ,putValue: function(value) {} + ,putValue: function() {} /** * Protected method that should be called from class childs when the value @@ -117,18 +135,9 @@ module.exports = new Class({ * @param {Object} value The new entry value */ ,valueChanged: function(value) { - this._value = value; - - if (this.conditionalFunc) - this.conditionalFunc(this, value); - - if (this._param && !this._blockParamChange) { - this._blockValueChange = true; - this._param.value = value; - this._blockValueChange = false; - } - - this.signalEmit('changed'); + this._lockField = true; + this._setValue(value); + this._lockField = false; } }); diff --git a/js/htk/field/entry.js b/js/htk/field/entry.js index 6f6fbd9b..fed77876 100644 --- a/js/htk/field/entry.js +++ b/js/htk/field/entry.js @@ -1,10 +1,8 @@ -module.exports = new Class -({ +module.exports = new Class({ Extends: Htk.Field ,Tag: 'htk-entry' - ,Properties: - { + ,Properties: { /** * Displayed text when there is no content. */ @@ -52,7 +50,7 @@ module.exports = new Class node.addEventListener('change', this._onChange.bind(this)); } - ,_onChange: function(event) { + ,_onChange: function() { var newValue; if (this.node.value == '') diff --git a/js/htk/field/select.js b/js/htk/field/select.js index d7f773a1..04d6b78c 100644 --- a/js/htk/field/select.js +++ b/js/htk/field/select.js @@ -153,7 +153,7 @@ module.exports = new Class({ popup.on('closed', this._onPopupClose.bind(this)); popup.show(this.node); - this.signalEmit('menu-show'); + this.emit('menu-show'); e.stopPropagation(); } @@ -184,7 +184,7 @@ module.exports = new Class({ ,_onPopupClose: function() { this._popup = null; - this.signalEmit('menu-hide'); + this.emit('menu-hide'); } ,_refreshShowText: function() { @@ -204,14 +204,14 @@ module.exports = new Class({ ,_onModelChange: function() { var model = this._model; - this.signalEmit('status-changed'); + this.emit('status-changed'); if (this._popup) this._popup.reset(); if (model && model.ready) { this._selectOption(); - this.signalEmit('ready'); + this.emit('ready'); } else this._setRow(-1); } diff --git a/js/htk/field/table.js b/js/htk/field/table.js index b1b39464..59c95301 100644 --- a/js/htk/field/table.js +++ b/js/htk/field/table.js @@ -1,58 +1,50 @@ -var Entry = require ('./entry'); -var ColumnRadio = require ('../column/radio'); +var Entry = require('./entry'); +var ColumnRadio = require('../column/radio'); -module.exports = new Class -({ +module.exports = new Class({ Extends: Entry ,Tag: 'htk-table' - ,render: function () - { - var tv = new Htk.TreeView (); - this.node.appendChild (tv.node); + ,render: function() { + var tv = new Htk.TreeView(); + this.node.appendChild(tv.node); - var renderer = new ColumnRadio (); - tv.appendColumn (0, renderer, ''); + var renderer = new ColumnRadio(); + tv.appendColumn(0, renderer, ''); var rbGroup = renderer.rbGroup; - rbGroup.addSignal ('changed', this.changed, this); + rbGroup.addSignal('changed', this.changed, this); this.treeview = tv; this.rbGroup = rbGroup; } - ,setModel: function (model) - { - this.treeview.setModel (model); - model.addSignal ('status-changed', this.modelRefresh, this); - this.selectValue (); + ,setModel: function(model) { + this.treeview.setModel(model); + model.addSignal('status-changed', this.modelRefresh, this); + this.selectValue(); } - ,changed: function (rbGroup) - { - this.realValue = this.rbGroup.getValue (); - this.signalEmit ('changed'); + ,changed: function() { + this.realValue = this.rbGroup.getValue(); + this.emit('changed'); } - ,selectValue: function () - { - this.rbGroup.setValue (this.realValue); + ,selectValue: function() { + this.rbGroup.setValue(this.realValue); } - ,setRealValue: function () - { - this.selectValue (); + ,setRealValue: function() { + this.selectValue(); } - ,modelRefresh: function (model, status) - { + ,modelRefresh: function(model, status) { if (status == Db.Model.Status.READY) - this.selectValue (); + this.selectValue(); } - ,setEditable: function (editable) - { - this.rbGroup.setEditable (editable); + ,setEditable: function(editable) { + this.rbGroup.setEditable(editable); } }); diff --git a/js/htk/image-editor.js b/js/htk/image-editor.js index cd0bcde2..cb348f07 100644 --- a/js/htk/image-editor.js +++ b/js/htk/image-editor.js @@ -35,7 +35,7 @@ module.exports = new Class({ if (!newValue) newValue = null - this.signalEmit('name-changed', newValue); + this.emit('name-changed', newValue); }, _onSubmit: function() { @@ -55,7 +55,7 @@ module.exports = new Class({ throw error; Toast.showMessage(_('ImageAdded')); - this.signalEmit('file-uploaded', this.$.name.value); + this.emit('file-uploaded', this.$.name.value); }, setData: function(image, directory) { diff --git a/js/htk/popup.js b/js/htk/popup.js index 72dfd9ec..044f5436 100644 --- a/js/htk/popup.js +++ b/js/htk/popup.js @@ -185,7 +185,7 @@ module.exports = new Class Vn.Node.remove(this._node); this._parent = null; this._isOpen = false; - this.signalEmit('closed'); + this.emit('closed'); } ,_bgMouseDown: function(e) { diff --git a/js/htk/repeater.js b/js/htk/repeater.js index 86aa0348..231f747a 100644 --- a/js/htk/repeater.js +++ b/js/htk/repeater.js @@ -169,7 +169,7 @@ module.exports = new Class({ } this.node.appendChild(this._container); - this.signalEmit('change'); + this.emit('change'); } ,_showNoRecordsFound: function() { diff --git a/js/htk/widget.js b/js/htk/widget.js index f78860e0..5f911e36 100644 --- a/js/htk/widget.js +++ b/js/htk/widget.js @@ -68,7 +68,8 @@ const Widget = new Class({ type: String ,set: function(x) { this._htmlId = x; - this._node.id = x; + if (this._node) + this._node.id = x; } ,get: function() { return this._htmlId; @@ -85,7 +86,9 @@ const Widget = new Class({ } ,createRoot: function(tagName) { - return this._node = this.createElement(tagName); + const node = this._node = this.createElement(tagName); + if (this._htmlId) node.id = this._htmlId; + return node; } ,renderBase: function() { diff --git a/js/sql/batch.js b/js/sql/batch.js deleted file mode 100644 index 6071e48e..00000000 --- a/js/sql/batch.js +++ /dev/null @@ -1,142 +0,0 @@ - -var Object = require('./object'); -var Value = require('./value'); - -/** - * A map container for many Sql.Object - */ -module.exports = new Class({ - Extends: Object - ,Tag: 'sql-batch' - ,Properties: { - blocked: { - type: Boolean - ,set: function(x) { - this._blocked = x; - } - ,get: function() { - return this._blocked; - } - } - } - - ,objects: {} - ,_blocked: false - - ,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') { - var object; - var id = childs[i].getAttribute('name'); - - 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) { - if (this.objects[id]) - return this.objects[id]; - - return null; - } - - ,add: function(id) { - if (!this.objects[id]) - this.objects[id] = null; - } - - ,_addObject: function(id, object) { - this.remove(id); - this.objects[id] = object; - object.on('changed', this.emitChanged, this); - this.emitChanged(); - } - - ,addObject: function(id, object) { - this._addObject(id, object.ref()); - } - - ,addValue: function(id, value) { - this._addObject(id, - new Value({value: value})); - } - - ,addValues: function(values) { - for (var id in values) - this.addValue(id, values[id]); - } - - ,addParam: function(id, param) { - this._addObject(id, - new Value({param: param})); - } - - ,getValue: function(id) { - var object = this.objects[id]; - - if (object instanceof Value) - return object.value; - - return null; - } - - ,addParams: function(params) { - for (var id in params) - this.addParam(id, params[id]); - } - - ,remove: function(id) { - if (this.objects[id]) { - this._unrefObject(this.objects[id]); - delete this.objects[id]; - } - } - - ,block: function() { - this._blocked = true; - } - - ,unblock: function() { - this._blocked = false; - } - - ,emitChanged: function() { - if (!this._blocked) - this.signalEmit('changed'); - } - - ,changed: function() { - this.signalEmit('changed'); - } - - ,isReady: function() { - for (var id in this.objects) - 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(); - } - } - - ,_destroy: function() { - for (var id in this.objects) - this._unrefObject(this.objects[id]); - - this.parent(); - } -}); diff --git a/js/sql/delete.js b/js/sql/delete.js index 41ee95f5..53447391 100644 --- a/js/sql/delete.js +++ b/js/sql/delete.js @@ -4,19 +4,14 @@ var Stmt = require('./stmt'); /** * The equivalent of a SQL delete. */ -module.exports = new Class -({ +module.exports = new Class({ Extends: Stmt + ,Tag: 'sql-delete' - ,render: function(batch) { - var sql = 'DELETE FROM ' + this.renderTarget(batch); - - if (this.where) - sql += ' WHERE ' + this.where.render(batch); - - sql += ' LIMIT 1'; // Only for security. - - return sql; + ,render: function(params) { + return 'DELETE FROM' + + this.renderTarget(params) + + this.renderIfSet(this.where, 'WHERE', params) + + this.renderLimit(params); } }); - diff --git a/js/sql/dml.js b/js/sql/dml.js index 31f2bab8..00ad03f5 100644 --- a/js/sql/dml.js +++ b/js/sql/dml.js @@ -13,13 +13,13 @@ module.exports = new Class({ ,expr: [] ,addSet: function(fieldName, value) { - this.expr.push(new Value({value: value})); this.field.push(new Field({name: fieldName})); + this.expr.push(new Value({value: value})); } ,addExpr: function(fieldName, expr) { - this.expr.push(expr); this.field.push(new Field({name: fieldName})); + this.expr.push(expr); } ,delSet: function() { diff --git a/js/sql/field.js b/js/sql/field.js index 315d5af1..ccd7f1bf 100644 --- a/js/sql/field.js +++ b/js/sql/field.js @@ -1,5 +1,5 @@ -var Expr = require('./expr'); +var Expr = require ('./expr'); /** * The equivalent of a SQL field. @@ -12,11 +12,17 @@ module.exports = new Class ,Tag: 'sql-field' ,Properties: { + /** + * The column name. + */ name: { type: String ,value: null }, + /** + * The source table name or its alias if it has been specified. + */ target: { type: String @@ -24,8 +30,9 @@ module.exports = new Class } } - ,render: function(batch) { - var sql = (this.target) ? '`' + this.target + '`.' : ''; - return sql + '`' + this.name + '`'; + ,render: function () + { + return this.renderPreIdent (this.target) + + this.renderIdent (this.name); } }); diff --git a/js/sql/filter-item.js b/js/sql/filter-item.js index a8d08d8e..1f0286be 100644 --- a/js/sql/filter-item.js +++ b/js/sql/filter-item.js @@ -1,17 +1,68 @@ var Operation = require('./operation'); +var Value = require('./value'); +var Field = require('./field'); /** - * The equivalent of a SQL operation. + * Objects to be used as an operands of @Sql.Filter. It represents a two + * expressions basic operation composed by a table column, the operator and the + * value extracted from the rendering paramerers. */ module.exports = new Class({ Extends: Operation ,Tag: 'sql-filter-item' ,Properties: { - primary: { - type: Boolean + /** + * The column name. + */ + field: { + type: String, + value: null + }, + /** + * The source table name or its alias if it has been specified. + */ + target: { + type: String, + value: null + }, + /** + * The parameter name. + */ + param: { + type: String, + value: null } } - - ,primary: true + + /** + * Checks if parameter name haa been defined and if it has a value. + */ + ,isReady: function(params) { + return this.param != null && params != null && params[this.param] != null; + } + + ,render: function(params) { + var newOp = new Operation({type: this.type}); + + newOp.push(new Field({ + name: this.field, + target: this.target + })); + + var value = params[this.param]; + + if (this.type === Operation.Type.LIKE && typeof value === 'string') { + value = value.replace(/[\%\?]/g, this._escapeChar); + value = value.replace(/^|\s+|$/g, '%'); + } + + newOp.push(new Value({value: value})); + + return newOp.render(params); + } + + ,_escapeChar: function(char) { + return '\\'+ char; + } }); diff --git a/js/sql/filter.js b/js/sql/filter.js index 8b515905..53e9f8ec 100644 --- a/js/sql/filter.js +++ b/js/sql/filter.js @@ -1,44 +1,52 @@ -var Operation = require('./operation'); +var Operation = require ('./operation'); +var Value = require ('./value'); /** - * The equivalent of a SQL operation. + * The equivalent of a SQL filter expression. It allows to automatically build + * SQL filters based on lot parameters. */ -module.exports = new Class({ +module.exports = new Class +({ Extends: Operation ,Tag: 'sql-filter' - ,Properties: { - alwaysReady: { - type: Boolean - } - } - ,isReady: function() { - if (this.alwaysReady) + /** + * Checks if any of filters childs are ready. + */ + ,isReady: function (params) + { + var exprs = this.exprs; + for (var i = exprs.length; i--;) + if (exprs[i].isReady (params)) return true; - - var e = this.exprs.getArray(); - for (var i = 0; i < e.length; i++) - if (e[i].isReady() && e[i].primary) - return true; - + return false; } - ,render: function(batch) { - var isReady = false; - var newOp = new Operation({type: this.type}); + /** + * Renders the filter as an SQL expression. If any of its childs isn't + * ready is ommitted from the expression. If all of its childs aren't ready + * renders the TRUE expression. + */ + ,render: function (params) + { + var newOp; + var newExprs = []; + + this.exprs.forEach (function (expr) { + if (expr.isReady (params)) + newExprs.push (expr); + }) - var e = this.exprs.getArray(); - for (var i = 0; i < e.length; i++) - if (e[i].isReady()) { - newOp.exprs.add(e[i]); - isReady = true; - } + if (newExprs.length > 0) + newOp = new Operation ({ + type: this.type, + exprs: newExprs + }); + else + newOp = new Value ({value: true}); - if (!isReady) - return 'TRUE'; - - return newOp.render(batch); + return newOp.render (params); } }); diff --git a/js/sql/function.js b/js/sql/function.js index b24df9d9..113d0de9 100644 --- a/js/sql/function.js +++ b/js/sql/function.js @@ -1,38 +1,56 @@ var Expr = require ('./expr'); -var List = require ('./list'); +var ListHolder = require ('./list-holder'); /** * The equivalent of a SQL function. - * - * @param {string} funcName The name of the function - * @param {Array#Sql.Expr} param Array with function parameters */ module.exports = new Class ({ Extends: Expr + ,Tag: 'sql-function' + ,Implements: ListHolder ,Properties: { + /** + * The function name. + */ name: { type: String ,value: null }, + /** + * The function schema. + */ schema: { type: String ,value: null }, + /** + * The function parameters. + */ params: { - type: List - ,value: null + type: Array + ,set: function (x) + { + this.list = x; + } + ,get: function () + { + return this.list; + } } } - ,render: function (batch) + ,render: function (params) { - var sql = (this.schema) ? '`' + this.schema + '`.' : ''; - return sql + '`' + this.name + '`()'; + return this.renderPreIdent (this.schema) + + this.renderIdent (this.name) + + '(' + + this.renderListWs (this.list, params, ', ') + + ')'; } }); diff --git a/js/sql/holder.js b/js/sql/holder.js index 797731e2..63288eab 100644 --- a/js/sql/holder.js +++ b/js/sql/holder.js @@ -1,12 +1,13 @@ -var Object = require('./object'); +var SqlObject = require ('./object'); +var Value = require ('./value'); /** * A holder for another object. */ module.exports = new Class ({ - Extends: Object + Extends: SqlObject ,Properties: { id: @@ -16,11 +17,24 @@ module.exports = new Class } } - ,render: function(batch) { - var object; + ,render: function (params) + { + if (params) + { + var object = params[this.id]; - if (batch && (object = batch.get(this.id))) - return object.render(batch); + if (object !== undefined) + { + if (!(object instanceof SqlObject)) + { + var sqlValue = new Value (); + sqlValue.value = object; + return sqlValue.render (); + } + else + return object.render (params); + } + } return '#'+ this.id; } diff --git a/js/sql/insert.js b/js/sql/insert.js index a74d29ae..0f6b46a9 100644 --- a/js/sql/insert.js +++ b/js/sql/insert.js @@ -8,31 +8,14 @@ module.exports = new Class ({ Extends: Dml - ,render: function (batch) + ,render: function (params) { - var sql; - var n; - - sql = 'INSERT INTO ' + this.renderTarget (batch) + ' ('; - - for (n = 0; n < this.field.length; n++) - { - if (n > 0) - sql += ', '; - sql += this.field[n].render (batch); - } - - sql += ') VALUES ('; - - for (n = 0; n < this.field.length; n++) - { - if (n > 0) - sql += ', '; - sql += this.expr[n].render(batch); - } - - sql += ')'; - - return sql; + return 'INSERT INTO' + + this.renderTarget (params) + + ' (' + + this.renderListWs (this.field, params, ', ') + + ') VALUES (' + + this.renderListWs (this.expr, params, ', ') + + ')'; } }) diff --git a/js/sql/join-item.js b/js/sql/join-item.js new file mode 100644 index 00000000..8a765790 --- /dev/null +++ b/js/sql/join-item.js @@ -0,0 +1,54 @@ + +var Target = require ('./target'); +var Expr = require ('./expr'); +var SqlObject = require ('./object'); +var Type = require ('./join').Type; + +var TypeSql = [ + 'INNER', + 'LEFT', + 'RIGHT' +]; + +/** + * The equivalent of a SQL join. + */ +module.exports = new Class +({ + Extends: SqlObject + ,Tag: 'sql-join-table' + ,Properties: + { + /** + * The join type. + */ + type: + { + enumType: Type + ,value: 0 + }, + /** + * The right target. + */ + target: + { + type: Target + ,value: null + }, + /** + * The join on condition. + */ + condition: + { + type: Expr + ,value: null + } + } + + ,render: function (params) + { + return TypeSql[this.type] +' JOIN ' + + this.target.render (params) + + this.renderIfSet (this.condition, 'ON', params); + } +}); diff --git a/js/sql/join.js b/js/sql/join.js new file mode 100644 index 00000000..40664d30 --- /dev/null +++ b/js/sql/join.js @@ -0,0 +1,54 @@ + +var Target = require ('./target'); +var ListHolder = require ('./list-holder'); + +/** + * The equivalent of a SQL join. + */ +var Klass = new Class (); +module.exports = Klass; + +var Type = { + INNER : 0, + LEFT : 1, + RIGHT : 2 +}; + +Klass.extend +({ + Type: Type +}); + +Klass.implement +({ + Extends: Target + ,Implements: ListHolder + ,Tag: 'sql-join' + ,Properties: + { + /** + * The right targets. + */ + targets: + { + type: Array + ,set: function (x) + { + this.list = x; + } + ,get: function () + { + return this.list; + } + } + } + + ,render: function (params) + { + return '(' + + this.target.render (params) + + ' ' + + this.renderList (this.list, params) + + ')'; + } +}); diff --git a/js/sql/list-holder.js b/js/sql/list-holder.js new file mode 100644 index 00000000..4e42825f --- /dev/null +++ b/js/sql/list-holder.js @@ -0,0 +1,60 @@ +/** + * Interface for array holders. + */ +module.exports = new Class +({ + Properties: + { +/* + list: + { + type: Array + ,set: function (x) + { + this._list = x; + } + ,get: function () + { + return this._list; + } + } +*/ + } + + ,list: [] + + ,appendChild: function (child) + { + this.list.push (child); + } + + /** + * Adds an element to the list. + * + * @param {SqlObject} element The element to add + */ + ,push: function (element) + { + this.list.push (element); + } + + /** + * Removes an element from the list. + * + * @param {Number} i The element index + */ + ,splice: function (i) + { + this.list.splice (i); + } + + /** + * Adds an element to the list. + * + * @param {Number} i The element index + */ + ,get: function (i) + { + return this.list[i]; + } +}); diff --git a/js/sql/list.js b/js/sql/list.js index df73cac9..00af5453 100644 --- a/js/sql/list.js +++ b/js/sql/list.js @@ -1,60 +1,69 @@ -var Object = require('./object'); +var Object = require ('./object'); /** * List of Sql.Object */ -module.exports = new Class({ +module.exports = new Class +({ Extends: Object ,objects: [] - ,add: function(object) { - this.objects.push(object.ref()); - object.on('changed', this._onObjectChange, this); - this._onObjectChange(); + ,add: function (object) + { + this.objects.push (object.ref ()); + object.on ('changed', this._onObjectChange, this); + this._onObjectChange (); } - ,get: function(i) { + ,get: function (i) + { return objects[i]; } - ,getArray: function() { + ,getArray: function () + { return this.objects; } - ,remove: function(i) { - this._unrefObject(this.objects.splice(i, 1)); - this._onObjectChange(); + ,remove: function (i) + { + this._unrefObject (this.objects.splice (i, 1)); + this._onObjectChange (); } - ,_onObjectChange: function() { - this.signalEmit('changed'); + ,_onObjectChange: function () + { + this.emit ('changed'); } - ,isReady: function() { + ,isReady: function () + { var o = this.objects; if (o.length == 0) return false; for (var i = 0; i < o.length; i++) - if (!o[i].isReady()) + if (!o[i].isReady ()) return false; return true; } - ,_unrefObject: function(object) { - object.disconnect('changed', this._onObjectChange, this); - object.unref(); + ,_unrefObject: function (object) + { + object.disconnect ('changed', this._onObjectChange, this); + object.unref (); } - ,_destroy: function() { + ,_destroy: function () + { for (var i = 0; i < this.objects.length; i++) - this._unrefObject(this.objects[i]); + this._unrefObject (this.objects[i]); - this.parent(); + this.parent (); } }); diff --git a/js/sql/multi-stmt.js b/js/sql/multi-stmt.js index ecd91a91..67fcfe40 100644 --- a/js/sql/multi-stmt.js +++ b/js/sql/multi-stmt.js @@ -1,5 +1,6 @@ var Stmt = require ('./stmt'); +var ListHolder = require ('./list-holder'); /** * The equivalent of a SQL multi statement. @@ -7,43 +8,29 @@ var Stmt = require ('./stmt'); module.exports = new Class ({ Extends: Stmt - - ,stmts: [] - - ,addStmt: function (stmt) + ,Implements: ListHolder + ,Tag: 'sql-multi-stmt' + ,Properties: { - return this.stmts.push (stmt); - } - - ,getStmt: function (stmtIndex) - { - return this.stmts[index]; - } - - ,isReady: function () - { - if (this.stmts.length == 0) - return false; - - for (var i = 0; i < this.stmts.length; i++) - if (!this.stmts[i].isReady ()) - return false; - - return true; - } - - ,render: function (batch) - { - var sql = ''; - - for (var i = 0; i < this.stmts.length; i++) + /** + * The statements list. + */ + stmts: { - if (i > 0) - sql += ";\n"; - - sql += this.stmts[i].render (batch); + type: Array + ,set: function (x) + { + this.list = x; + } + ,get: function () + { + return this.list; + } } + } - return sql; + ,render: function (params) + { + return this.renderListWs (this.list, params, ";\n"); } }); diff --git a/js/sql/object.js b/js/sql/object.js index 0da96489..848ac8ce 100644 --- a/js/sql/object.js +++ b/js/sql/object.js @@ -3,18 +3,11 @@ */ module.exports = new Class({ Extends: Vn.Object - - /** - * Renders the object as an SQL string. - * - * @param {Sql.Batch} batch The batch used to render the object - * @return {String} The SQL string - */ - ,render: function(batch) {} /** * Gets if the object is ready to be rendered. * + * @param {Object} params The query parameters * @return {boolean} %true if the object is ready, %false otherwise */ ,isReady: function() { @@ -22,9 +15,90 @@ module.exports = new Class({ } /** - * Through the query looking for containers and adds it to the batch. + * Through the query looking for containers. * - * @return {Sql.Batch} batch The batch + * @return {Array} An array with the names of the found parameters */ - ,findHolders: function(batch) {} + ,findHolders: function() {} + + /** + * Renders the object as an SQL string. + * + * @param {Object} params The params used to render the object + * @return {string} The SQL string + */ + ,render: function() {} + + /** + * Renders an objects array. + * + * @param {Array} list The objects array + * @param {Object} params The parameters + * @return {string} The rendered SQL string + */ + ,renderList: function(list, params) { + var sql = ''; + + list.forEach(function(item) { + sql += item.render(params); + }) + + return sql; + } + + /** + * Renders an objects array using a separator. + * + * @param {Array} list The objects array + * @param {Object} params The parameters + * @param {String} separator The separator between items + * @return {string} The rendered SQL string + */ + ,renderListWs: function(list, params, separator) { + var sql = ''; + + list.forEach(function(item, i) { + if (i > 0) + sql += separator; + sql += item.render(params); + }) + + return sql; + } + + /** + * Renders a quoted SQL identifier. + * + * @param {String} identifier The identifier + * @return {string} The quoted identifier + */ + ,renderIdent: function(identifier) { + return '`'+ identifier +'`'; + } + + /** + * Renders a quoted SQL identifier. + * + * @param {String} identifier The identifier + * @return {string} The quoted identifier + */ + ,renderPreIdent: function(identifier) { + if (identifier) + return this.renderIdent(identifier) +'.'; + else + return ''; + } + + /** + * Renders the object if it's defined. + * + * @param {String} prefix The rendered string prefix + * @return {string} The rendered object with its prefix + */ + ,renderIfSet: function(object, prefix, params) { + if (object) + return ' '+ prefix +' '+ object.render(params); + else + return ''; + } }); diff --git a/js/sql/operation.js b/js/sql/operation.js index a87a11c8..639ad3c6 100644 --- a/js/sql/operation.js +++ b/js/sql/operation.js @@ -1,78 +1,96 @@ -var Expr = require('./expr'); +var Expr = require ('./expr'); +var ListHolder = require ('./list-holder'); /** * The equivalent of a SQL operation between exprs. * - * @param {Array#Sql.Expr} expr Array with the exprs - * @param {Sql..Operation.Type} type The type of the operation + * @param {Array#Expr} exprs Array with the exprs + * @param {Type} type The type of the operation */ -var Operation = new Class(); -module.exports = Operation; +var Klass = new Class (); +module.exports = Klass; -var Type = { +var Type = +{ EQUAL : 0 ,LIKE : 1 ,AND : 2 ,OR : 3 ,REGEXP : 4 + ,LOWER : 5 + ,UPPER : 6 + ,LE : 7 + ,UE : 8 + ,PLUS : 9 + ,MINUS : 10 + ,MULT : 11 + ,DIV : 12 + ,NE : 13 + ,MOD : 14 }; -var Operators = [ +var Operators = +[ '=' ,'LIKE' ,'AND' ,'OR' ,'REGEXP' + ,'<' + ,'>' + ,'<=' + ,'>=' + ,'+' + ,'-' + ,'*' + ,'/' + ,'<>' + ,'MOD' ]; -Operation.extend({ +Klass.extend +({ Type: Type ,Operators: Operators }); -Operation.implement({ +Klass.implement +({ Extends: Expr + ,Implements: ListHolder ,Tag: 'sql-operation' - ,Properties: { - type: { + ,Properties: + { + type: + { enumType: Type ,value: -1 + }, + target: + { + type: String + ,value: null + }, + exprs: + { + type: Array + ,set: function (x) + { + this.list = x; + } + ,get: function () + { + return this.list; + } } } - ,initialize: function(props) { - this.parent(props); - this.link({exprs: new Sql.List()}, {'changed': this.onListChange}); - } - - ,appendChild: function(child) { - this.exprs.add(child); - } - - ,onListChange: function() { - this.signalEmit('changed'); - } - - ,isReady: function() { - return this.exprs.isReady(); - } - - ,render: function(batch) { - var sql = '('; + ,render: function (params) + { var operator = ' '+ Operators[this.type] +' '; - var e = this.exprs.getArray(); - - for (var i = 0; i < e.length; i++) { - if (i > 0) - sql += operator; - - sql += e[i].render(batch); - } - - sql += ')'; - - return sql; + return '(' + + this.renderListWs (this.list, params, operator) + + ')'; } }); - diff --git a/js/sql/search-tags.js b/js/sql/search-tags.js deleted file mode 100644 index 4c24ff80..00000000 --- a/js/sql/search-tags.js +++ /dev/null @@ -1,19 +0,0 @@ - -var Value = require('./value'); - -/** - * The equivalent of a SQL value. - */ -module.exports = new Class -({ - Extends: Value - ,Tag: 'sql-search-tags' - - ,render: function(batch) { - if (typeof this._value == 'string') { - var value = this._value.replace(/^|\s+|$/g, '%'); - return "'" + value.replace(this.regexp, this.replaceFunc) + "'"; - } else - return this.parent(); - } -}); diff --git a/js/sql/select.js b/js/sql/select.js index 4a0db861..958ae69a 100644 --- a/js/sql/select.js +++ b/js/sql/select.js @@ -16,22 +16,13 @@ module.exports = new Class this.expr.push (new Field ({name: fieldName})); } - ,render: function (batch) + ,render: function (params) { - var sql = 'SELECT ' - - for (var i = 0; i < this.expr.length; i++) - { - if (i > 0) - sql += ', '; - sql += this.expr[i].render(batch); - } - - sql += ' FROM ' + this.renderTarget (batch); - - if (this.where) - sql += ' WHERE ' + this.where.render (batch); - - return sql; + return 'SELECT ' + + this.renderListWs (this.expr, params, ', ') + + ' FROM' + + this.renderTarget (params) + + this.renderIfSet (this.where, 'WHERE', params) + + this.renderLimit (params); } }); diff --git a/js/sql/sql.js b/js/sql/sql.js index 7de038f9..068b8231 100644 --- a/js/sql/sql.js +++ b/js/sql/sql.js @@ -1,28 +1,29 @@ -require ('vn/vn'); +require('vn/vn'); Sql = module.exports = { - Object : require ('./object') - ,Holder : require ('./holder') - ,Batch : require ('./batch') - ,List : require ('./list') - ,Expr : require ('./expr') - ,Value : require ('./value') - ,Field : require ('./field') - ,Function : require ('./function') - ,Operation : require ('./operation') - ,Target : require ('./target') - ,Table : require ('./table') - ,Stmt : require ('./stmt') - ,Dml : require ('./dml') - ,String : require ('./string') - ,Delete : require ('./delete') - ,Insert : require ('./insert') - ,Select : require ('./select') - ,Update : require ('./update') - ,MultiStmt : require ('./multi-stmt') - ,Filter : require ('./filter') - ,FilterItem : require ('./filter-item') - ,SearchTags : require ('./search-tags') + Object : require('./object') + ,Holder : require('./holder') + ,List : require('./list') + ,ListHolder : require('./list-holder') + ,Expr : require('./expr') + ,Value : require('./value') + ,Field : require('./field') + ,Function : require('./function') + ,Operation : require('./operation') + ,Target : require('./target') + ,Table : require('./table') + ,Join : require('./join') + ,JoinItem : require('./join-item') + ,Stmt : require('./stmt') + ,Dml : require('./dml') + ,String : require('./string') + ,Delete : require('./delete') + ,Insert : require('./insert') + ,Select : require('./select') + ,Update : require('./update') + ,MultiStmt : require('./multi-stmt') + ,Filter : require('./filter') + ,FilterItem : require('./filter-item') }; diff --git a/js/sql/stmt.js b/js/sql/stmt.js index e447db74..a6d3e564 100644 --- a/js/sql/stmt.js +++ b/js/sql/stmt.js @@ -1,47 +1,40 @@ -var Object = require ('./object'); -var Expr = require ('./expr'); +var Object = require('./object'); +var Expr = require('./expr'); /** * The equivalent of a SQL statement. */ -module.exports = new Class -({ +module.exports = new Class({ Extends: Object - ,Properties: - { - where: - { + ,Properties: { + where: { type: Expr ,value: null + }, + limit: { + type: Number + ,value: null } } ,target: [] - ,addTarget: function (target) - { - this.target.push (target); + ,addTarget: function(target) { + this.target.push(target); } - ,renderTarget: function (batch) - { - var sql; - var len = this.target.length; - - if (len > 0) - { - sql = ' '; - - for (var n = 0; n < len; n++) - { - if (n > 0) sql += ', '; - sql += this.target[n].render (batch); - } - } + ,renderTarget: function(params) { + if (this.target.length > 0) + return ' '+ this.renderListWs(this.target, params, ', '); else - sql += 'DUAL'; - - return sql; - } + return ' DUAL'; + } + + ,renderLimit: function() { + if (this.limit != null) + return ' LIMIT '+ parseInt(this.limit); + else + return ''; + } }); diff --git a/js/sql/string.js b/js/sql/string.js index aa9fefb7..f95f3fbf 100644 --- a/js/sql/string.js +++ b/js/sql/string.js @@ -8,6 +8,7 @@ var Holder = require ('./holder'); module.exports = new Class ({ Extends: Stmt + ,Tag: 'sql-string' ,Properties: { query: @@ -18,19 +19,25 @@ module.exports = new Class } ,regexp: /#\w+/g - - ,replaceFunc: function (batch, token) + + ,appendChild: function (child) { - var holder = new Holder ({id: token.substr (1)}); - return holder.render (batch); + if (child.nodeType === Node.TEXT_NODE) + this.query = child.textContent; } - ,render: function (batch) + ,render: function (params) { if (!this.query) return null; + + function replaceFunc (token) + { + var holder = new Holder ({id: token.substr (1)}); + return holder.render (params); + } - return this.query.replace (this.regexp, this.replaceFunc.bind (this, batch)); + return this.query.replace (this.regexp, replaceFunc); } ,findHolders: function () diff --git a/js/sql/table.js b/js/sql/table.js index 3ab4cf9f..578e5ea5 100644 --- a/js/sql/table.js +++ b/js/sql/table.js @@ -1,5 +1,5 @@ -var Target = require('./target'); +var Target = require ('./target'); /** * Represents a database table. @@ -14,6 +14,11 @@ module.exports = new Class type: String ,value: null }, + alias: + { + type: String + ,value: null + }, schema: { type: String @@ -21,9 +26,14 @@ module.exports = new Class } } - ,render: function(batch) { - var sql = this.schema ? '`' + this.schema + '`.' : ''; - sql += '`' + this.name + '`'; + ,render: function () + { + var sql = this.renderPreIdent (this.schema) + + this.renderIdent (this.name); + + if (this.alias) + sql += ' AS '+ this.renderIdent (this.alias); + return sql; } }); diff --git a/js/sql/target.js b/js/sql/target.js index e45fda9a..219e346d 100644 --- a/js/sql/target.js +++ b/js/sql/target.js @@ -1,5 +1,5 @@ -var Object = require('./object'); +var Object = require ('./object'); /** * The equivalent of a SQL target. diff --git a/js/sql/update.js b/js/sql/update.js index 89d4ae8e..26594e71 100644 --- a/js/sql/update.js +++ b/js/sql/update.js @@ -8,25 +8,23 @@ module.exports = new Class ({ Extends: Dml - ,render: function (batch) + ,render: function (params) { - var sql; - var n; + var sql = 'UPDATE' + + this.renderTarget (params) + + ' SET '; - sql = 'UPDATE ' + this.renderTarget (batch) + ' SET '; - - for (n = 0; n < this.field.length; n++) - { - if (n > 0) + this.field.forEach (function (field, i) { + if (i > 0) sql += ', '; - sql += this.field[n].render () + ' = ' + this.expr[n].render(batch); - } + + sql += field.render (params) + + ' = ' + + this.expr[i].render(params); + }, this); - if (this.where) - sql += ' WHERE ' + this.where.render (batch); - - sql += ' LIMIT 1'; // Only for security. - + sql += this.renderIfSet (this.where, 'WHERE', params) + + this.renderLimit(params); return sql; } }); diff --git a/js/sql/value.js b/js/sql/value.js index c2523ef6..1650eead 100644 --- a/js/sql/value.js +++ b/js/sql/value.js @@ -5,64 +5,69 @@ var Expr = require('./expr'); * The equivalent of a SQL value. */ module.exports = new Class({ - Extends: Expr - ,Tag: 'sql-value' + Extends: Expr + ,Implements: Vn.ParamIface + ,Tag: 'sql-value' ,Properties: { - /** - * The master param. - */ - param: { - type: Vn.Param + value: { + type: null ,set: function(x) { - this.link({_param: x}, {'changed': this.onParamChange}); - this.onParamChange(); + this._setValue(x); + } + ,get: function() { + return this._value; + } + }, + type: { + type: Type + ,set: function(x) { + this._setType(x); + } + ,get: function() { + return this._type; + } + }, + param: { + type: Vn.ParamIface + ,set: function(x) { + this._setParam(x); } ,get: function() { return this._param; } }, - /** - * The value. - */ - value: { - type: String + lot: { + type: Vn.LotIface ,set: function(x) { - if (Vn.Value.compare(x, this._value)) - return; - - if (x instanceof Date) - x = x.clone(); - - this._value = x; - - if (this._param && !this.paramLock) { - this.paramLock = true; - this._param.value = x; - this.paramLock = false; - } - - this.signalEmit('changed'); + this._setLot(x); } ,get: function() { - return this._value; + return this._lot; + } + }, + name: { + type: String + ,set: function(x) { + this._name = x; + this._onLotChange(); + } + ,get: function() { + return this._name; + } + }, + oneWay: { + type: Boolean + ,set: function(x) { + this._oneWay = x; + } + ,get: function() { + return this._oneWay; } } } - ,_value: undefined - ,_param: null ,regexp: new RegExp('(\\\\)|\'', 'g') - ,paramLock: false - - ,onParamChange: function() { - if (this.paramLock || !this._param) - return; - - this.paramLock = true; - this.value = this._param.value; - this.paramLock = false; - } ,isReady: function() { return this._value !== undefined; diff --git a/js/vn/browser.js b/js/vn/browser.js index fde58c69..92986ab0 100644 --- a/js/vn/browser.js +++ b/js/vn/browser.js @@ -1,36 +1,29 @@ -module.exports = -{ - getPageYOffset: function () - { +module.exports = { + getPageYOffset: function() { return window.pageYOffset; }, - getPageXOffset: function () - { + getPageXOffset: function() { return window.pageXOffset; }, - getInnerHeight: function () - { + getInnerHeight: function() { return window.innerHeight; }, - getInnerWidth: function () - { + getInnerWidth: function() { return window.innerWidth; }, - createRadio: function (uid, doc) - { - var radio = doc.createElement ('input'); + createRadio: function(uid, doc) { + var radio = doc.createElement('input'); radio.type = 'radio'; radio.name = uid; return radio; }, - setInputTypeNumber: function (input) - { + setInputTypeNumber: function(input) { input.type = 'number'; } }; diff --git a/js/vn/builder.js b/js/vn/builder.js index 5041ba14..c6039494 100644 --- a/js/vn/builder.js +++ b/js/vn/builder.js @@ -2,6 +2,7 @@ const VnObject = require('./object'); const Widget = require('../htk/widget'); const VnNode = require('./node'); const Scope = require('./scope'); +const Type = require('./type'); const kebabToCamel = require('./string-util').kebabToCamel; /** @@ -488,6 +489,9 @@ module.exports = new Class({ case Function: context.funcProps[propName] = this._getMethod(value); break; + case Type: + newValue = window[value]; + break; default: if (propInfo.enumType) newValue = propInfo.enumType[value]; diff --git a/js/vn/date.js b/js/vn/date.js index 2c0d887a..24fb6598 100644 --- a/js/vn/date.js +++ b/js/vn/date.js @@ -8,8 +8,7 @@ Date.prototype.clone = function() { module.exports = { - WDays: - [ + WDays: [ 'Sunday' ,'Monday' ,'Tuesday' @@ -18,8 +17,7 @@ module.exports = ,'Friday' ,'Saturday' ] - ,AbrWDays: - [ + ,AbrWDays: [ 'Su' ,'Mo' ,'Tu' @@ -28,8 +26,7 @@ module.exports = ,'Fr' ,'Sa' ] - ,Months: - [ + ,Months: [ 'January' ,'February' ,'March' @@ -43,8 +40,7 @@ module.exports = ,'November' ,'December' ] - ,AbrMonths: - [ + ,AbrMonths: [ 'Jan' ,'Feb' ,'Mar' diff --git a/js/vn/enum.js b/js/vn/enum.js new file mode 100644 index 00000000..3c48b4aa --- /dev/null +++ b/js/vn/enum.js @@ -0,0 +1,4 @@ +/** + * Base type for ennumerations. + */ + module.exports = function() {}; diff --git a/js/vn/form.js b/js/vn/form.js new file mode 100644 index 00000000..3437407e --- /dev/null +++ b/js/vn/form.js @@ -0,0 +1,72 @@ + +var Iterator = require('./iterator'); +var ModelIface = require('./model-iface'); + +module.exports = new Class({ + Extends: Iterator + ,Tag: 'db-form' + ,Properties: { + model: { + type: ModelIface + ,set: function(x) { + this.link({_model: x}, + { + 'status-changed': this.onModelChange + ,'row-updated': this.onModelRowUpdate + }); + } + ,get: function() { + return this._model; + } + }, + /** + * The row where the form positioned, has -1 if the row is unselected. + */ + row: { + type: Number + ,set: function(x) { + if (!this._model || this._model.numRows <= x || x < -1) + x = -1; + if (x == this._row) + return; + + this._row = x; + this.rowChanged(); + } + ,get: function() { + return this._row; + } + } + } + + ,initialize: function(props) { + Object.assign(this, { + _lastRow: 0 + ,_lastReady: false + }); + this.parent(props); + } + + ,onModelChange: function() { + var ready = this._model && this._model.ready; + + if (ready != this._lastReady) { + if (this._row != -1) + this._lastRow = this._row; + + this._lastReady = ready; + this.emit('status-changed'); + + if (this._row == -1) + this.row = this._lastRow; + + if (ready) + this.emit('ready'); + } + } + + ,onModelRowUpdate: function(model, row) { + if (row == this._row) + this.rowChanged(); + } +}); diff --git a/js/vn/hash-listener.js b/js/vn/hash-listener.js deleted file mode 100644 index 2a554675..00000000 --- a/js/vn/hash-listener.js +++ /dev/null @@ -1,14 +0,0 @@ - -var Object = require('./object'); - -/** - * Class to handle the URL. - */ -module.exports = new Class -({ - Extends: Object - - ,changed: function() { - this.signalEmit('changed'); - } -}); diff --git a/js/vn/hash-param.js b/js/vn/hash-param.js deleted file mode 100644 index 40d23d15..00000000 --- a/js/vn/hash-param.js +++ /dev/null @@ -1,127 +0,0 @@ - -var Object = require('./object'); -var Param = require('./param'); -var Hash = require('./hash'); - -module.exports = new Class({ - Extends: Object - ,Tag: 'vn-hash-param' - ,Child: 'param' - ,Properties: { - param: { - type: Param - ,set: function(x) { - this.link({_param: x}, {'changed': this._onParamChange}); - this._refreshParam(); - } - ,get: function() { - return this._param; - } - }, - key: { - type: String - ,set: function(x) { - this._key = x; - this._onHashChange(); - } - ,get: function() { - return this._key; - } - }, - value: { - type: Object - ,set: function(x) { - this._setValue(x, true); - } - ,get: function() { - return this._value; - } - }, - type: { - type: Object - ,set: function(x) { - this._type = x; - this._onHashChange(); - } - ,get: function() { - return this._type; - } - } - } - - ,_hashLock: false - ,_paramLock: false - ,_value: undefined - ,_key: null - ,_type: null - - ,initialize: function(props) { - this.parent(props); - var listener = Hash.getListener(); - this.link({_listener: listener}, {'changed': this._onHashChange}); - this._onHashChange(); - } - - ,_onHashChange: function() { - if (this._hashLock || !this._key || !this._listener) - return; - - var newValue = Hash.get(this._key); - - if (newValue === '') - newValue = null; - - if (this._type && newValue !== undefined && newValue !== null) - switch (this._type) { - case Boolean: - newValue = (/^(true|1)$/i).test(newValue); - break; - case Number: - newValue = 0 + new Number(newValue); - break; - } - - this._hashLock = true; - this._setValue(newValue, true); - this._hashLock = false; - } - - ,_setValue: function(newValue, signal) { - if (newValue == this._value) - return; - - this._value = newValue; - - if (this._key && !this._hashLock) { - this._hashLock = true; - - var map = {}; - map[this._key] = newValue; - Hash.add(map); - - this._hashLock = false; - } - - this._refreshParam(); - - if (signal) - this.signalEmit('changed', newValue); - } - - ,_refreshParam: function() { - if (this._param && !this._paramLock) { - this._paramLock = true; - this._param.value = this._value; - this._paramLock = false; - } - } - - ,_onParamChange: function() { - if (this._paramLock) - return; - - this._paramLock = true; - this._setValue(this._param.value); - this._paramLock = false; - } -}); diff --git a/js/vn/hash.js b/js/vn/hash.js index 25bf22ef..655e2f10 100644 --- a/js/vn/hash.js +++ b/js/vn/hash.js @@ -1,137 +1,164 @@ -var HashListener = require('./hash-listener'); +var VnDate = require('./date'); +var Lot = require('./lot'); /** - * Class to handle the URL. + * Class to handle the hash part of the URL as a key-value javascript object. + * It also handles dates and objects as a value. */ -module.exports = { - path: null - ,_hash: null - ,_hashMap: {} - ,_listener: null - - ,initialize: function() { - this.$ = this._hasMap; - this._listener = new HashListener(); - - this._hashChangedHandler = this._hashChanged.bind(this); - window.addEventListener('hashchange', this._hashChangedHandler); - this._hashChanged(); - } - - ,destroy: function() { - window.removeEventListener('hashchange', this._hashChangedHandler); - } - - ,getListener: function() { - return this._listener; - } - - /** - * Gets the hash part of the URL. - * - * @param {string} key The variable name - */ - ,get: function(key) { - return this._hashMap[key]; - } - - /** - * Unsets a hash key. - * - * @param {string} key The variable name - */ - ,unset: function(key) { - this.add({[key]: undefined}); - } - - /** - * Sets the hash part of the URL, respecting the current hash variables. - * - * @param {Object} map A key-value map - */ - ,add: function(map) { - var newMap = this._hashMap; - - for (var key in map) - newMap[key] = map[key]; - - this.set(newMap); - } - - /** - * Sets the hash part of the URL. - * - * @param {Object} map A key-value map - */ - ,set: function(map) { - if (map) - for (var key in map) - if (map[key] === null || map[key] === undefined) - delete map[key]; - - var newHash = this.make(map); - - if (!map) - map = {}; - - if (newHash !== this._hash) { - this._hashMap = map; - this.$ = map; - this._hash = newHash; - - this._blockChanged = true; - location.hash = newHash; - this._blockChanged = false; - - this._listener.changed(); +module.exports = new Class({ + Extends: Lot + ,Properties: { + /** + * The main window object. + */ + window: + { + type: Window + ,set: function(x) { + this._window = x; + x.addEventListener('hashchange', this._hashChangedHandler); + this._hashChanged(); + } + ,get: function() { + return this._window; + } } } + ,initialize: function(props) { + Object.assign(this, { + _hash: null + ,_hashLock: false + ,_window: null + ,_hashChangedHandler: this._hashChanged.bind(this) + }); + this.parent(props); + } + + ,_paramsChanged: function() { + this.updateHash(); + } + /** * Creates a URL with the given hash data. * - * @param {Object} map A key-value map - * @param {boolean} add %true to combine with the current map, %false otherwise - * @return {String} The URL + * @param {Object} params A key-value object + * @param {boolean} add %true to combine with the current params, %false otherwise + * @return {string} The URL */ - ,make: function(map, add) { - var hash = '#!'; - - if (add && map) - for (var key in this._hashMap) - if (!map[key]) - map[key] = this._hashMap[key]; + ,make: function(params, add) { + if (add) { + params = Object.assign({}, params); - for (var key in map) { + for (var key in this._params) + if (!params[key]) + params[key] = this._params[key]; + } + + return this.renderHash(params); + } + + /** + * Updates the window hash with current params. + */ + ,updateHash: function() { + if (this._hashLock) + return; + + this._hash = this.renderHash(this._params); + + this._hashLock = true; + location.hash = this._hash; + this._hashLock = false; + } + + /* + * Called when window hash changes. + */ + ,_hashChanged: function() { + var newHash = location.hash; + + if (this._hashLock || this._hash === newHash) + return; + + this._hash = newHash; + + this._hashLock = true; + this.params = this.parseHash(newHash); + this._hashLock = false; + } + + /** + * Creates a URL with the given hash data. + * + * @param {Object} params The key-value object + * @return {string} The URL + */ + ,renderHash: function(params) { + var hash = '#!'; + + for (var key in params) + if (params[key] !== undefined) { if (hash.length > 2) hash += '&'; - hash += encodeURIComponent(key) +'='+ encodeURIComponent(map[key]); + hash += encodeURIComponent(key) +'='+ this.renderValue(params[key]); } return hash; } - ,_hashChanged: function() { - var newHash = location.hash; - - if (this._blockChanged || newHash === this._hash) - return; - + /** + * Parses a hash string to a key-value object. + * + * @param {string} hashString The hash string + * @return {Object} The key-value object + */ + ,parseHash: function(hashString) { var newMap = hashMap = {}; - var kvPairs = newHash.substring(2).split('&'); + var kvPairs = hashString.substr(2).split('&'); for (var i = 0; i < kvPairs.length; i++) { var kvPair = kvPairs[i].split('=', 2); if (kvPair[0]) - newMap[decodeURIComponent(kvPair[0])] = decodeURIComponent(kvPair[1]); + newMap[decodeURIComponent(kvPair[0])] = this.parseValue(kvPair[1]); } - this._hashMap = newMap; - this.$ = newMap; - this._hash = newHash; - this._listener.changed(); + return newMap; } -}; + + ,renderValue: function(v) { + if (v == null) + return ''; + + switch (typeof v) { + case 'object': + if (v instanceof Date) + return VnDate.strftime(v, '%Y-%m-%d'); + else + return JSON.stringify(v) + } + + return v; + } + + ,parseValue: function(v) { + if (v == null) + return v; + + v = decodeURIComponent(v); + + if (v === '') + return null; + + return v; + } + + ,_destroy: function() { + this._window.removeEventListener('hashchange', this._hashChangedHandler); + this._window = null; + this.parent(); + } +}); diff --git a/js/vn/ie.js b/js/vn/ie.js index cb8e7add..29b451f9 100644 --- a/js/vn/ie.js +++ b/js/vn/ie.js @@ -1,48 +1,40 @@ -module.exports = -{ - getPageYOffset: function () - { +module.exports = { + getPageYOffset: function() { if (document.documentElement.scrollTop) return document.documentElement.scrollTop; else return document.body.scrollTop; }, - getPageXOffset: function () - { + getPageXOffset: function() { if (document.documentElement.scrollLeft) return document.documentElement.scrollLeft; else return document.body.scrollLeft; }, - getInnerHeight: function () - { + getInnerHeight: function() { if (document.documentElement.clientHeight) return document.documentElement.clientHeight; else return document.body.clientHeight; }, - getInnerWidth: function () - { + getInnerWidth: function() { if (document.documentElement.clientWidth) return document.documentElement.clientWidth; else return document.body.clientWidth; }, - createRadio: function (radioName) - { + createRadio: function(radioName) { var radio; try { - radio = document.createElement (''); - } - catch (e) - { - radio = document.createElement ('input'); + radio = document.createElement(''); + } catch (e) { + radio = document.createElement('input'); radio.type = 'radio'; radio.name = radioName; } @@ -50,30 +42,26 @@ module.exports = return radio; }, - setInputTypeNumber: function (input) - { + setInputTypeNumber: function(input) { input.type = 'text'; } }; -if (!Function.bind) -{ - Function.prototype.bind = function () - { +if (!Function.bind) { + Function.prototype.bind = function() { var bindFunc = this; var bindThis = arguments[0]; var bindArgs = arguments; - var IE_bind = function () - { - var args = new Array (); + var IE_bind = function() { + var args = new Array(); for (var i = 1; i < bindArgs.length; i++) - args.push (bindArgs[i]); + args.push(bindArgs[i]); for (var i = 0; i < arguments.length; i++) - args.push (arguments[i]); + args.push(arguments[i]); - bindFunc.apply (bindThis, args); + bindFunc.apply(bindThis, args); } return IE_bind; @@ -82,33 +70,28 @@ if (!Function.bind) // attachEvent -> addEventListener -if (window.attachEvent && !window.addEventListener) -{ - function IE_addEventListener (signal, func, capture) - { +if (window.attachEvent && !window.addEventListener) { + function IE_addEventListener(signal, func, capture) { var obj = this; - func.IE_eventHandler = function (event) - { + func.IE_eventHandler = function(event) { event.target = event.srcElement; event.layerY = event.clientY; event.layerX = event.clientX; event.pageX = event.offsetX; event.pageY = event.offsetY; - event.stopPropagation = function () - { + event.stopPropagation = function() { this.cancelBubble = true; } - func.call (obj, event); + func.call(obj, event); } - this.attachEvent ('on' + signal, func.IE_eventHandler); + this.attachEvent('on' + signal, func.IE_eventHandler); } - function IE_removeEventListener (signal, func, capture) - { - this.detachEvent ('on' + signal, func.IE_eventHandler); + function IE_removeEventListener(signal, func, capture) { + this.detachEvent('on' + signal, func.IE_eventHandler); } window.addEventListener = IE_addEventListener; @@ -118,9 +101,8 @@ if (window.attachEvent && !window.addEventListener) var IE_createElement = document.createElement; - document.createElement = function (tagName) - { - var node = IE_createElement (tagName); + document.createElement = function(tagName) { + var node = IE_createElement(tagName); node.addEventListener = IE_addEventListener; node.removeEventListener = IE_removeEventListener; return node; @@ -129,11 +111,9 @@ if (window.attachEvent && !window.addEventListener) // ActiveXObject ('Microsoft.XMLHTTP') -> XMLHttpRequest -if (!window.XMLHttpRequest && window.ActiveXObject) -{ - function XMLHttpRequest () - { - return new ActiveXObject ('Microsoft.XMLHTTP'); +if (!window.XMLHttpRequest && window.ActiveXObject) { + function XMLHttpRequest() { + return new ActiveXObject('Microsoft.XMLHTTP'); } } diff --git a/js/vn/iterator-iface.js b/js/vn/iterator-iface.js new file mode 100644 index 00000000..c01e0e8e --- /dev/null +++ b/js/vn/iterator-iface.js @@ -0,0 +1,67 @@ + +var LotIface = require('./lot-iface'); +var ModelIface = require('./model-iface'); + +module.exports = new Class({ + Implements: LotIface + ,Properties: { + /** + * The model associated to this form. + */ + model: { + type: ModelIface + }, + /** + * The row where the form positioned, has -1 if the row is unselected. + */ + row: { + type: Number + }, + /** + * The number of rows in the form. + */ + numRows: { + type: Number + }, + /** + * Checks if the form data is ready. + */ + ready: { + type: Boolean + } + } + + ,insertRow: function() { + if (this._model) + this.row = this._model.insertRow(); + } + + /** + * Removes the current row. + */ + ,deleteRow: function() { + if (this._row >= 0) + this._model.deleteRow(this._row); + } + + /** + * Gets a value from the form. + * + * @param {string} columnName The column name + * @return {Object} The value + */ + ,get: function(columnName) { + return this._model ? + this._model.get(this._row, columnName) : undefined; + } + + /** + * Sets a value on the form. + * + * @param {string} columnName The column name + * @param {Object} value The new value + */ + ,set: function(columnName, value) { + this._model.set(this._row, columnName, value); + } +}); diff --git a/js/vn/iterator.js b/js/vn/iterator.js new file mode 100644 index 00000000..ba6f51aa --- /dev/null +++ b/js/vn/iterator.js @@ -0,0 +1,75 @@ + +var Lot = require('./lot'); +var IteratorIface = require('./iterator-iface'); +var ModelIface = require('./model-iface'); + +/** + * A light iterator for models. It assumes that its row and model properties + * are always valid. + */ +module.exports = new Class({ + Extends: Lot + ,Implements: IteratorIface + ,Properties: { + model: { + type: ModelIface + ,set: function(x) { + this._model = x; + } + ,get: function() { + return this._model; + } + }, + row: { + type: Number + ,set: function(x) { + this._row = x; + this.rowChanged(); + } + ,get: function() { + return this._row; + } + }, + numRows: { + type: Number + ,get: function() { + return this._model ? + this._model.numRows : 0; + } + }, + ready: { + type: Boolean + ,get: function() { + return this._model ? + this._model.ready : false; + } + } + } + + ,_model: null + + ,initialize: function(props) { + Object.assign(this, { + _row: -1 + ,_rowLock: false + }); + this.parent(props); + } + + ,_paramsChanged: function(diff) { + if (!this._rowLock) + for (var key in diff) + this._model.set(this._row, key, diff[key]); + } + + ,rowChanged: function() { + var row; + + if (this._model) + row = this._model.getObject(this._row); + + this._rowLock = true; + this.params = row != null ? row : {}; + this._rowLock = false; + } +}); diff --git a/js/vn/json-connection.js b/js/vn/json-connection.js index 9e012eb8..ca00d381 100644 --- a/js/vn/json-connection.js +++ b/js/vn/json-connection.js @@ -70,7 +70,7 @@ module.exports = new Class({ var storage = remember ? localStorage : sessionStorage; storage.setItem('vnToken', this.token); - this.signalEmit('openned'); + this.emit('openned'); } else this._closeClient(); @@ -93,7 +93,7 @@ module.exports = new Class({ * Called when close operation is done. */ ,_onClose: function(callback, json, error) { - this.signalEmit('closed'); + this.emit('closed'); if (callback) callback(this, null, error); @@ -221,7 +221,7 @@ module.exports = new Class({ this._requestsCount++; if (this._requestsCount === 1) - this.signalEmit('loading-changed', true); + this.emit('loading-changed', true); } ,_onStateChange: function(request, callback) { @@ -231,7 +231,7 @@ module.exports = new Class({ this._requestsCount--; if (this._requestsCount === 0) - this.signalEmit('loading-changed', false); + this.emit('loading-changed', false); var data = null; var error = null; @@ -314,7 +314,7 @@ module.exports = new Class({ if (error.exception == 'SessionExpired') this.clearToken(); - this.signalEmit('error', error); + this.emit('error', error); } } }); diff --git a/js/vn/lot-iface.js b/js/vn/lot-iface.js new file mode 100644 index 00000000..7639a16e --- /dev/null +++ b/js/vn/lot-iface.js @@ -0,0 +1,83 @@ + +/** + * Holds a plain key-value javascript object and monitorizes changes over it. + */ +module.exports = new Class({ + Properties: { + /** + * The internal object with the params, this is the lot internal object + * and should be used for read-only purposes. + */ + params: { + type: Object + } + /** + * Shortcut for params property. + */ + ,$: { + type: Object + } + } + + /** + * Gets a value from the lot. + * + * @param {string} field The field name + * @return {*} The field value + */ + ,get: function(field) { + return this.params[field]; + } + + /** + * Sets a value on the lot. + * + * @param {string} field The field name + * @param {*} value The new field value + */ + ,set: function(field, value) { + var params = {}; + params[field] = value; + this.assign(params); + } + + /** + * Returns an array with the lot keys. + * + * @return {Array} The lot keys + */ + ,keys: function() {} + + /** + * Emits the 'change' signal on the lot. + * + * @param {Object} changes The changed params and its values + */ + ,changed: function(changes) { + this.emit('change', changes); + } + + /** + * Copies all values from another lot. + * + * @param {Object} object The source object + */ + ,assign: function() {} + + /** + * Copies all values from another lot. + * + * @param {LotIface} lot The source lot + */ + ,assignLot: function(lot) { + this.assign(lot.$); + } + + /** + * Resets all values. + */ + ,reset: function() { + this.params = {}; + } + }); + \ No newline at end of file diff --git a/js/vn/lot-query.js b/js/vn/lot-query.js new file mode 100644 index 00000000..3e4ffa39 --- /dev/null +++ b/js/vn/lot-query.js @@ -0,0 +1,122 @@ + +var Lot = require('./lot'); +var LotIface = require('./lot-iface'); +var Spec = require('./spec'); +var Value = require('./value'); + +module.exports = new Class({ + Extends: Lot + ,Tag: 'vn-lot-query' + ,Properties: { + fields: { + type: Array + ,set: function(x) { + this._fields = x; + this._onSourceChange(); + } + ,get: function() { + return this._fields; + } + }, + source: { + type: LotIface + ,set: function(x) { + this.link({_source: x}, {change: this._onSourceChange}); + this._onSourceChange(); + } + ,get: function() { + return this._source; + } + } + } + + ,initialize: function(props) { + Object.assign(this, { + _fields: null, + _source: null, + _lockSource: false, + _specs: {} + }); + this.parent(props); + } + + ,appendChild: function(child) { + if (!(child instanceof Spec)) + throw new Error('VnLotQuery: Child must be a Vn.Spec instance'); + + this._specs[child.name] = child; + this._onSourceChange(); + } + + ,_onSourceChange: function() { + if (this._lockSource) + return; + + var params = this._source ? this._source.params : {}; + var myParams = {}; + + for (var key in this._specs) + myParams[key] = Value.simpleClone(params[key]); + + this.assign(myParams); + } + + ,assign: function(params) { + params = this.transformParams(params); + var diff = Value.partialDiff(this._params, params); + this._assign(diff); + } + + ,setAll: function(params) { + params = this.transformParams(params); + var diff = Value.diff(this._params, params); + this._assign(diff); + } + + ,_assign: function(diff) { + if (diff) { + Object.assign(this._params, diff); + + if (this.source) { + this._lockSource = true; + this.source.assign(diff); + this._lockSource = false; + } + + this._paramsChanged(diff); + this.changed(diff); + } + } + + ,transformParams: function(params) { + var newParams = {}; + + for (var key in this._specs) { + var spec = this._specs[key]; + if (params[key]) + newParams[key] = cast(params[key], spec.type); + } + + return Object.assign(params, newParams); + } +}); + +function cast(value, type) { + switch (type) { + case Boolean: + return (/^(true|1)$/i).test(value); + case Number: + return 0 + new Number(value); + case Date: + var date = new Date(value); + date.setHours(0, 0, 0, 0); + return date; + case String: + return value; + default: + if (type instanceof Object) + return JSON.parse(value); + else + return value; + } +} diff --git a/js/vn/lot.js b/js/vn/lot.js new file mode 100644 index 00000000..8d9cd5a4 --- /dev/null +++ b/js/vn/lot.js @@ -0,0 +1,65 @@ + +var VnObject = require('./object'); +var LotIface = require('./lot-iface'); +var Value = require('./value'); + +module.exports = new Class({ + Extends: VnObject + ,Implements: LotIface + ,Tag: 'vn-lot' + ,Properties: { + params: { + type: Object + ,set: function(x) { + this.setAll(x); + } + ,get: function() { + return this._params; + } + } + ,$: { + type: Object + ,set: function(x) { + this.setAll(x); + } + ,get: function() { + return this._params; + } + } + } + + ,initialize: function(props) { + this._params = {}; + this.parent(props); + } + + ,keys: function() { + return Object.keys(this._params); + } + + ,assign: function(params) { + var diff = Value.partialDiff(this._params, params); + this._assign(diff); + } + + ,setAll: function(params) { + var diff = Value.diff(this._params, params); + this._assign(diff); + } + + ,_assign: function(diff) { + if (diff) { + Object.assign(this._params, diff); + this._paramsChanged(diff); + this.changed(diff); + } + } + + /** + * Called when lot params changes, can be implemented by child classes to be + * notified about changes. + * + * @param {Object} diff Changed parameters and its new values + */ + ,_paramsChanged: function() {} +}); diff --git a/js/vn/model-iface.js b/js/vn/model-iface.js new file mode 100644 index 00000000..7b4abdb9 --- /dev/null +++ b/js/vn/model-iface.js @@ -0,0 +1,127 @@ + +/** + * Readable data model. + */ + module.exports = new Class({ + Properties: { + /** + * The number of rows in the model. + */ + numRows: { + type: Number + }, + /** + * The current status of the model. + */ + status: { + type: Number + }, + /** + * Checks if the model data is ready. + */ + ready: { + type: Boolean + } + } + + /** + * Checks if the column exists. + * + * @param {integer} column The column index + * @return {boolean} %true if column exists, %false otherwise + */ + ,checkColExists: function() {} + + /** + * Checks if the row exists. + * + * @param {integer} rowIndex The row index + * @return {boolean} %true if row exists, %false otherwise + */ + ,checkRowExists: function() {} + + /** + * Get the index of the column from its name. + * + * @param {string} columnName The column name + * @return {number} The column index or -1 if column not exists + */ + ,getColumnIndex: function() {} + + /** + * Gets a value from the model. + * + * @param {number} rowIndex The row index + * @param {string} columnName The column name + * @return {*} The value, or %undefined + */ + ,get: function() {} + + /** + * Gets a value using the column index. + * + * @param {number} rowIndex The row index + * @param {number} column The column index + * @return {*} The value + */ + ,getByIndex: function() {} + + /** + * Gets a row as an object using the column index. + * + * @param {number} rowIndex The row index + * @return {Object} The row as an object + */ + ,getObject: function() {} + + /** + * Orders the model by the specified column name. + * + * @param {number} column The column name + * @param {SortWay} way The sort way + */ + ,sortByName: function() {} + + /** + * Orders the model by the specified column. + * + * @param {number} column The column index + * @param {SortWay} way The sort way + */ + ,sort: function() {} + + /** + * Searchs a value on the model and returns the row index of the first + * ocurrence. + * If an index have been built on that column, it will be used, for more + * information see the indexColumn() method. + * + * @param {string} column The column name + * @param {Object} value The value to search + * @return {number} The column index + */ + ,search: function() {} + + /** + * Searchs a value on the model and returns the row index of the first + * ocurrence. + * + * @param {number} col The column index + * @param {Object} value The value to search + * @return {number} The column index + */ + ,searchByIndex: function() {} + + /** + * Builds an internal hash index for the specified column, this speeds + * significantly searches on that column, specially when model has a lot of + * rows. + * + * FIXME: Not fully implemented. + * + * @param {string} column The column name + */ + ,indexColumn: function() {} + }); + + \ No newline at end of file diff --git a/js/vn/model-proxy.js b/js/vn/model-proxy.js new file mode 100644 index 00000000..04fabcd0 --- /dev/null +++ b/js/vn/model-proxy.js @@ -0,0 +1,80 @@ + +var ModelIface = require('./object'); + +/** + * Monitorizes all changes made to a model. + */ +var Klass = new Class(); +module.exports = Klass; + +var Mode = { + ON_CHANGE : 1 + ,ON_DEMAND : 2 +}; + +var Operation = { + INSERT : 1 << 1 + ,UPDATE : 1 << 2 + ,DELETE : 1 << 3 +}; + +Klass.extend({ + Mode: Mode + ,Operation: Operation +}); + +Klass.implement({ + Implements: ModelIface + ,Properties: { + /** + * Update mode. + */ + mode: { + enumType: Mode + ,value: Mode.ON_CHANGE + } + } + + /** + * Updates a value on the model. + * + * @param {number} rowIndex The row index + * @param {string} columnName The column name + * @param {*} value The new value + */ + ,set: function() {} + + /** + * Updates a value on the model using the column index. + * + * @param {number} rowIndex The row index + * @param {number} col The column index + * @param {*} value The new value + */ + ,setByIndex: function() {} + + /** + * Deletes a row from the model. + * + * @param {number} rowIndex The row index + */ + ,deleteRow: function() {} + + /** + * Inserts a new row on the model. + * + * @return The index of the inserted row + */ + ,insertRow: function() {} + + /** + * Cleans the model data. + */ + ,clean: function() {} + + /** + * Performs all model changes on the database. + */ + ,performOperations: function() {} +}); + diff --git a/js/vn/model.js b/js/vn/model.js new file mode 100644 index 00000000..d33ead43 --- /dev/null +++ b/js/vn/model.js @@ -0,0 +1,350 @@ + +var VnObject = require('./object'); +var simpleEquals = require('./value').simpleEquals; + +var Klass = new Class(); +module.exports = Klass; + +var Status = { + CLEAN : 1 + ,LOADING : 2 + ,READY : 3 + ,ERROR : 4 +}; + +var SortWay = { + ASC : 1 + ,DESC : 2 +}; + +Klass.extend({ + Status + ,SortWay +}); + +Klass.implement({ + Extends: VnObject + ,Tag: 'vn-model' + ,Properties: { + /** + * The model internal data. + */ + data: { + type: Array + ,get: function() { + return this._data; + } + ,set: function(x) { + this._setData(x); + } + }, + /** + * The number of rows in the model. + */ + numRows: { + type: Number + ,get: function() { + if (this._data) + return this._data.length; + + return 0; + } + }, + /** + * The current status of the model. + */ + status: { + type: Number + ,get: function() { + return this._status; + } + }, + /** + * Checks if the model data is ready. + */ + ready: { + type: Boolean + ,get: function() { + return this._status === Status.READY; + } + } + } + + ,_data: null + ,_status: Status.CLEAN + + ,_requestedSortName: null + ,_sortColumn: null + ,_sortWay: null + + ,_requestedIndexes: {} + ,_indexes: [] + + //+++++++++++++++++++++++++++++ Data hadling + + ,_setData: function(data) { + this._data = data; + this._refreshRowIndexes(0); + + if (this._requestedSortName != null) + this._realSort(this._requestedSortName, this._sortWay); + for (column in this._requestedIndexes) + this._buildIndex(column); + + this._setStatus(Status.READY); + } + + /** + * Checks if the row exists. + * + * @param {Integer} rowIndex The row index + * @return {Boolean} %true if row exists, %false otherwise + */ + ,checkRowExists: function(rowIndex) { + return this._data != null + && rowIndex >= 0 + && rowIndex < this._data.length; + } + + /** + * Gets a value from the model. + * + * @param {Number} rowIndex The row index + * @param {Number} columnName The column name + * @return {*} The value + */ + ,get: function(rowIndex, columnName) { + if (!this.checkRowExists(rowIndex)) + return undefined; + + return this._data[rowIndex][columnName]; + } + + /** + * Updates a value on the model. + * + * @param {Number} rowIndex The row index + * @param {Number} columnName The column name + * @param {*} value The new value + */ + ,set: function(rowIndex, columnName, value) { + if (!this.checkRowExists(rowIndex)) + return; + + this.emit('row-updated-before', rowIndex); + this._data[rowIndex][columnName] = value; + this.emit('row-updated', rowIndex, [columnName]); + } + + /** + * Gets a row as an object using the column index. + * + * @param {Number} rowIndex The row index + * @return {Object} The row as an object + */ + ,getObject: function(rowIndex) { + return this.checkRowExists(rowIndex) ? + this._data[rowIndex] : null; + } + + ,_setStatus: function(status) { + this._status = status; + this.emit('status-changed', status); + this.emit('status-changed-after', status); + } + + /** + * Inserts a new row on the model. + * + * @return The index of the inserted row + */ + ,insertRow: function(newRow) { + var rowIndex = this._data.push(newRow) - 1; + newRow.index = rowIndex; + this.emit('row-inserted', rowIndex); + return rowIndex; + } + + /** + * Deletes a row from the model. + * + * @param {Number} rowIndex The row index + */ + ,deleteRow: function(rowIndex) { + if (!this.checkRowExists(rowIndex)) + return; + + this.emit('row-deleted-before', rowIndex); + this._data.splice(rowIndex, 1); + this.emit('row-deleted', rowIndex); + this._refreshRowIndexes(rowIndex); + } + + //+++++++++++++++++++++++++++++ Sorting + + /** + * Orders the model by the specified column name. + * + * @param {String} columnName The column name + * @param {SortWay} way The sort way + */ + ,sort: function(columnName, way) { + this._requestedSortName = columnName; + this._sort(columnName, way); + } + + ,_sort: function(columnName, way) { + var status = this._status; + this._setStatus(Status.LOADING); + this._realSort(columnName, way); + this._setStatus(status); + } + + ,_realSort: function(columnName, way) { + if (!this._data) + return; + + if (columnName !== this._sortColumn) { + if (way === SortWay.DESC) + var sortFunction = this.sortFunctionDesc; + else + var sortFunction = this.sortFunctionAsc; + + this._data.sort(sortFunction.bind(this, columnName)); + } else if (way !== this._sortWay) + this._data.reverse(); + + this._sortColumn = columnName; + this._sortWay = way; + + this._refreshRowIndexes(0); + } + + ,_refreshRowIndexes: function(start) { + var data = this._data; + + for (var i = start; i < data.length; i++) + data[i].index = i; + } + + /* + * Function used to sort the model ascending. + */ + ,sortFunctionAsc: function(column, a, b) { + if (a[column] < b[column]) + return -1; + else if (a[column] > b[column]) + return 1; + + return 0; + } + + /* + * Function used to sort the model descending. + */ + ,sortFunctionDesc: function(column, a, b) { + if (a[column] > b[column]) + return -1; + else if (a[column] < b[column]) + return 1; + + return 0; + } + + //+++++++++++++++++++++++++++++ Searching + + /** + * Builds an internal hash index for the specified column, this speeds + * significantly searches on that column, specially when model has a lot of + * rows. + * + * FIXME: Not fully implemented. + * + * @param {String} column The column name + */ + ,indexColumn: function(column) { + this._requestedIndexes[column] = true; + + if (this._status === Status.READY) + this._buildIndex(column); + } + + ,getHashValue: function(value) { + if (value instanceof Date) + return value.getTime(); + else + return value; + } + + ,_buildIndex: function(columnName) { + if (this.columnMap[columnName] === undefined) + return; + + var data = this._data; + var values = {}; + var nulls = []; + + for (var i = 0; i < data.length; i++) { + var value = data[i][columnName]; + + if (value == null) { + nulls.push(data[i]); + continue; + } + + index[this.getHashValue(value)] = data[i]; + } + + this._indexes[columnName] = { + values: values, + index: index + }; + } + + /** + * Searchs a value on the model and returns the row index of the first + * ocurrence. + * + * @param {Number} columnName The column name + * @param {Object} value The value to search + * @return {Number} The column index + */ + ,search: function(columnName, value) { + var data = this._data; + + if (data == null) + return -1; + + // Searchs the value using an internal index. + + var index = this._indexes[columnName]; + + if (index) { + if (value == null) { + if (index.nulls[0] !== undefined) + return index.nulls[0].index; + } else { + var row = index.values[this.getHashValue(value)]; + + if (rowIndex !== undefined) + return row.index; + } + + return -1; + } + + // Searchs the value using a loop. + + for (var i = 0; i < data.length; i++) + if (simpleEquals(data[i][columnName], value)) + return i; + + return -1; + } + + //+++++++++++++++++++++++++++++ Virtual + + ,refresh: function() { + this._setStatus(Status.READY); + } +}); diff --git a/js/vn/mutators.js b/js/vn/mutators.js index 6ee6576e..11951890 100644 --- a/js/vn/mutators.js +++ b/js/vn/mutators.js @@ -5,24 +5,20 @@ var Mutators = Class.Mutators; var _Extends = Mutators.Extends; -Mutators.Extends = function () -{ - _Extends.apply (this, arguments); - this.implement ({Properties: {}}); +Mutators.Extends = function() { + _Extends.apply(this, arguments); + this.implement({Properties: {}}); } -Mutators.Tag = function (tagName) -{ +Mutators.Tag = function(tagName) { vnCustomTags[tagName] = this; - this.extend ({Tag: tagName}); + this.extend({Tag: tagName}); }; -Mutators.Properties = function (props) -{ +Mutators.Properties = function(props) { var parentProps; - for (var propName in props) - { + for (var propName in props) { var prop = props[propName]; prop.configurable = true; @@ -35,17 +31,15 @@ Mutators.Properties = function (props) if (!props[propName]) props[propName] = parentProps[propName]; - this.extend ({Properties: props}); - Object.defineProperties (this.prototype, props); + this.extend({Properties: props}); + Object.defineProperties(this.prototype, props); }; -Mutators.Parent = function (propName) -{ - this.extend ({Parent: propName}); +Mutators.Parent = function(propName) { + this.extend({Parent: propName}); }; -Mutators.Child = function (propName) -{ - this.extend ({Child: propName}); +Mutators.Child = function(propName) { + this.extend({Child: propName}); }; diff --git a/js/vn/object.js b/js/vn/object.js index c607e995..a4474687 100644 --- a/js/vn/object.js +++ b/js/vn/object.js @@ -1,53 +1,90 @@ /** - * The main base class. Manages the signal system. - * - * @param signals Map with all connected signal handlers + * The main base class. Manages the signal system. Objects based on this class + * can be instantiated declaratively using XML. */ module.exports = new Class({ + /** + * Tag to be used when the class instance is defined via XML. All classes + * must define this attribute, even if it is not used. + */ Tag: 'vn-object' + + /** + * Class public properties. + */ ,Properties: {} + /* + * Reference count. + */ ,_refCount: 1 - ,_signalData: null + /* + * Signal handlers data. + */ + ,_thisArg: null + + /** + * Initializes the object and sets all properties passed to the class + * constructor. + * + * @param {Object} props The properties passed to the contructor + */ ,initialize: function(props) { this.setProperties(props); } + /** + * Sets a group of object properties. + * + * @param {Object} props Properties + */ ,setProperties: function(props) { for (var prop in props) this[prop] = props[prop]; } + /** + * Increases the object reference count. + */ ,ref: function() { this._refCount++; return this; } + /** + * Decreases the object reference count. + */ ,unref: function() { this._refCount--; if (this._refCount === 0) this._destroy(); } - - ,loadXml: function(builder, node) {} - ,appendChild: function(child) {} - - ,_signalInit: function() { - if (!this._signalData) - this._signalData = { - signals: {}, - links: {} - }; - } + + /** + * Called from @Vn.Builder when it finds a custom tag as a child of the + * element. + * + * @param {Vn.Scope} scope The scope instance + * @param {Node} node The custom tag child nodes + */ + ,loadXml: function() {} + + /** + * Called from @Vn.Builder when it finds a a child tag that isn't + * associated to any property. + * + * @param {Object} child The child object instance + */ + ,appendChild: function() {} /** * Conects a signal with a function. * - * @param {String} id The signal identifier - * @param {Function} callback The callback + * @param {string} id The signal identifier + * @param {function} callback The callback * @param {Object} instance The instance */ ,on: function(id, callback, instance) { @@ -57,13 +94,13 @@ module.exports = new Class({ } this._signalInit(); - var callbacks = this._signalData.signals[id]; + var callbacks = this._thisArg.signals[id]; if (!callbacks) - callbacks = this._signalData.signals[id] = []; + callbacks = this._thisArg.signals[id] = []; callbacks.push({ - blocked: false + blocked: false ,callback: callback ,instance: instance }); @@ -72,15 +109,15 @@ module.exports = new Class({ /** * Locks/Unlocks a signal emission to the specified object. * - * @param {String} id The signal identifier - * @param {Function} callback The callback - * @param {Boolean} block %true for lock the signal, %false for unlock + * @param {string} id The signal identifier + * @param {function} callback The callback + * @param {boolean} block %true for lock the signal, %false for unlock */ ,blockSignal: function(id, callback, block, instance) { - if (!this._signalData) + if (!this._thisArg) return; - var callbacks = this._signalData.signals[id]; + var callbacks = this._thisArg.signals[id]; if (!callbacks) return; @@ -92,15 +129,15 @@ module.exports = new Class({ } /** - * Emits a signal in the current object. + * Emits a signal in the object. * - * @param {String} id The signal identifier + * @param {string} id The signal identifier */ - ,signalEmit: function(id) { - if (!this._signalData) + ,emit: function(id) { + if (!this._thisArg) return; - var callbacks = this._signalData.signals[id]; + var callbacks = this._thisArg.signals[id]; if (!callbacks) return; @@ -119,23 +156,21 @@ module.exports = new Class({ /** * Disconnects a signal from current object. * - * @param {String} id The signal identifier - * @param {Function} callback The connected callback + * @param {string} id The signal identifier + * @param {function} callback The connected callback * @param {Object} instance The instance */ ,disconnect: function(id, callback, instance) { - if (!this._signalData) + if (!this._thisArg) return; - var callbacks = this._signalData.signals[id]; + var callbacks = this._thisArg.signals[id]; - if (!callbacks) - return; - - for (var i = 0; i < callbacks.length; i++) - if (callbacks[i].callback == callback - && callbacks[i].instance == instance) - callbacks.splice(i--, 1); + if (callbacks) + for (var i = callbacks.length; i--;) + if (callbacks[i].callback === callback + && callbacks[i].instance === instance) + callbacks.splice(i, 1); } /** @@ -144,48 +179,54 @@ module.exports = new Class({ * @param {Object} instance The instance */ ,disconnectByInstance: function(instance) { - if (!this._signalData) + if (!this._thisArg) return; - var signals = this._signalData.signals; + var signals = this._thisArg.signals; for (var signalId in signals) { var callbacks = signals[signalId]; - for (var i = 0; i < callbacks.length; i++) - if (callbacks[i].instance == instance) - callbacks.splice(i--, 1); + if (callbacks) + for (var i = callbacks.length; i--;) + if (callbacks[i].instance === instance) + callbacks.splice(i, 1); } } /** * Destroys the object, this method should only be called before losing - * the last reference to the object. + * the last reference to the object. It can be overwritten by child classes + * but should always call the parent method. */ ,_destroy: function() { - if (!this._signalData) + if (!this._thisArg) return; - var links = this._signalData.links; + var links = this._thisArg.links; for (var key in links) - links[key].disconnectByInstance(this); + this._unlink(links[key]); - this._signalData = null; + this._thisArg = null; } + /** + * Links the object with another object. + * + * @param {Object} prop The linked property + * @param {Object} handlers The object events to listen with + */ ,link: function(prop, handlers) { this._signalInit(); - var links = this._signalData.links; + var links = this._thisArg.links; for (var key in prop) { var newObject = prop[key]; var oldObject = this[key]; - if (oldObject) { - oldObject.disconnectByInstance(this); - oldObject.unref(); - } + if (oldObject) + this._unlink(oldObject); this[key] = newObject; @@ -195,8 +236,20 @@ module.exports = new Class({ for (var signal in handlers) newObject.on(signal, handlers[signal], this); } else if (oldObject) - delete links[key]; + links[key] = undefined; } } -}); + ,_unlink: function(object) { + object.disconnectByInstance(this); + object.unref(); + } + + ,_signalInit: function() { + if (!this._thisArg) + this._thisArg = { + signals: {}, + links: {} + }; + } +}); \ No newline at end of file diff --git a/js/vn/param-iface.js b/js/vn/param-iface.js new file mode 100644 index 00000000..792de330 --- /dev/null +++ b/js/vn/param-iface.js @@ -0,0 +1,136 @@ + +var LotIface = require('./lot-iface'); +var Type = require('./type'); +var Value = require('./value'); + +/** + * A value holder, it emits the changed signal when value is changed. + * Also it can be linked with a lot value or another parameter. + */ +module.exports = new Class({ + Properties: { + /** + * The parameter value. + */ + value: { + type: null + }, + /** + * The parameter type. + */ + type: { + type: Type + }, + /** + * Another parameter to bind with. + */ + param: { + type: Object + }, + /** + * A lot to bind with. + */ + lot: { + type: LotIface + }, + /** + * The field name in the lot. + */ + name: { + type: String + }, + /** + * Determines whether the link to the lot is unidirectional, ie, a + * change in the lot updates the parameter but not viceversa. + */ + oneWay: { + type: Boolean + } + } + + ,_value: undefined + ,_type: null + ,_param: null + ,_paramLock: false + ,_lot: null + ,_name: null + ,_lotLock: false + ,_oneWay: false + + ,_setValue: function(newValue) { + if (this._putValue(newValue)) + this._notifyChanges(); + } + + ,_putValue: function(newValue) { + if (Value.simpleEquals(newValue, this._value)) + return false; + + this._value = Value.simpleClone(newValue); + return true; + } + + ,_notifyChanges: function() { + this._refreshLot(); + this._refreshParam(); + this.emit('changed', this._value); + } + + ,_setType: function(type) { + this._type = type; + this._onLotChange(); + } + + ,_setParam: function(param) { + this.link({_param: param}, {changed: this._onParamChange}); + this._refreshParam(); + } + + ,_onParamChange: function() { + if (this._paramLock || !this._param) + return; + + this._paramLock = true; + this._setValue(this._param.value); + this._paramLock = false; + } + + ,_refreshParam: function() { + if (this._paramLock || !this._param) + return; + + this._paramLock = true; + this._param.value = this._value; + this._paramLock = false; + } + + ,_setLot: function(lot) { + this.link({_lot: lot}, {change: this._onLotChange}); + this._onLotChange(); + } + + ,_onLotChange: function() { + if (this._lotLock || !this._name || !this._lot) + return; + + var newValue = this._lot.get(this._name, this._type); + + this._lotLock = true; + this._setValue(newValue); + this._lotLock = false; + } + + ,_refreshLot: function() { + if (this._lotLock || !this._name || !this._lot || this._oneWay) + return; + + this._lotLock = true; + this._lot.set(this._name, this._value); + this._lotLock = false; + } + + ,_setName: function(name) { + this._name = name; + this._onLotChange(); + } +}); diff --git a/js/vn/param.js b/js/vn/param.js index 7001198c..1962e427 100644 --- a/js/vn/param.js +++ b/js/vn/param.js @@ -1,71 +1,79 @@ -var Object = require ('./object'); -var Param = require ('./param'); -var Value = require ('./value'); +var VnObject = require('./object'); +var Type = require('./type'); +var ParamIface = require('./param-iface'); +var LotIface = require('./lot-iface'); /** - * Simply a linkable value holder. + * A simple implementation of @ParamIface. */ -module.exports = new Class -({ - Extends: Object +module.exports = new Class({ + Extends: VnObject + ,Implements: ParamIface ,Tag: 'vn-param' - ,Properties: - { - value: - { - type: String - ,set: function (x) - { - if (Value.compare (x, this._value)) - return; - - if (x instanceof Date) - x = x.clone (); - - this._value = x; - - if (this._master && !this.masterLock) - { - this.masterLock = true; - this._master.value = x; - this.masterLock = false; - } - - this.signalEmit ('changed', this._value); + ,Properties: { + value: { + type: null + ,set: function(x) { + this._setValue(x); } - ,get: function () - { + ,get: function() { return this._value; } }, - master: - { - type: Param - ,set: function (x) - { - this.link ({_master: x}, {'changed': this._onMasterChange}); - this._onMasterChange (); + type: { + type: Type + ,set: function(x) { + this._setType(x); } - ,get: function () - { - return this._master; + ,get: function() { + return this._type; + } + }, + param: { + type: ParamIface + ,set: function(x) { + this._setParam(x); + } + ,get: function() { + return this._param; + } + }, + master: { + type: ParamIface + ,set: function(x) { + this._setParam(x); + } + ,get: function() { + return this._param; + } + }, + lot: { + type: LotIface + ,set: function(x) { + this._setLot(x); + } + ,get: function() { + return this._lot; + } + }, + name: { + type: String + ,set: function(x) { + this._setName(x); + } + ,get: function() { + return this._name; + } + }, + oneWay: { + type: Boolean + ,set: function(x) { + this._oneWay = x; + } + ,get: function() { + return this._oneWay; } } } - - ,_value: undefined - ,_master: null - ,masterLock: false - - ,_onMasterChange: function () - { - if (this.masterLock) - return; - - this.masterLock = true; - this.value = this._master.value; - this.masterLock = false; - } }); - diff --git a/js/vn/spec.js b/js/vn/spec.js new file mode 100644 index 00000000..f24bc6da --- /dev/null +++ b/js/vn/spec.js @@ -0,0 +1,37 @@ + +var VnObject = require('./object'); +var Type = require('./type'); + +/** + * Paramter specification. + */ +module.exports = new Class({ + Extends: VnObject + ,Tag: 'vn-spec' + ,Properties: { + /** + * The parameter name. + */ + name: { + type: String + ,set: function(x) { + this._name = x; + } + ,get: function() { + return this._name; + } + }, + /** + * The parameter type. + */ + type: { + type: Type + ,set: function(x) { + this._type = x; + } + ,get: function() { + return this._type; + } + } + } +}); diff --git a/js/vn/type.js b/js/vn/type.js new file mode 100644 index 00000000..b380e90a --- /dev/null +++ b/js/vn/type.js @@ -0,0 +1,4 @@ +/** + * Type that references another type. + */ + module.exports = function() {}; \ No newline at end of file diff --git a/js/vn/value.js b/js/vn/value.js index 50dcb287..b45bf17f 100644 --- a/js/vn/value.js +++ b/js/vn/value.js @@ -1,65 +1,191 @@ -var VnDate = require ('./date'); +var VnDate = require('./date'); -module.exports = -{ - regexpNumber: /%\.([0-9]+)d/g - ,regexpString: /%s/g +/** + * Clones a simple value. A simple value is any value that is not an array, + * object or function. If non-simple value is passed, the same object reference + * is returned. + * + * @param {*} value The value to be copied + * @return {*} The value copy + */ +function simpleClone(value) { + if (value instanceof Date) + return value.clone(); - ,compare: function (a, b) - { - if (a === b) - return true; - if (typeof a === typeof b && a instanceof Date) - return a.getTime () === b.getTime (); - - return false; - } - - ,format: function (value, format) - { - if (value === null || value === undefined) - return ''; - - if (format) - switch (typeof value) - { - case 'number': - return format.replace (this.regexpNumber, - this.replaceNumber.bind (null, value)); - case 'string': - return format.replace (this.regexpString, - this.replaceString.bind (null, value)); - case 'object': - if (value instanceof Date) - return VnDate.strftime (value, format); - } - - return value; - } - - ,replaceNumber: function (value, token, digits) - { - return new Number (value).toFixed (parseInt (digits)); - } - - ,replaceString: function (value) - { - return value; - } -}; + return value; +} -window.sprintf = function (formatString) -{ +/** + * Checks if two simple values are equal using the strict equality operator. For + * information about simple values see simpleClone() function. + * + * @param {*} a Value to compare to + * @param {*} b Value to compare with + * @return {boolean} %true if they are equal, %false otherwise + */ +function simpleEquals(a, b) { + if (a === b) + return true; + if (a instanceof Date && b instanceof Date) + return a.getTime() === b.getTime(); + + return false; +} + +/** + * Calculates differences between two simple key-value objects. + * + * @param {Object} orgObject Value to compare to + * @param {Object} newObject Value to compare with + * @return {Object} The differences or %null if there are no differences + */ +function diff(orgObject, newObject) { + var diff = {}; + + for (var key in orgObject) + if (!simpleEquals(orgObject[key], newObject[key])) + diff[key] = simpleClone(newObject[key]); + + for (var key in newObject) + if (orgObject[key] === undefined && newObject[key] !== undefined) + diff[key] = simpleClone(newObject[key]); + + if (Object.keys(diff).length > 0) + return diff; + + return null; +} + +/** + * Calculates new differences between two simple key-value objects. + * + * @param {Object} orgObject Value to compare to + * @param {Object} newObject Value to compare with + * @return {Object} The differences or %null if there are no differences + */ +function partialDiff(orgObject, newObject) { + var diff = {}; + + for (var key in newObject) + if (!simpleEquals(orgObject[key], newObject[key])) + diff[key] = simpleClone(newObject[key]); + + if (Object.keys(diff).length > 0) + return diff; + + return null; +} + +/** + * Clones a simple key-value object in wich properties are simple values. For + * information about simple values see simpleClone() function. + * + * @param {*} object The object to be cloned + * @return The cloned object + */ +function kvClone(object) { + var copy = {}; + + for (var key in object) + copy[key] = simpleClone(object[key]); + + return copy; +} + +/** + * Checks if two values are equal, it also checks objects. Basic values are + * compared using the strict equality operator. + * + * @param {*} a Value to compare to + * @param {*} b Value to compare with + * @return {boolean} %true if they are equal, %false otherwise + */ +function equals(a, b) { + if (a === b) + return true; + if (a instanceof Date && b instanceof Date) + return a.getTime() === b.getTime(); + if (a && b && (typeof a === 'object') && (typeof b === 'object')) { + for (var key in a) + if (!equals(a[key], b[key])) + return false; + + for (var key in b) + if (a[key] === undefined && b[key] !== undefined) + return false; + + return true; + } + + return false; +} + +/** + * Returns a formated string. + * + * @param {Object} formatString The base string template + * @param {...} arguments Format parameters + * @return {string} The formated string + */ +function sprintf(formatString) { var args = arguments; if (args.length <= 1) return formatString; var i = 1; - return formatString.replace (/%[s|d]/g, function () - { + return formatString.replace(/%[s|d]/g, function() { return args[i++]; }); } +module.exports = { + regexpNumber: /%\.([0-9]+)d/g + ,regexpString: /%s/g + + ,equals + ,diff + ,partialDiff + ,kvClone + ,simpleClone + ,simpleEquals + ,sprintf + + ,compare: function(a, b) { + if (a === b) + return true; + if (a instanceof Date && b instanceof Date) + return a.getTime() === b.getTime(); + + return false; + } + + ,format: function(value, format) { + if (value === null || value === undefined) + return ''; + + if (format) + switch (typeof value) { + case 'number': + return format.replace(this.regexpNumber, + this.replaceNumber.bind(null, value)); + case 'string': + return format.replace(this.regexpString, + this.replaceString.bind(null, value)); + case 'object': + if (value instanceof Date) + return VnDate.strftime(value, format); + } + + return value; + } + + ,replaceNumber: function(value, token, digits) { + return new Number(value).toFixed(parseInt(digits)); + } + + ,replaceString: function(value) { + return value; + } +}; diff --git a/js/vn/vn.js b/js/vn/vn.js index d6536319..feb249d5 100644 --- a/js/vn/vn.js +++ b/js/vn/vn.js @@ -3,18 +3,28 @@ require('mootools'); Vn = module.exports = { Locale : require('./locale') - ,Enum : function() {} + ,Enum : require('./enum') + ,Type : require('./type') ,Object : require('./object') + ,Mutators : require('./mutators') ,Browser : require('./browser') ,Cookie : require('./cookie') ,Date : require('./date') ,Value : require('./value') ,Url : require('./url') - ,Mutators : require('./mutators') - ,Param : require('./param') - ,HashListener : require('./hash-listener') + ,LotIface : require('./lot-iface') + ,Lot : require('./lot') + ,LotQuery : require('./lot-query') ,Hash : require('./hash') - ,HashParam : require('./hash-param') + ,ParamIface : require('./param-iface') + ,Param : require('./param') + ,Spec : require('./spec') + ,Model : require('./model') + ,ModelIface : require('./model-iface') + ,ModelProxy : require('./model-proxy') + ,IteratorIface : require('./iterator-iface') + ,Iterator : require('./iterator') + ,Form : require('./form') ,Node : require('./node') ,NodeBuilder : require('./node-builder') ,Builder : require('./builder') @@ -374,4 +384,3 @@ Vn = module.exports = { return this.isMobileCached; } }; - diff --git a/reports/delivery-note/ui.xml b/reports/delivery-note/ui.xml index 23f95654..eb7d3db5 100644 --- a/reports/delivery-note/ui.xml +++ b/reports/delivery-note/ui.xml @@ -1,7 +1,7 @@ - + CALL myTicket_get(#ticket) @@ -47,7 +47,7 @@
- + CALL myTicket_getRows(#ticket) @@ -64,7 +64,7 @@ property="model" id="services" conn="conn" - batch="batch" + lot="lot" on-status-changed="onServicesChanged"> CALL myTicket_getServices(#ticket) @@ -77,7 +77,7 @@ CALL myTicket_getPackages(#ticket) diff --git a/reports/items-report/items-report.js b/reports/items-report/items-report.js index 76963065..cae507db 100644 --- a/reports/items-report/items-report.js +++ b/reports/items-report/items-report.js @@ -1,5 +1,4 @@ -Hedera.ItemsReport = new Class -({ +Hedera.ItemsReport = new Class({ Extends: Hedera.Report }); diff --git a/reports/items-report/ui.xml b/reports/items-report/ui.xml index 5eb2f1a9..ac2d2383 100644 --- a/reports/items-report/ui.xml +++ b/reports/items-report/ui.xml @@ -6,7 +6,7 @@ CALL item_getList(#warehouse, CURDATE(), #realm, #rate) diff --git a/reports/shelves-report/shelves-report.js b/reports/shelves-report/shelves-report.js index 64068259..e681ef81 100644 --- a/reports/shelves-report/shelves-report.js +++ b/reports/shelves-report/shelves-report.js @@ -7,20 +7,15 @@ Hedera.ShelvesReport = new Class({ ,trayThickness: 2 ,trayMargin: 5 - ,open: function(batch) { - this.batch = batch; - this.title = batch.getValue('reportTitle'); - this.maxAmount = batch.getValue('maxAmount'); - this.showPacking = batch.getValue('showPacking'); - this.stack = batch.getValue('stack'); - this.useIds = batch.getValue('useIds'); + ,open: function(lot) { + this.lot = lot; var query = 'SELECT id, name, nTrays, topTrayHeight, trayHeight, width, depth '+ 'FROM shelf WHERE id = #shelf; '+ 'CALL item_listAllocation(#warehouse, #date, #family, #namePrefix, #useIds)'; - this.conn.execQuery(query, this.onQueryExec.bind(this), this.batch); + this.conn.execQuery(query, this.onQueryExec.bind(this), lot.$); } ,onQueryExec: function(resultSet) {