Builder refactor, various fixes
gitea/hedera-web/pipeline/head This commit looks good Details

This commit is contained in:
Juan Ferrer 2022-06-18 23:04:34 +02:00
parent d1819118d8
commit fd6f39371a
31 changed files with 745 additions and 763 deletions

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
hedera-web (1.407.79) stable; urgency=low
hedera-web (1.407.80) stable; urgency=low
* Initial Release.

View File

@ -60,7 +60,7 @@
<htk-button
icon="delete"
tip="_RemoveAddress"
on-click="this.onRemoveAddressClick($.address)"/>
on-click="this.onRemoveAddressClick($iter)"/>
<htk-button
icon="edit"
tip="_EditAddress"

View File

@ -30,7 +30,7 @@
<htk-bar-button
icon="check"
tip="_Accept"
on-click="$.iter.performOperations()"/>
on-click="iter.performOperations()"/>
</div>
<div id="form" class="address">
<div class="form box vn-w-sm vn-pa-lg">

View File

@ -1,22 +1,18 @@
<vn>
<vn-group>
<db-form id="password-form">
<db-form v-model="passwordForm">
<db-model property="model">
<custom>
SELECT length, nAlpha, nUpper, nDigits, nPunct
FROM account.userPassword
</custom>
SELECT length, nAlpha, nUpper, nDigits, nPunct
FROM account.userPassword
</db-model>
</db-form>
<db-form id="user-form">
<db-model property="model" id="user-model" updatable="true">
<custom>
SELECT u.id, u.name, u.email, u.nickname,
u.lang, c.isToBeMailed, c.id clientFk
FROM account.myUser u
LEFT JOIN myClient c
ON u.id = c.id
</custom>
SELECT u.id, u.name, u.email, u.nickname,
u.lang, c.isToBeMailed, c.id clientFk
FROM account.myUser u
LEFT JOIN myClient c
ON u.id = c.id
</db-model>
</db-form>
</vn-group>
@ -27,7 +23,7 @@
<htk-bar-button
icon="place"
tip="_Addresses"
on-click="$.hash.setAll({form: 'account/address-list'})"/>
on-click="hash.setAll({form: 'account/address-list'})"/>
<htk-bar-button
icon="lock_reset"
tip="_Change password"
@ -119,24 +115,19 @@
</h5>
<ul>
<li>
<htk-text form="password-form" column="length"/>
<t>characters long</t>
{{passwordForm.length}} <t>characters long</t>
</li>
<li>
<htk-text form="password-form" column="nAlpha"/>
<t>alphabetic characters</t>
{{passwordForm.nAlpha}} <t>alphabetic characters</t>
</li>
<li>
<htk-text form="password-form" column="nUpper"/>
<t>capital letters</t>
{{passwordForm.nUpper}} <t>capital letters</t>
</li>
<li>
<htk-text form="password-form" column="nDigits"/>
<t>digits</t>
{{passwordForm.nDigits}} <t>digits</t>
</li>
<li>
<htk-text form="password-form" column="nPunct"/>
<t>symbols</t>
{{passwordForm.nPunct}} <t>symbols</t>
</li>
</ul>
</div>

View File

@ -1,6 +1,6 @@
<vn>
<vn-group>
<db-form id="user">
<db-form v-model="user">
<db-model property="model" lot="hash">
SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role
FROM account.user u
@ -16,11 +16,11 @@
<div id="form" class="access-log">
<div class="box vn-w-xs vn-pa-lg">
<div class="form">
<h4><htk-text form="user" column="nickname"/></h4>
<p>#<htk-text form="user" column="id"/> - <htk-text form="user" column="user"/></p>
<p><htk-text form="user" column="role"/></p>
<p><htk-text form="user" column="email"/></p>
<p><htk-text form="user" column="phone"/></p>
<h4>{{user.nickname}}</h4>
<p>#{{user.id}} - {{user.user}}</p>
<p>{{user.role}}</p>
<p>{{user.email}}</p>
<p>{{user.phone}}</p>
</div>
</div>
<htk-repeater form-id="iter" class="box vn-w-xs htk-list vn-mt-md">

View File

@ -6,7 +6,7 @@
<htk-bar-button
icon="refresh"
tip="_Refresh"
on-click="$.sessions.refresh()"/>
on-click="sessions.refresh()"/>
<div class="connections-sum">
<htk-text>
<db-calc-sum

View File

@ -26,7 +26,7 @@
<div class="item">
<div class="side vn-mr-md">
<htk-image
form="iter"
form="$iter"
column="image"
stamp-column="updated"
class="photo"

View File

@ -10,7 +10,7 @@
<htk-bar-button
icon="refresh"
tip="_Refresh"
on-click="$.visits.refresh()"/>
on-click="visits.refresh()"/>
<htk-bar-button
icon="visibility"
tip="_Connections"

View File

@ -7,7 +7,7 @@
class="start-order"
icon="add_shopping_cart"
tip="_Start order"
on-click="$.hash.setAll({form: 'ecomerce/catalog'})"/>
on-click="hash.setAll({form: 'ecomerce/catalog'})"/>
</div>
<div id="form" class="home">
<div class="column mansonry" id="news-column">

View File

@ -44,19 +44,16 @@
class="delete"
tip="_Remove"
icon="delete"
on-click="this.onDeleteClick($.iter)"/>
on-click="this.onDeleteClick($iter)"/>
<htk-image
form="iter"
column="image"
value="{{iter.image}}"
stamp-column="updated"
class="photo"
directory="catalog"
subdir="200x200"
full-dir="1600x900"/>
<div class="info">
<h2>
<htk-text form="iter" column="item"/>
</h2>
<h2>{{iter.item}}</h2>
<p class="tags">
{{iter.value5}} {{iter.value6}} {{iter.value7}}
</p>

View File

@ -169,13 +169,6 @@ Hedera.Catalog = new Class({
Hedera.Catalog.View.GRID : Hedera.Catalog.View.LIST);
}
,onBasketReady: function(form) {
if (form.$.method != 'PICKUP')
Vn.Node.setText(this.$.method, _('Agency'));
else
Vn.Node.setText(this.$.method, _('Warehouse'));
}
,onItemsChange: function(model, status) {
if (status !== Db.Model.Status.CLEAN)
this.$.order.style.display = 'block';
@ -255,7 +248,7 @@ Hedera.Catalog = new Class({
if (this.isGuest()) return;
this.onEraseClick();
this.$.card.row = form.row;
this.$.$card.row = form.row;
this.$.cardLot.assign({item: form.$.id});
this.$.cardPopup.show(event.currentTarget);
}
@ -303,7 +296,7 @@ Hedera.Catalog = new Class({
if (amountSum > 0) {
this.conn.execQuery(sql);
var itemName = this.$.card.get('item');
var itemName = this.$.$card.get('item');
Htk.Toast.showMessage(
Vn.Value.sprintf(_('Added%dOf%s'), amountSum, itemName));
}
@ -318,7 +311,7 @@ Hedera.Catalog = new Class({
,onPopupClose: function() {
this.onEraseClick();
this.$.card.row = -1;
this.$.$card.row = -1;
this.$.cardLot.value = undefined;
}

View File

@ -87,7 +87,7 @@
param="producer"/>
</vn-group>
<vn-group>
<db-form id="basket" on-ready="onBasketReady">
<db-form v-model="basket">
<db-model property="model">
SELECT b.id, b.sent, a.description agency, m.code method
FROM myBasket b
@ -108,7 +108,7 @@
JOIN vn.itemType t ON t.id = i.typeFk
WHERE #filter;
CALL myBasket_calcCatalogFull;
SELECT i.id, i.description, i.longName item, i.subName,
SELECT i.id, i.longName item, i.subName,
i.tag5, i.value5, i.tag6, i.value6, i.tag7, i.value7,
i.relevancy, i.size, i.category,
k.name ink, p.name producer, o.name origin,
@ -126,7 +126,7 @@
ORDER BY i.relevancy DESC, i.name, i.size
LIMIT 5000;
</db-model>
<db-form id="card" model="items"/>
<db-form id="$card" v-model="card" model="items"/>
<vn-lot id="card-lot"/>
</vn-group>
<div id="form" class="catalog">
@ -135,17 +135,17 @@
id="grid-view"
empty-message="_Choose filter from right menu"
form-id="item"
model="items" >
model="items">
<custom>
<div
id="item-box"
class="item-box clickable"
title="{{_('AddToBasket')}}"
on-click="this.onAddItemClick($event, $.item)">
on-click="this.onAddItemClick($event, $iter)">
<htk-image
directory="catalog"
subdir="200x200"
form="item"
form="$iter"
column="image"
stamp-column="updated"
full-dir="1600x900"
@ -193,12 +193,10 @@
</div>
<div id="right-panel" class="right-panel" on-click="onRightPanelClick">
<div class="basket-info">
<p>{{Vn.Value.format(basket.sent, '%D')}}</p>
<p>
<htk-text form="basket" column="sent" format="%D"/>
</p>
<p>
<span id="method"/>
<htk-text form="basket" column="agency"/>
{{_(basket.method != 'PICKUP' ? 'Agency' : 'Warehouse')}}
{{basket.agency}}
</p>
<button class="thin" on-click="this.onConfigureClick()">
<t>Modify</t>
@ -266,7 +264,7 @@
placeholder="_Color"
form="params"
column="color"
on-mousedown="$.colors.lazyRefresh()">
on-mousedown="colors.lazyRefresh()">
<db-model
id="colors"
property="model"
@ -286,7 +284,7 @@
placeholder="_Producer"
form="params"
column="producer"
on-mousedown="$.producers.lazyRefresh()">
on-mousedown="producers.lazyRefresh()">
<db-model
id="producers"
property="model"
@ -306,7 +304,7 @@
placeholder="_Origin"
form="params"
column="origin"
on-mousedown="$.origins.lazyRefresh()">
on-mousedown="origins.lazyRefresh()">
<db-model
id="origins"
property="model"
@ -327,7 +325,7 @@
placeholder="_Category"
form="params"
column="category"
on-mousedown="$.categorys.lazyRefresh()">
on-mousedown="categorys.lazyRefresh()">
<db-model
id="categorys"
property="model"
@ -395,7 +393,7 @@
modal="true"
on-closed="onPopupClose">
<div property="child-node" class="item-card">
<db-form id="card-extend">
<db-form vModel="extendedCard">
<db-model
property="model"
lot="card-lot"
@ -410,29 +408,19 @@
<htk-image
directory="catalog"
subdir="200x200"
form="card"
form="$card"
column="image"
stamp-column="updated"
full-dir="1600x900"
conn="conn"
editable="true"/>
<div class="item-info">
<h2>
<htk-text form="card" column="item"/>
</h2>
<p class="sub-name">
<htk-text form="card" column="subName"/>
</p>
<p>
#<htk-text form="card" column="id"/>
</p>
<p>
<htk-text form="card" column="stems" format="_%.0d Units"/>
</p>
<h2>{{card.item}}</h2>
<p class="sub-name">{{card.subname}}</p>
<p>#{{card.id}}</p>
<p>{{Vn.Value.format(card.stems, _('%.0d Units'))}}</p>
</div>
<p class="desc">
<htk-text form="card-extend" column="description" id="desc"/>
</p>
<p class="desc">{{extendedCard.description}}</p>
<htk-repeater show-status="false" form-id="tag" class="tags">
<db-model
property="model"
@ -448,8 +436,8 @@
</db-model>
<custom>
<tr>
<td><htk-text form="tag" column="name"/></td>
<td><htk-text form="tag" column="value"/></td>
<td>{{tag.name}}</td>
<td>{{tag.value}}</td>
</tr>
</custom>
</htk-repeater>

View File

@ -14,9 +14,7 @@
column="debt">
<db-form property="form">
<db-model property="model">
<custom>
SELECT -myClient_getDebt(NULL) debt
</custom>
SELECT -myClient_getDebt(NULL) debt
</db-model>
</db-form>
</htk-text>
@ -42,9 +40,7 @@
form-id="iter"
renderer="repeaterFunc">
<db-model property="model" id="tickets">
<custom>
CALL myTicket_list (NULL, NULL);
</custom>
CALL myTicket_list(NULL, NULL);
</db-model>
<custom>
<a id="link" class="item" title="{{_('SeeOrder')}}">

View File

@ -10,13 +10,6 @@ Hedera.Ticket = new Class({
this.conn.execQuery('CALL myTicket_logAccess(#ticket)', null, params);
},
onTicketReady: function(form) {
if (form.$.method != 'PICKUP')
Vn.Node.setText(this.$.method, _('Agency'));
else
Vn.Node.setText(this.$.method, _('Warehouse'));
},
onPrintClick: function() {
let params = Vn.Url.makeUri({
authorization: this.conn.token,
@ -27,24 +20,17 @@ Hedera.Ticket = new Class({
window.open(`/api/report/delivery-note?${params}`);
},
repeaterFunc: function(res, form) {
var discount = res.$.discount;
discount.style.display = form.$.discount ? 'inline' : 'none';
res.$.discountSubtotal.value = this.discountSubtotal(form);
res.$.subtotal.value = this.subtotal(form);
repeaterFunc: function(scope, form) {
scope.$.discount.style.display = form.$.discount ? 'inline' : 'none';
},
discountSubtotal: function(form) {
return form.$.quantity * form.$.price;
discountSubtotal: function(line) {
return line.quantity * line.price;
},
subtotal: function(form) {
var discount = form.$.discount;
return this.discountSubtotal(form) * ((100 - discount) / 100);
},
servicesFunc: function(res, form) {
res.$.subtotal.value = form.$.quantity * form.$.price;
subtotal: function(line) {
var discount = line.discount;
return this.discountSubtotal(line) * ((100 - discount) / 100);
},
onServicesChanged: function(model) {

View File

@ -2,7 +2,7 @@
<vn-lot-query id="params">
<vn-spec name="ticket" type="Number"/>
</vn-lot-query>
<db-lot id="ticket" lot="params" on-ready="onTicketReady">
<db-lot id="ticket-form" lot="params" v-model="ticket">
CALL myTicket_get(#ticket)
</db-lot>
<div id="title">
@ -16,42 +16,32 @@
</div>
<div id="form" class="ticket">
<div class="box vn-w-sm vn-pa-lg">
<htk-loader class="head" form="ticket">
<h5>#<htk-text column="id" form="ticket"/></h5>
<htk-loader class="head" form="ticket-form">
<h5>#{{ticket.id}}</h5>
<div>
<h6><t>ShippingInformation</t></h6>
<p>
<t>Preparation</t> <htk-text form="ticket" column="shipped" format="%D"/>
<t>Preparation</t> {{Vn.Value.format(ticket.shipped, '%D')}}
</p>
<p>
<t>Delivery</t> <htk-text form="ticket" column="landed" format="%D"/>
<t>Delivery</t> {{Vn.Value.format(ticket.landed, '%D')}}
</p>
<p>
<span id="method"></span> <htk-text form="ticket" column="agency"/>
{{_(ticket.method != 'PICKUP' ? 'Agency' : 'Warehouse')}} {{ticket.agency}}
</p>
</div>
<div class="address">
<h6><t>DeliveryAddress</t></h6>
<p>
<htk-text form="ticket" column="nickname"/>
</p>
<p>
<htk-text form="ticket" column="street"/>
</p>
<p>
<htk-text form="ticket" column="postalCode"/>
<htk-text form="ticket" column="city"/>
(<htk-text form="ticket" column="province"/>)
</p>
<p>{{ticket.nickname}}</p>
<p>{{ticket.street}}</p>
<p>{{ticket.postalCode}} {{ticket.city}} ({{ticket.province}})</p>
</div>
<div class="total">
<p class="important total">
<t>Total</t>
<htk-text format="%.2d€" form="ticket" column="taxBase"/>
<t>Total</t> {{Vn.Value.format(ticket.taxBase, '%.2d€')}}
</p>
<p class="important total">
<t>Total + tax</t>
<htk-text format="%.2d€" form="ticket" column="total"/>
<t>Total + tax</t> {{Vn.Value.format(ticket.total, '%.2d€')}}
</p>
</div>
</htk-loader>
@ -65,38 +55,32 @@
<custom>
<div class="line">
<htk-image
form="iter"
column="image"
value="{{iter.image}}"
stamp-column="updated"
class="photo"
directory="catalog"
subdir="200x200"
full-dir="1600x900"/>
<div class="info">
<h2>
<htk-text form="iter" column="concept"/>
</h2>
<h2>{{iter.concept}}</h2>
<p class="tags">
<htk-text form="iter" column="value5"/>
<htk-text form="iter" column="value6"/>
<htk-text form="iter" column="value7"/>
{{iter.value5}} {{iter.value6}} {{iter.value7}}
</p>
<p class="amount">
<htk-text form="iter" column="quantity"/> x
<htk-text form="iter" column="price" format="%.2d€"/>
{{iter.quantity}} x {{Vn.Value.format(iter.price, '%.2d€')}}
</p>
<p class="subtotal">
<span class="discount" id="discount">
<htk-text id="discount-subtotal" format="%.2d€"/> -
<htk-text form="iter" column="discount" format="%.0d%"/> =
{{Vn.Value.format(this.discountSubtotal(iter), '%.2d€')}} -
{{Vn.Value.format(iter.discount, '%.0d%')}} =
</span>
<htk-text id="subtotal" format="%.2d€"/>
{{Vn.Value.format(this.subtotal(iter), '%.2d€')}}
</p>
</div>
</div>
</custom>
</htk-repeater>
<htk-repeater form-id="iter" id="services" class="packages" renderer="servicesFunc">
<htk-repeater form-id="iter" id="services" class="packages">
<db-model
property="model"
on-status-changed="onServicesChanged"
@ -106,15 +90,12 @@
<custom>
<div class="line">
<div class="info">
<h2>
<htk-text form="iter" column="description"/>
</h2>
<h2>{{iter.description}}</h2>
<p class="amount">
<htk-text form="iter" column="quantity"/> x
<htk-text form="iter" column="price" format="%.2d€"/>
{{iter.quantity}} x {{Vn.Value.format(iter.price, '%.2d€')}}
</p>
<p class="subtotal">
<htk-text id="subtotal" format="%.2d€"/>
{{Vn.Value.format(iter.quantity * iter.price, '%.2d€')}}
</p>
</div>
<div class="clear"/>
@ -131,22 +112,15 @@
<custom>
<div class="line">
<htk-image
form="iter"
column="image"
value="{{iter.image}}"
class="photo"
directory="catalog"
subdir="200x200"
full-dir="1600x900"/>
<div class="info">
<h2>
<htk-text form="iter" column="name"/>
</h2>
<p>
#<htk-text form="iter" column="id"/>
</p>
<p class="amount">
<htk-text form="iter" column="quantity"/>
</p>
<h2>{{iter.name}}</h2>
<p>#{{iter.id}}</p>
<p class="amount">{{iter.quantity}}</p>
</div>
<div class="clear"/>
</div>

View File

@ -24,7 +24,7 @@
title="_EditNew">
<div class="side vn-mr-md">
<htk-image
form="iter"
form="$iter"
column="image"
class="photo"
directory="news"
@ -46,7 +46,7 @@
<htk-button
tip="_Remove"
icon="delete"
on-click="this.onDeleteClick($.iter)"/>
on-click="this.onDeleteClick($iter)"/>
</div>
</a>
</custom>

View File

@ -844,6 +844,7 @@ Model.implement({
if (!(op.type & Operation.DELETE
&& op.type & Operation.INSERT))
// eslint-disable-next-line no-unused-vars
isOperation = true;
if (op.type & Operation.DELETE) {

View File

@ -20,11 +20,11 @@ module.exports = new Class({
const hash = this.hash;
const builder = new Vn.Builder();
builder.compileFile('forms/'+ this.formInfo.path +'/ui.xml');
builder.compileFile(`forms/${this.formInfo.path}/ui.xml`);
const scope = this.builder = builder.load(null, this);
this.$ = scope.$;
scope.link(null, {conn, hash});
scope.link({conn, hash});
this.node = scope.$.form;
const paramsLot = this.$.params;

View File

@ -69,7 +69,7 @@ module.exports = new Class({
builder.compileFile('reports/'+ this.info.path +'/ui.xml');
var scope = this.scope = builder.load(this.doc, this);
scope.link(null, {
scope.link({
lot: this.lot,
conn: this.conn
});

View File

@ -12,6 +12,8 @@ td.cell-button {
width: 44px;
margin: 0 auto;
border: none;
border-radius: 50%;
padding: 10px;
background-color: transparent;
box-sizing: border-box;

View File

@ -5,13 +5,11 @@ module.exports = new Class({
Extends: Component
,Tag: 'htk-repeater'
,Child: 'model'
,Properties:
{
,Properties: {
/**
* The source data model.
*/
model:
{
model: {
type: Db.Model
,set: function(x) {
this.link({_model: x}, {
@ -30,8 +28,7 @@ module.exports = new Class({
/**
* The identifier for internal iterator.
*/
,formId:
{
,formId: {
type: String
,set: function(x) {
this._formId = x;
@ -44,8 +41,7 @@ module.exports = new Class({
* {Function (Vn.BuilderResult, Db.Form)} Function to call after every
* box rendering.
*/
,renderer:
{
,renderer: {
type: Function
,set: function(x) {
this._renderer = x;
@ -57,8 +53,7 @@ module.exports = new Class({
/**
* Wether to show the model status.
*/
,showStatus:
{
,showStatus: {
type: Boolean
,set: function(x) {
this._showStatus = x;
@ -71,8 +66,7 @@ module.exports = new Class({
/**
* Message that should be displayed when source model is not ready.
*/
,emptyMessage:
{
,emptyMessage: {
type: String
,value: null
}
@ -105,7 +99,7 @@ module.exports = new Class({
}
,getBuilder: function(index) {
return this._childsData[index].builder;
return this._childsData[index].scope;
}
,getForm: function(index) {
@ -119,14 +113,12 @@ module.exports = new Class({
});
var scope = this._builder.load(this.doc, null, this._parentScope);
scope.link([set.getObject()], {
[this._formId]: set
scope.link({
$iter: set,
[this._formId]: set.getObject()
});
this._childsData.push({
builder: scope,
set: set
});
this._childsData.push({scope, set});
if (this._renderer)
this._renderer(scope, set);
@ -228,7 +220,7 @@ module.exports = new Class({
,_unrefChildData: function(index) {
var childData = this._childsData[index];
childData.set.unref();
childData.builder.unref();
childData.scope.unref();
}
,destroy: function() {

View File

@ -1,16 +1,22 @@
const VnObject = require('./object');
const Component = require('./component');
const VnNode = require('./node');
const Scope = require('./scope');
const Type = require('./type');
const kebabToCamel = require('./string-util').kebabToCamel;
const CompilerObject = require('./compiler-object');
const CompilerElement = require('./compiler-element');
const CompilerText = require('./compiler-text');
const regCompilers = [
CompilerText,
CompilerObject,
CompilerElement
];
/**
* Creates a object from a XML specification.
*/
module.exports = new Class({
Extends: VnObject
,_contexts: null
/**
* Compiles an XML file.
@ -30,8 +36,8 @@ module.exports = new Class({
* @return {Boolean} %true on success, %false othersise
*/
,compileString: function(xmlString) {
var parser = new DOMParser();
var doc = parser.parseFromString(xmlString, 'text/xml');
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, 'text/xml');
return this.compileDocument(doc);
}
@ -41,12 +47,12 @@ module.exports = new Class({
* @param {Document} doc The DOM document
* @return {Boolean} %true on success, %false othersise
*/
,compileDocument: function(doc, exprArgs) {
,compileDocument: function(doc) {
if (!doc)
return false;
this._preCompile(exprArgs);
var docElement = doc.documentElement;
this._preCompile();
const docElement = doc.documentElement;
if (docElement.tagName !== 'vn') {
this.showError('The toplevel tag should be named \'vn\'');
@ -54,9 +60,9 @@ module.exports = new Class({
return false;
}
var childs = docElement.childNodes;
const childs = docElement.childNodes;
if (childs)
for (var i = 0; i < childs.length; i++)
for (let i = 0; i < childs.length; i++)
this._compile(childs[i]);
this._postCompile();
@ -69,8 +75,8 @@ module.exports = new Class({
* @path Node The DOM node
* @return %true on success, %false othersise
*/
,compileNode: function(node, exprArgs) {
this._preCompile(exprArgs);
,compileNode: function(node) {
this._preCompile();
this._mainContext = this._compile(node).id;
this._postCompile();
return true;
@ -79,28 +85,27 @@ module.exports = new Class({
/**
* Called before starting to compile nodes.
*/
,_preCompile: function(exprArgs) {
,_preCompile: function() {
this._path = null;
this._tags = {};
this._contexts = [];
this._exprContexts = [];
this._contextMap = {};
this._links = [];
this._mainContext = null;
this._baseExprArgs = ['_', '$'];
if (exprArgs)
this._baseExprArgs = this._baseExprArgs.concat(exprArgs);
this._baseEventArgs = this._baseExprArgs.concat(['$event']);
this._exprArgs = this._baseExprArgs.join(',');
this._eventArgs = this._baseEventArgs.join(',');
this._compilers = [];
for (regCompiler of regCompilers)
this._compilers.push(new regCompiler(this));
}
/**
* Called after all nodes have been compiled.
*/
,_postCompile: function() {}
,_postCompile: function() {
for (const compiler of this._compilers)
compiler.postCompile(this._contextMap);
}
/**
* Compiles a node.
@ -115,24 +120,24 @@ module.exports = new Class({
else if (node.nodeType !== Node.TEXT_NODE
|| /^[\n\r\t]*$/.test(node.textContent))
return null;
context =
this.textCompile(node, tagName)
|| this.objectCompile(node, tagName)
|| this.elementCompile(node, tagName);
let i;
const compilers = this._compilers;
for (i = 0; i < compilers.length && context === null; i++)
context = compilers[i].compile(this, node, tagName);
context.id = this._contexts.length;
context.compiler = compilers[i - 1];
if (isElement) {
var nodeId = node.getAttribute('id');
const nodeId = node.getAttribute('id');
if (nodeId) {
this._contextMap[kebabToCamel(nodeId)] = context.id;
context.nodeId = nodeId;
}
var tags = this._tags[tagName];
let tags = this._tags[tagName];
if (!tags)
this._tags[tagName] = tags = [];
@ -143,537 +148,21 @@ module.exports = new Class({
return context;
}
,getMain: function(scope) {
return scope.objects[this._mainContext];
}
,getByTagName: function(scope, tagName) {
var tags = this._tags[tagName];
if (tags) {
var arr = new Array(tags.length);
for (var i = 0; i < tags.length; i++)
arr[i] = scope.objects[tags[i]];
return arr;
}
return [];
}
,load: function(dstDocument, thisArg, parentScope) {
if (this._contexts === null)
return null;
if (!this._contexts) return null;
const doc = dstDocument ? dstDocument : document;
const contexts = this._contexts;
const len = contexts.length;
const objects = new Array(len);
const scope = new Scope(this, objects, thisArg, parentScope);
for (var i = 0; i < len; i++) {
var context = contexts[i];
if (context.tagName)
objects[i] = this.elementInstantiate(doc, context, scope);
else if (context.klass)
objects[i] = this.objectInstantiate(doc, context, scope);
else
objects[i] = this.textInstantiate(doc, context, scope);
}
return scope;
}
,link: function(scope) {
const objects = scope.objects;
const links = this._links;
// Pre-link
for (var i = links.length - 1; i >= 0; i--) {
const link = links[i];
const object = objects[link.context.id];
const objectRef = scope.$[link.objectId];
if (objectRef === undefined) {
this.showError('Referenced unexistent object with id \'%s\'',
link.objectId);
continue;
}
if (link.prop)
object[link.prop] = objectRef;
else
object.appendChild(objectRef);
}
// Post-link
const contexts = this._contexts;
for (var i = 0; i < contexts.length; i++) {
const context = contexts[i];
const object = objects[i];
if (context.tagName)
this.elementLink(context, object, objects, scope);
else if (context.klass)
this.objectLink(context, object, objects, scope);
}
}
,digest(scope) {
const contexts = this._contexts;
const objects = scope.objects;
const exprScope = scope.exprScope;
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
const object = objects[i];
if (context.exprs) {
const values = [];
let isEmpty = false;
for (expr of context.exprs) {
let value = undefined;
try {
value = expr.apply(scope.thisArg, exprScope);
if (value == null) {
isEmpty = true;
break;
}
} catch (e) {
console.warn('Expression error:', e.message);
continue;
}
values.push(value);
}
let text;
if (!isEmpty) {
let k = 0;
text = context.text.replace(/{{\d+}}/g, function() {
return values[k++];
});
} else
text = '';
object.textContent = text;
} else {
const dynProps = context.dynProps;
for (const prop in dynProps) {
let value = undefined;
try {
value = dynProps[prop].apply(scope.thisArg, exprScope);
} catch (e) {
console.warn('Expression error:', e.message);
continue;
}
if (context.tagName)
object.setAttribute(prop, value);
else
object[prop] = value;
}
}
}
const objects = new Array(this._contexts.length);
const exprValues = new Array(this._exprContexts.length);
return new Scope(this, doc, objects, exprValues, thisArg, parentScope);
}
,showError: function(error) {
var path = this._path ? this._path : 'Node';
var logArgs = ['Vn.Builder: %s: '+ error, path];
const path = this._path ? this._path : 'Node';
const logArgs = ['Vn.Builder: %s: '+ error, path];
for (var i = 1; i < arguments.length; i++)
for (let i = 1; i < arguments.length; i++)
logArgs.push(arguments[i]);
console.warn.apply(null, logArgs);
}
,_addLink: function(context, prop, objectId) {
this._links.push({
context
,prop
,objectId: kebabToCamel(objectId)
});
}
,fnExpr(expr) {
return new Function(this._exprArgs,
'"use strict"; return ' + expr + ';'
);
}
,matchExpr(value) {
const match = /^{{(.*)}}$/.exec(value);
if (!match) return null;
return this.fnExpr(match[1]);
}
,_translateValue: function(value) {
var chr = value.charAt(0);
if (chr === '_')
return _(value.substr(1));
else if (chr === '\\' && value.charAt(1) === '_')
return value.substr(1);
return value;
}
,_getMethod: function(value) {
let method;
if (this.isIdentifier(value)) {
// XXX: Compatibility with old events
method = value;
} else {
try {
method = new Function(this._eventArgs,
'"use strict"; return ' + value + ';'
);
} catch (err) {
this.showError(`Method: ${err.message}: ${value}`);
}
}
return method;
}
,_isEvent: function(attribute) {
return /^on-\w+/.test(attribute);
}
,isIdentifier: function(value) {
return /^[a-zA-Z_$][\w$]*$/.test(value);
}
,_replaceFunc: function(token) {
return token.charAt(1).toUpperCase();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TextNode
/**
* Creates a text node context.
*/
,textCompile: function(node, tagName) {
if (!tagName) {
let text = node.textContent;
if (/{{.*}}/.test(text)) {
let i = 0;
const self = this;
const exprs = [];
text = text.replace(/{{((?:(?!}}).)*)}}/g, function(match, capture) {
exprs.push(self.fnExpr(capture));
return `{{${i++}}}`;
});
return {text, exprs};
} else
return {text};
} else if (tagName === 't')
return {text: _(node.firstChild.textContent)};
else
return null;
}
,textInstantiate: function(doc, context) {
return doc.createTextNode(context.exprs ? '' : context.text);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Vn.Object
/**
* Creates a object context.
*/
,objectCompile: function(node, tagName) {
var klass = vnCustomTags[tagName];
if (!klass)
return null;
var props = {};
var objectProps = {};
var childs = [];
var events = {};
var context = {
klass: klass,
props: props,
dynProps: {},
funcProps: {},
objectProps: objectProps,
childs: childs,
events: events,
custom: null
};
var a = node.attributes;
for (var i = 0; i < a.length; i++) {
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if (this._isEvent(attribute)) {
var handler = this._getMethod(value)
if (handler)
events[attribute.substr(3)] = handler;
} else if (!/^(id|property)$/.test(attribute)) {
this.propCompile(context, klass, props,
node, attribute, value);
}
}
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++) {
var child = childNodes[i];
var isElement = child.nodeType === Node.ELEMENT_NODE;
var childTagName = isElement ? child.tagName.toLowerCase() : null;
var childContext;
if (childTagName === 'pointer') {
this._addLink(context, null, child.getAttribute('object'));
} else if (childTagName === 'custom') {
context.custom = child;
} else if (childContext = this._compile(child)) {
var prop = isElement ? child.getAttribute('property') : null;
if (prop) {
prop = prop.replace(/-./g, this._replaceFunc);
objectProps[prop] = childContext.id;
} else
childs.push(childContext.id);
}
}
return context;
}
,propCompile: function(context, klass, props, node, attribute, value) {
let isLink = false;
let propError = false;
let newValue = null;
const propName = attribute.replace(/-./g, this._replaceFunc);
const propInfo = klass.Properties[propName];
if (!propInfo) {
this.showError('Attribute \'%s\' not valid for tag \'%s\'',
attribute, node.tagName);
return;
}
if (!value) {
this.showError('Attribute \'%s\' empty on tag \'%s\'',
attribute, node.tagName);
return;
}
const expr = this.matchExpr(value);
if (expr) {
context.dynProps[propName] = expr;
} else {
switch (propInfo.type) {
case Boolean:
newValue = (/^(true|1)$/i).test(value);
break;
case Number:
newValue = 0 + new Number(value);
break;
case String:
newValue = this._translateValue(value);
break;
case Function:
context.funcProps[propName] = this._getMethod(value);
break;
case Type:
newValue = window[value];
break;
default:
if (propInfo.enumType)
newValue = propInfo.enumType[value];
else if (propInfo.type instanceof Function)
isLink = true;
else
propError = true;
}
if (isLink)
this._addLink(context, propName, value);
else if (newValue !== null && newValue !== undefined)
props[propName] = newValue;
else if (propError)
this.showError('Attribute \'%s\' invalid for tag \'%s\'',
attribute, node.tagName);
}
}
,objectInstantiate: function(doc, context, scope) {
const object = new context.klass();
object.setProperties(context.props);
if (context.nodeId && object instanceof Component) {
var id = context.nodeId;
object.htmlId = scope.getHtmlId(id);
object.className = '_'+ id +' '+ (object.className || '');
}
return object;
}
,objectLink: function(context, object, objects, scope) {
const objectProps = context.objectProps;
for (const prop in objectProps)
object[prop] = objects[objectProps[prop]];
const childs = context.childs;
for (let i = 0; i < childs.length; i++)
object.appendChild(objects[childs[i]]);
const funcProps = context.funcProps;
for (const prop in funcProps) {
let method;
const handler = funcProps[prop];
if (typeof handler === 'string') {
// XXX: Compatibility with old expressions
method = scope.thisArg[handler];
if (!method)
this.showError(`Function '${handler}' not found`);
method = method.bind(scope.thisArg);
} else {
method = function() {
handler.apply(scope.thisArg, scope.exprScope);
};
}
if (method)
object[prop] = method;
}
const events = context.events;
for (const event in events) {
let listener;
const handler = events[event];
if (typeof handler === 'string') {
// XXX: Compatibility with old expressions
listener = scope.thisArg[handler];
if (!listener)
this.showError(`Function '${handler}' not found`);
} else {
listener = function() {
handler.apply(scope.thisArg, scope.exprScope.concat(arguments));
};
}
if (listener)
object.on(event, listener, scope.thisArg);
}
if (context.custom)
object.loadXml(scope, context.custom);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Element
/**
* Creates a HTML node context.
*/
,elementCompile: function(node, tagName) {
var attributes = {};
var dynProps = {};
var childs = [];
var events = {};
var handler;
var a = node.attributes;
for (var i = 0; i < a.length; i++) {
var attribute = a[i].nodeName;
var value = a[i].nodeValue;
if (this._isEvent(attribute)) {
var handler = this._getMethod(value);
if (handler) events[attribute.substr(3)] = handler;
} else if (attribute !== 'id') {
const expr = this.matchExpr(value);
if (expr)
dynProps[attribute] = expr;
else
attributes[attribute] = this._translateValue(value);
}
}
var childContext;
var childNodes = node.childNodes;
if (childNodes)
for (var i = 0; i < childNodes.length; i++)
if (childContext = this._compile(childNodes[i]))
childs.push(childContext.id);
return {
tagName,
attributes,
dynProps,
childs,
events
};
}
,elementInstantiate: function(doc, context, scope) {
var object = doc.createElement(context.tagName);
const attributes = context.attributes;
for (const attribute in attributes)
object.setAttribute(attribute, attributes[attribute]);
if (context.nodeId) {
const id = context.nodeId;
object.setAttribute('id', scope.getHtmlId(id));
VnNode.addClass(object, '_'+ id);
}
return object;
}
,elementLink: function(context, object, objects, scope) {
const childs = context.childs;
for (var i = 0; i < childs.length; i++) {
let child = objects[childs[i]];
if (child instanceof Component)
child = child.node;
if (child instanceof Node)
object.appendChild(child);
}
const events = context.events;
for (const event in events) {
let listener;
const handler = events[event];
if (typeof handler === 'string') {
// XXX: Compatibility with old expressions
listener = scope.thisArg[handler];
if (!listener)
this.showError(`Function '${handler}' not found`);
listener = listener.bind(scope.thisArg);
} else {
listener = function(e) {
handler.apply(scope.thisArg, scope.exprScope.concat(e));
};
}
if (listener)
object.addEventListener(event, listener);
}
}
});

85
js/vn/compiler-element.js Normal file
View File

@ -0,0 +1,85 @@
const Compiler = require('./compiler');
const Component = require('./component');
const VnNode = require('./node');
/**
* Compiles a @HTMLElement from element tag.
*/
module.exports = new Class({
Extends: Compiler
,compile: function(builder, node, tagName) {
const context = {
tagName,
attributes: {},
childs: [],
events: {}
};
const {attributes} = context;
const a = node.attributes;
for (let i = 0; i < a.length; i++) {
const attribute = a[i].nodeName;
const value = a[i].nodeValue;
if (this.isEvent(attribute)) {
const handler = this._getMethod(value);
if (handler) context.events[attribute.substr(3)] = handler;
} else if (!/^(id|property)$/.test(attribute)) {
if (this.isExpr(value))
this.compileExpr(context, attribute, value);
else
attributes[attribute] = this._translateValue(value);
}
}
let childContext;
const childNodes = node.childNodes;
if (childNodes)
for (let i = 0; i < childNodes.length; i++)
if (childContext = builder._compile(childNodes[i]))
context.childs.push(childContext.id);
return context;
}
,instantiate: function(doc, context, scope) {
const object = doc.createElement(context.tagName);
const attributes = context.attributes;
for (const attribute in attributes)
object.setAttribute(attribute, attributes[attribute]);
if (context.nodeId) {
const id = context.nodeId;
object.setAttribute('id', scope.getHtmlId(id));
VnNode.addClass(object, '_'+ id);
}
return object;
}
,setProperty(object, property, value) {
object.setAttribute(property, value);
}
,link: function(context, object, objects, scope) {
const childs = context.childs;
for (let i = 0; i < childs.length; i++) {
let child = objects[childs[i]];
if (child instanceof Component)
child = child.node;
if (child instanceof Node)
object.appendChild(child);
}
const events = context.events;
for (const event in events) {
const listener = this.bindMethod(events[event], scope, true);
if (listener)
object.addEventListener(event, listener);
}
}
});

216
js/vn/compiler-object.js Normal file
View File

@ -0,0 +1,216 @@
const Compiler = require('./compiler');
const Component = require('./component');
const Type = require('./type');
const kebabToCamel = require('./string-util').kebabToCamel;
/**
* Compiles a @Vn.Object from element tag.
*/
module.exports = new Class({
Extends: Compiler
,_links: []
/**
* Creates a object context.
*/
,compile: function(builder, node, tagName) {
const klass = vnCustomTags[tagName];
if (!klass) return null;
const context = {
klass,
props: {},
funcProps: {},
objectProps: {},
childs: [],
events: {},
custom: null
};
const a = node.attributes;
for (let i = 0; i < a.length; i++) {
const attribute = a[i].nodeName;
const value = a[i].nodeValue;
if (this.isEvent(attribute)) {
const handler = this._getMethod(value)
if (handler) context.events[attribute.substr(3)] = handler;
} else if (!/^(id|property)$/.test(attribute)) {
this.propCompile(context, node, attribute, value);
}
}
const childNodes = node.childNodes;
if (childNodes)
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
const isElement = child.nodeType === Node.ELEMENT_NODE;
const childTagName = isElement ? child.tagName.toLowerCase() : null;
let childContext;
if (childTagName === 'pointer') {
this._addLink(context, null, child.getAttribute('object'));
} else if (childTagName === 'custom') {
context.custom = child;
} else if (childContext = builder._compile(child)) {
let prop = isElement ? child.getAttribute('property') : null;
if (prop) {
prop = kebabToCamel(prop);
context.objectProps[prop] = childContext.id;
} else
context.childs.push(childContext.id);
}
}
return context;
}
,propCompile: function(context, node, attribute, value) {
const tagName = node.tagName;
const propName = kebabToCamel(attribute);
const propInfo = context.klass.Properties[propName];
if (!value) {
this.showError('Attribute \'%s\' empty on tag \'%s\'',
attribute, tagName);
return;
}
if (propName == 'vModel') {
context.vModel = this.modelExpr(value);
return;
}
if (!propInfo) {
this.showError('Attribute \'%s\' not valid for tag \'%s\'',
attribute, tagName);
return;
}
if (this.isExpr(value)) {
this.compileExpr(context, propName, value);
} else {
let isLink = false;
let propError = false;
let newValue = null;
switch (propInfo.type) {
case Boolean:
newValue = (/^(true|1)$/i).test(value);
break;
case Number:
newValue = 0 + new Number(value);
break;
case String:
newValue = this._translateValue(value);
break;
case Function:
context.funcProps[propName] = this._getMethod(value);
break;
case Type:
newValue = window[value];
break;
default:
if (propInfo.enumType)
newValue = propInfo.enumType[value];
else if (propInfo.type instanceof Function)
isLink = true;
else
propError = true;
}
if (isLink)
this._addLink(context, propName, value);
else if (newValue !== null && newValue !== undefined)
context.props[propName] = newValue;
else if (propError)
this.showError('Attribute \'%s\' invalid for tag \'%s\'',
attribute, tagName);
}
}
,instantiate: function(doc, context, scope) {
const object = new context.klass();
object.setProperties(context.props);
if (context.nodeId && object instanceof Component) {
const id = context.nodeId;
object.htmlId = scope.getHtmlId(id);
object.className = '_'+ id +' '+ (object.className || '');
}
return object;
}
,setProperty(object, property, value) {
object[property] = value;
}
,preLink(scope) {
const objects = scope.objects;
const links = this._links;
for (let i = links.length - 1; i >= 0; i--) {
const link = links[i];
const object = objects[link.context.id];
const objectRef = scope.$[link.objectId];
if (objectRef === undefined) {
this.showError('Referenced unexistent object with id \'%s\'',
link.objectId);
continue;
}
if (link.prop)
object[link.prop] = objectRef;
else
object.appendChild(objectRef);
}
}
,link: function(context, object, objects, scope) {
const objectProps = context.objectProps;
for (const prop in objectProps)
object[prop] = objects[objectProps[prop]];
const childs = context.childs;
for (let i = 0; i < childs.length; i++)
object.appendChild(objects[childs[i]]);
const funcProps = context.funcProps;
for (const prop in funcProps)
object[prop] = this.bindMethod(funcProps[prop], scope);
const events = context.events;
for (const event in events) {
const listener = this.bindMethod(events[event], scope);
if (listener)
object.on(event, listener, scope.thisArg);
}
if (context.vModel) {
object.on('change', function(lot) {
context.vModel.call(scope.thisArg, scope.$, lot.$);
scope.digest();
}, scope);
}
if (context.custom)
object.loadXml(scope, context.custom);
}
,_addLink: function(context, prop, objectId) {
this._links.push({
context
,prop
,objectId: kebabToCamel(objectId)
});
}
,_replaceFunc: function(token) {
return token.charAt(1).toUpperCase();
}
});

33
js/vn/compiler-text.js Normal file
View File

@ -0,0 +1,33 @@
const Compiler = require('./compiler');
/**
* Compiles a @Text from text node.
*/
module.exports = new Class({
Extends: Compiler
,compile: function(builder, node, tagName) {
if (tagName && tagName != 't')
return null;
const text = node.textContent;
const context = {text};
if (tagName === 't') {
context.text = _(node.firstChild.textContent);
} else if (this.isExpr(text, true)) {
context.text = '';
this.compileExpr(context, null, text, true);
}
return context;
}
,instantiate: function(doc, context) {
return doc.createTextNode(context.text);
}
,setProperty(object, property, value) {
object.textContent = value;
}
});

150
js/vn/compiler.js Normal file
View File

@ -0,0 +1,150 @@
var VnObject = require('./object');
/**
* Base class for compilers.
*/
module.exports = new Class({
Extends: VnObject
,compile: function() {}
,postCompile: function() {}
,instantiate: function() {}
,preLink: function() {}
,link: function() {}
,connect: function() {}
,postLink: function() {}
,setProperty: function() {}
,free: function() {}
,initialize: function(builder) {
this._builder = builder;
this._interpoler = builder._interpoler;
this.parent();
}
/**
* Checks if the passed attribute name it's an event.
*
* @param {String} attribute The attribute name
* @return {Boolean} %true if it's an event, otherwise %false
*/
,isEvent: function(attribute) {
return /^on-\w+/.test(attribute);
}
,isIdentifier: function(value) {
return /^[a-zA-Z_$][\w$]*$/.test(value);
}
/**
* Logs an error parsing the node.
*
* @param {String} error The error message template
* @param {...} varArgs The message template arguments
*/
,showError: function() {
this._builder.showError.apply(this._builder, arguments);
}
,_getMethod: function(value) {
// XXX: Compatibility with old methods
return this.isIdentifier(value)
? value
: this.fnExpr(value);
}
,bindMethod(handler, scope, isEvent) {
// XXX: Compatibility with old methods
if (typeof handler === 'string') {
const method = scope.thisArg[handler];
if (!method) {
this.showError(`Function '${handler}' not found`);
return undefined;
}
return method.bind(scope.thisArg);
}
return function($event) {
let handlerScope;
if (isEvent) {
handlerScope = Object.create(scope.$);
Object.assign(handlerScope, {$event});
} else
handlerScope = scope.$;
handler.call(this, handlerScope);
}.bind(scope.thisArg);
}
,matchExpr(value) {
const match = /^{{(.*)}}$/.exec(value);
if (!match) return null;
return this.fnExpr(match[1]);
}
,modelExpr(expr) {
try {
return new Function('$scope', '$value',
`"use strict"; $scope.${expr} = $value;`
);
} catch (err) {
this.showError(`${err.message}:`, expr);
}
}
,exprRegex: /^{{((?:(?!}}).)*)}}$/
,exprRegexMulti: /{{((?:(?!}}).)*)}}/g
,isExpr(expr, isMulti) {
return isMulti
? this.exprRegexMulti.test(expr)
: this.exprRegex.test(expr);
}
,compileExpr(context, property, value, isMulti) {
const exprContext = {
context,
property,
value
};
if (isMulti) {
let i = 0;
const self = this;
exprContext.exprs = [];
exprContext.template = value.replace(this.exprRegexMulti,
function(match, capture) {
exprContext.exprs.push(self.fnExpr(capture));
return `{{${i++}}}`;
});
} else {
const match = this.exprRegex.exec(value);
exprContext.expr = this.fnExpr(match[1]);
}
this._builder._exprContexts.push(exprContext);
}
,fnExpr(expr) {
try {
return new Function('$scope',
`with($scope) { return ${expr}; }`
);
} catch (err) {
this.showError(`${err.message}:`, expr);
}
}
,_translateValue: function(value) {
var chr = value.charAt(0);
if (chr === '_')
return _(value.substr(1));
else if (chr === '\\' && value.charAt(1) === '_')
return value.substr(1);
return value;
}
});

View File

@ -29,7 +29,11 @@ module.exports = new Class({
}
,initialize: function(props) {
this._params = {};
this._params = new Proxy({}, {
set(obj, prop, value) {
return Reflect.set(obj, prop, value);
}
});
VnObject.prototype.initialize.call(this, props);
}

View File

@ -6,21 +6,30 @@ let scopeUid = 0;
module.exports = new Class({
Extends: VnObject
,initialize: function(builder, objects, thisArg, parent) {
,initialize: function(builder, doc, objects, exprValues, thisArg, parent) {
this.builder = builder;
this.objects = objects;
this.exprValues = exprValues;
this.thisArg = thisArg;
this.parent = parent;
this.uid = ++scopeUid;
this.$ = parent ? Object.create(parent.$) : {};
if (parent) {
parent.on('lot-change', this.onLotChange, this);
parent.ref();
// XXX: Keep commented until optimized
//parent.on('change', this.onChange, this);
if (!thisArg) this.thisArg = parent.thisArg;
}
const contexts = builder._contexts;
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
objects[i] = context.compiler.instantiate(doc, context, this);
}
}
,link: function(exprScope, extraObjects) {
,link: function(extraObjects) {
var contextMap = this.builder._contextMap;
for (var id in extraObjects)
@ -28,26 +37,88 @@ module.exports = new Class({
for (var id in contextMap)
this.$[id] = this.objects[contextMap[id]];
this.exprScope = [
_,
this.$
].concat(exprScope);
const builder = this.builder;
const contexts = builder._contexts;
const objects = this.objects;
this.builder.link(this);
this.builder.digest(this);
for (const compiler of builder._compilers)
compiler.preLink(this);
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
context.compiler.link(context, objects[i], objects, this);
}
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
context.compiler.connect(context, objects[i], objects, this);
}
for (const compiler of builder._compilers)
compiler.postLink(this);
this.digest();
for (const object of this.objects)
if (object.assignLot)
object.on('change', this.onLotChange, this);
object.on('change', this.onChange, this);
}
,onLotChange() {
this.emit('lot-change');
this.builder.digest(this);
,digest() {
const exprContexts = this.builder._exprContexts;
const exprValues = this.exprValues;
const objects = this.objects;
for (let i = 0; i < exprContexts.length; i++) {
const exprContext = exprContexts[i];
let newValue;
if (exprContext.template) {
const values = [];
let isEmpty = false;
for (expr of exprContext.exprs) {
const value = this.execExpr(expr);
if (value == null) {
isEmpty = true;
break;
}
values.push(value);
}
if (!isEmpty) {
let k = 0;
newValue = exprContext.template.replace(/{{\d+}}/g, function() {
return values[k++];
});
} else
newValue = '';
} else
newValue = this.execExpr(exprContext.expr);
if (newValue !== exprValues[i]) {
const context = exprContext.context;
context.compiler.setProperty(objects[context.id],
exprContext.property, newValue);
exprValues[i] = newValue;
}
}
}
,execExpr(expr) {
try {
return expr.call(this.thisArg, this.$);
// eslint-disable-next-line no-empty
} catch (e) {}
}
,onChange() {
this.emit('change');
this.digest();
}
,getMain: function() {
return this.builder.getMain(this);
return this.objects[this.builder._mainContext];
}
,getById: function(objectId) {
@ -56,7 +127,18 @@ module.exports = new Class({
}
,getByTagName: function(tagName) {
return this.builder.getByTagName(this, tagName);
const tags = this.builder._tags[tagName];
if (tags) {
const arr = new Array(tags.length);
for (let i = 0; i < tags.length; i++)
arr[i] = this.objects[tags[i]];
return arr;
}
return [];
}
,getHtmlId: function(nodeId) {

View File

@ -1,6 +1,6 @@
{
"name": "hedera-web",
"version": "1.407.79",
"version": "1.407.80",
"description": "Verdnatura web page",
"license": "GPL-3.0",
"repository": {

View File

@ -66,7 +66,7 @@ class HtmlService extends Service {
// Setting the version
setcookie('vnVersion', $this->getVersion());
setcookie('vnVersion', $this->getVersion(), ['samesite' => 'Lax']);
// Loading the requested page

View File

@ -124,7 +124,10 @@ abstract class Service {
);
if (isset($row['access'])) {
setcookie('vnVisit', $row['visit'], time() + 31536000); // 1 Year
setcookie('vnVisit', $row['visit'], [
'expires' => time() + 31536000, // 1 Year
'samesite' => 'Lax'
]);
$_SESSION['access'] = $row['access'];
} else
$_SESSION['skipVisit'] = TRUE;