--- a/svghmi/detachable_pages.ysl2 Tue Aug 04 11:53:39 2020 +0200
+++ b/svghmi/detachable_pages.ysl2 Wed Aug 05 18:49:29 2020 +0200
@@ -138,14 +138,14 @@
warning > Page id="«$page/@id»" : No match for path "«$desc/path/@value»" in HMI tree
| page_index: «$desc/path/@index»,
- foreach "$page_relative_widgets" {
- | hmi_widgets["«@id»"]`if "position()!=last()" > ,`
- foreach "$page_managed_widgets[not(@id = $page_relative_widgets/@id)]" {
- | hmi_widgets["«@id»"]`if "position()!=last()" > ,`
+ foreach "$page_managed_widgets" { + const "widget_paths_relativeness" + foreach "func:widget(@id)/path" { + value "func:is_descendant_path(@value, $desc/path/@value)"; + if "position()!=last()" > , + | [hmi_widgets["«@id»"], [«$widget_paths_relativeness»]]`if "position()!=last()" > ,` --- a/svghmi/gen_index_xhtml.xslt Tue Aug 04 11:53:39 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt Wed Aug 05 18:49:29 2020 +0200
@@ -497,26 +497,22 @@
- <xsl:text> relative_widgets: [
- <xsl:for-each select="$page_relative_widgets">
- <xsl:text> hmi_widgets["</xsl:text>
+ <xsl:for-each select="$page_managed_widgets"> + <xsl:variable name="widget_paths_relativeness"> + <xsl:for-each select="func:widget(@id)/path"> + <xsl:value-of select="func:is_descendant_path(@value, $desc/path/@value)"/> + <xsl:if test="position()!=last()"> + <xsl:text> [hmi_widgets["</xsl:text> <xsl:value-of select="@id"/>
- <xsl:text>"]</xsl:text>
- <xsl:if test="position()!=last()">
- <xsl:text> absolute_widgets: [
- <xsl:for-each select="$page_managed_widgets[not(@id = $page_relative_widgets/@id)]">
- <xsl:text> hmi_widgets["</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>"]</xsl:text>
+ <xsl:text>"], [</xsl:text> + <xsl:value-of select="$widget_paths_relativeness"/> + <xsl:text>]]</xsl:text> <xsl:if test="position()!=last()">
@@ -947,33 +943,49 @@
<xsl:text> /* remove subsribers */
- <xsl:text> if(!this.unsubscribable) for(let index of this.indexes){
- <xsl:text> let idx = index + this.offset;
- <xsl:text> subscribers[idx].delete(this);
+ <xsl:text> if(!this.unsubscribable) + <xsl:text> for(let i = 0; i < this.indexes.length; i++) { + <xsl:text> let index = this.indexes[i]; + <xsl:text> if(this.relativeness[i]) + <xsl:text> index += this.offset; + <xsl:text> subscribers[index].delete(this); <xsl:text> this.offset = 0;
+ <xsl:text> this.relativeness = undefined; - <xsl:text> sub(new_offset=0){
- <xsl:text> /* set the offset because relative */
+ <xsl:text> sub(new_offset=0, relativeness){ <xsl:text> this.offset = new_offset;
+ <xsl:text> this.relativeness = relativeness; <xsl:text> /* add this's subsribers */
- <xsl:text> if(!this.unsubscribable) for(let index of this.indexes){
- <xsl:text> subscribers[index + new_offset].add(this);
+ <xsl:text> if(!this.unsubscribable) + <xsl:text> for(let i = 0; i < this.indexes.length; i++) { + <xsl:text> let index = this.indexes[i]; + <xsl:text> if(relativeness[i]) + <xsl:text> index += new_offset; + <xsl:text> subscribers[index].add(this); <xsl:text> need_cache_apply.push(this);
@@ -993,7 +1005,7 @@
<xsl:text> if(cached_val != undefined)
- <xsl:text> dispatch_value_to_widget(this, realindex, cached_val, cached_val);
+ <xsl:text> this.new_hmi_value(realindex, cached_val, cached_val); @@ -1005,7 +1017,7 @@
<xsl:text> let orig = this.indexes[index];
- <xsl:text> return this.offset ? orig + this.offset : orig;
+ <xsl:text> return this.relativeness[index] ? orig + this.offset : orig; @@ -1023,6 +1035,62 @@
+ <xsl:text> new_hmi_value(index, value, oldval) { + <xsl:text> // TODO avoid searching, store index at sub() + <xsl:text> for(let i = 0; i < this.indexes.length; i++) { + <xsl:text> let refindex = this.indexes[i]; + <xsl:text> if(this.relativeness[i]) + <xsl:text> refindex += this.offset; + <xsl:text> if(index == refindex) { + <xsl:text> let d = this.dispatch; + <xsl:text> if(typeof(d) == "function"){ + <xsl:text> d.call(this, value, oldval, i); + <xsl:text> else if(typeof(d) == "object"){ + <xsl:text> d[i].call(this, value, oldval); + <xsl:text> /* else dispatch_0, ..., dispatch_n ? */ + <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); + <xsl:text> } catch(err) { + <xsl:text> console.log(err); @@ -1892,6 +1960,20 @@
<xsl:template mode="widget_defs" match="widget[@type='ForEach']">
<xsl:param name="hmi_element"/>
+ <xsl:if test="count(path) != 1"> + <xsl:message terminate="yes"> + <xsl:text>ForEach widget </xsl:text> + <xsl:value-of select="$hmi_element/@id"/> + <xsl:text> must have one HMI path given.</xsl:text> + <xsl:if test="count(arg) != 1"> + <xsl:message terminate="yes"> + <xsl:text>ForEach widget </xsl:text> + <xsl:value-of select="$hmi_element/@id"/> + <xsl:text> must have one argument given : a class name.</xsl:text> <xsl:variable name="class" select="arg[1]/@value"/>
<xsl:variable name="base_path" select="path/@value"/>
<xsl:variable name="hmi_index_base" select="$indexed_hmitree/*[@hmipath = $base_path]"/>
@@ -1993,7 +2075,9 @@
<xsl:template mode="widget_class" match="widget[@type='ForEach']">
<xsl:text>class ForEachWidget extends Widget{
+ <xsl:text> unsub_items(){ <xsl:text> for(let item of this.items){
@@ -2005,13 +2089,23 @@
+ <xsl:text> this.unsub_items(); <xsl:text> this.offset = 0;
+ <xsl:text> this.relativeness = undefined; - <xsl:text> foreach_widgets_do(todo){
+ <xsl:text> sub_items(){ <xsl:text> for(let i = 0; i < this.items.length; i++) {
@@ -2023,9 +2117,23 @@
<xsl:text> let item_index_offset = item_index - orig_item_index;
+ <xsl:text> if(this.relativeness[0]) + <xsl:text> item_index_offset += this.offset; <xsl:text> for(let widget of item) {
- <xsl:text> todo(widget).call(widget, this.offset + item_index_offset);
+ <xsl:text> /* all variables of all widgets in a ForEach are all relative. + <xsl:text> TODO: allow absolute variables in ForEach widgets + <xsl:text> widget.sub(item_index_offset, widget.indexes.map(_=>true)); @@ -2035,11 +2143,13 @@
- <xsl:text> sub(new_offset=0){
+ <xsl:text> sub(new_offset=0, relativeness=[]){ <xsl:text> this.offset = new_offset;
- <xsl:text> this.foreach_widgets_do(w=>w.sub);
+ <xsl:text> this.relativeness = relativeness; + <xsl:text> this.sub_items(); @@ -2047,7 +2157,7 @@
<xsl:text> apply_cache() {
- <xsl:text> this.foreach_widgets_do(w=>w.apply_cache);
+ <xsl:text> this.items.forEach(item=>item.forEach(widget=>widget.apply_cache())); @@ -2081,9 +2191,9 @@
<xsl:text> this.item_offset = new_item_offset;
- <xsl:text> this.unsub();
- <xsl:text> this.sub(this.offset);
+ <xsl:text> this.unsub_items(); + <xsl:text> this.sub_items(); <xsl:text> update_subscriptions();
@@ -3049,44 +3159,6 @@
- <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
- <xsl:text> let idx = widget.offset ? index - widget.offset : index;
- <xsl:text> let idxidx = widget.indexes.indexOf(idx);
- <xsl:text> let d = widget.dispatch;
- <xsl:text> if(typeof(d) == "function" && idxidx == 0){
- <xsl:text> d.call(widget, value, oldval);
- <xsl:text> else if(typeof(d) == "object" && d.length >= idxidx){
- <xsl:text> d[idxidx].call(widget, value, oldval);
- <xsl:text> /* else dispatch_0, ..., dispatch_n ? */
- <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index);
- <xsl:text> } catch(err) {
- <xsl:text> console.log(err);
<xsl:text>function dispatch_value(index, value) {
@@ -3105,7 +3177,7 @@
<xsl:text> for(let widget of widgets){
- <xsl:text> dispatch_value_to_widget(widget, index, value, oldval);
+ <xsl:text> widget.new_hmi_value(index, value, oldval); @@ -3421,7 +3493,7 @@
<xsl:text> indexes: [heartbeat_index],
- <xsl:text> dispatch: function(value) {
+ <xsl:text> new_hmi_value: function(index, value, oldval) { <xsl:text> apply_hmi_value(heartbeat_index, value+1);
@@ -3687,17 +3759,13 @@
- <xsl:text> old_desc.absolute_widgets.map(w=>w.unsub());
- <xsl:text> old_desc.relative_widgets.map(w=>w.unsub());
+ <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); - <xsl:text> new_desc.absolute_widgets.map(w=>w.sub());
<xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
- <xsl:text> new_desc.relative_widgets.map(w=>w.sub(new_offset));
+ <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness)); --- a/svghmi/svghmi.js Tue Aug 04 11:53:39 2020 +0200
+++ b/svghmi/svghmi.js Wed Aug 05 18:49:29 2020 +0200
@@ -323,12 +323,10 @@
- old_desc.absolute_widgets.map(w=>w.unsub());
- old_desc.relative_widgets.map(w=>w.unsub());
+ old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); - new_desc.absolute_widgets.map(w=>w.sub());
var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
- new_desc.relative_widgets.map(w=>w.sub(new_offset));
+ new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness)); --- a/svghmi/widget_foreach.ysl2 Tue Aug 04 11:53:39 2020 +0200
+++ b/svghmi/widget_foreach.ysl2 Wed Aug 05 18:49:29 2020 +0200
@@ -2,6 +2,9 @@
template "widget[@type='ForEach']", mode="widget_defs" {
+ if "count(path) != 1" error > ForEach widget «$hmi_element/@id» must have one HMI path given. + if "count(arg) != 1" error > ForEach widget «$hmi_element/@id» must have one argument given : a class name. const "class","arg[1]/@value";
const "base_path","path/@value";
@@ -49,34 +52,48 @@
template "widget[@type='ForEach']", mode="widget_class"
class ForEachWidget extends Widget{
for(let item of this.items){
for(let widget of item) {
- foreach_widgets_do(todo){
+ this.relativeness = undefined; for(let i = 0; i < this.items.length; i++) {
let item = this.items[i];
let orig_item_index = this.index_pool[i];
let item_index = this.index_pool[i+this.item_offset];
let item_index_offset = item_index - orig_item_index;
+ if(this.relativeness[0]) + item_index_offset += this.offset; for(let widget of item) {
- todo(widget).call(widget, this.offset + item_index_offset);
+ /* all variables of all widgets in a ForEach are all relative. + TODO: allow absolute variables in ForEach widgets + widget.sub(item_index_offset, widget.indexes.map(_=>true));
+ sub(new_offset=0, relativeness=[]){ this.offset = new_offset;
- this.foreach_widgets_do(w=>w.sub);
+ this.relativeness = relativeness; - this.foreach_widgets_do(w=>w.apply_cache);
+ this.items.forEach(item=>item.forEach(widget=>widget.apply_cache())); @@ -93,8 +110,8 @@
this.item_offset = new_item_offset;
need_cache_apply.push(this);
jumps_need_update = true;
--- a/svghmi/widgets_common.ysl2 Tue Aug 04 11:53:39 2020 +0200
+++ b/svghmi/widgets_common.ysl2 Wed Aug 05 18:49:29 2020 +0200
@@ -78,20 +78,28 @@
- if(!this.unsubscribable) for(let index of this.indexes){
- let idx = index + this.offset;
- subscribers[idx].delete(this);
+ if(!this.unsubscribable) + for(let i = 0; i < this.indexes.length; i++) { + let index = this.indexes[i]; + if(this.relativeness[i]) + subscribers[index].delete(this); + this.relativeness = undefined;
- /* set the offset because relative */
+ sub(new_offset=0, relativeness){ this.offset = new_offset;
+ this.relativeness = relativeness; /* add this's subsribers */
- if(!this.unsubscribable) for(let index of this.indexes){
- subscribers[index + new_offset].add(this);
+ if(!this.unsubscribable) + for(let i = 0; i < this.indexes.length; i++) { + let index = this.indexes[i]; + subscribers[index].add(this); need_cache_apply.push(this);
@@ -107,7 +115,7 @@
let orig = this.indexes[index];
- return this.offset ? orig + this.offset : orig;
+ return this.relativeness[index] ? orig + this.offset : orig; change_hmi_value(index,opstr) {
return change_hmi_value(this.get_idx(index), opstr);