SVGHMI: non significant cosmetic changes svghmi
authorEdouard Tisserant
Mon, 19 Oct 2020 10:15:26 +0200
branchsvghmi
changeset 3065 c369a742443d
parent 3064 4b44d09c48a7
child 3066 d7b9c2ceb3fb
SVGHMI: non significant cosmetic changes
svghmi/gen_index_xhtml.xslt
svghmi/widget_display.ysl2
svghmi/widget_jsontable.ysl2
tests/svghmi/py_ext_0@py_ext/pyfile.xml
--- a/svghmi/gen_index_xhtml.xslt	Mon Oct 19 09:29:11 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt	Mon Oct 19 10:15:26 2020 +0200
@@ -1475,6 +1475,180 @@
       </xsl:otherwise>
     </xsl:choose>
   </func:function>
+  <xsl:template mode="widget_class" match="widget[@type='Animate']">
+    <xsl:text>class AnimateWidget extends Widget{
+</xsl:text>
+    <xsl:text>    frequency = 5;
+</xsl:text>
+    <xsl:text>    speed = 0;
+</xsl:text>
+    <xsl:text>    start = false;
+</xsl:text>
+    <xsl:text>    widget_center = undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    dispatch(value) {
+</xsl:text>
+    <xsl:text>        this.speed = value / 5;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //reconfigure animation
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>       // change animation properties
+</xsl:text>
+    <xsl:text>       for(let child of this.element.children){
+</xsl:text>
+    <xsl:text>            if(child.nodeName.startsWith("animate")){
+</xsl:text>
+    <xsl:text>                if(this.speed != 0 &amp;&amp; !this.start){
+</xsl:text>
+    <xsl:text>                    this.start = true;
+</xsl:text>
+    <xsl:text>                    this.element.beginElement();
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>                if(this.speed &gt; 0){
+</xsl:text>
+    <xsl:text>                    child.setAttribute("dur", this.speed+"s");
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else if(this.speed &lt; 0){
+</xsl:text>
+    <xsl:text>                    child.setAttribute("dur", (-1)*this.speed+"s");
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else{
+</xsl:text>
+    <xsl:text>                    this.start = false;
+</xsl:text>
+    <xsl:text>                    this.element.endElement();
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>       }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        let widget_pos = this.element.getBBox();
+</xsl:text>
+    <xsl:text>        this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)];
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template mode="widget_defs" match="widget[@type='Animate']">
+    <xsl:param name="hmi_element"/>
+    <xsl:text>
+</xsl:text>
+  </xsl:template>
+  <xsl:template mode="widget_class" match="widget[@type='AnimateRotation']">
+    <xsl:text>class AnimateRotationWidget extends Widget{
+</xsl:text>
+    <xsl:text>    frequency = 5;
+</xsl:text>
+    <xsl:text>    speed = 0;
+</xsl:text>
+    <xsl:text>    widget_center = undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    dispatch(value) {
+</xsl:text>
+    <xsl:text>        this.speed = value / 5;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //reconfigure animation
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>       // change animation properties
+</xsl:text>
+    <xsl:text>       for(let child of this.element.children){
+</xsl:text>
+    <xsl:text>            if(child.nodeName == "animateTransform"){
+</xsl:text>
+    <xsl:text>                if(this.speed &gt; 0){
+</xsl:text>
+    <xsl:text>                    child.setAttribute("dur", this.speed+"s");
+</xsl:text>
+    <xsl:text>                    child.setAttribute("from", "0 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                    child.setAttribute("to", "360 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else if(this.speed &lt; 0){
+</xsl:text>
+    <xsl:text>                    child.setAttribute("dur", (-1)*this.speed+"s");
+</xsl:text>
+    <xsl:text>                    child.setAttribute("from", "360 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                    child.setAttribute("to", "0 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else{
+</xsl:text>
+    <xsl:text>                    child.setAttribute("from", "0 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                    child.setAttribute("to", "0 "+this.widget_center[0]+" "+this.widget_center[1]);
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>       }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        let widget_pos = this.element.getBBox();
+</xsl:text>
+    <xsl:text>        this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)];
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template mode="widget_defs" match="widget[@type='AnimateRotation']">
+    <xsl:param name="hmi_element"/>
+    <xsl:text>
+</xsl:text>
+  </xsl:template>
   <xsl:template mode="widget_class" match="widget[@type='Back']">
     <xsl:text>class BackWidget extends Widget{
 </xsl:text>
@@ -1506,7 +1680,9 @@
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
-    <xsl:text>    state = 0;
+    <xsl:text>    state_plc = 0;
+</xsl:text>
+    <xsl:text>    state_hmi = 0;
 </xsl:text>
     <xsl:text>    plc_lock = false;
 </xsl:text>
@@ -1518,89 +1694,113 @@
 </xsl:text>
     <xsl:text>    dispatch(value) {
 </xsl:text>
-    <xsl:text>        if(value){
-</xsl:text>
-    <xsl:text>            this.button_release();
+    <xsl:text>        this.state_plc = value;
+</xsl:text>
+    <xsl:text>        if(this.plc_lock){
+</xsl:text>
+    <xsl:text>            if(this.state_plc == 1){
+</xsl:text>
+    <xsl:text>                this.apply_hmi_value(0, 0);
+</xsl:text>
+    <xsl:text>                this.plc_lock = false;
+</xsl:text>
+    <xsl:text>            }
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>     on_mouse_down(evt) {
-</xsl:text>
-    <xsl:text>         if (this.active_style &amp;&amp; this.inactive_style) {
-</xsl:text>
-    <xsl:text>             this.active_elt.setAttribute("style", this.active_style);
-</xsl:text>
-    <xsl:text>             this.inactive_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>         this.apply_hmi_value(0, 1);
-</xsl:text>
-    <xsl:text>         this.plc_lock = false;
-</xsl:text>
-    <xsl:text>     }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>     on_mouse_up(evt) {
-</xsl:text>
-    <xsl:text>         this.button_release();
-</xsl:text>
-    <xsl:text>     }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>     button_release(){
-</xsl:text>
-    <xsl:text>        if(!this.plc_lock){
-</xsl:text>
-    <xsl:text>            this.plc_lock = true;
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //redraw button
+</xsl:text>
+    <xsl:text>        this.state_hmi = this.state_plc;
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>        if (this.active_style &amp;&amp; this.inactive_style) {
+</xsl:text>
+    <xsl:text>           // redraw button on screen refresh
+</xsl:text>
+    <xsl:text>           if (this.state_hmi) {
+</xsl:text>
+    <xsl:text>               this.active_elt.setAttribute("style", this.active_style);
+</xsl:text>
+    <xsl:text>               this.inactive_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>           } else {
+</xsl:text>
+    <xsl:text>               this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+    <xsl:text>               this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>           }
+</xsl:text>
+    <xsl:text>       }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    on_click(evt) {
+</xsl:text>
+    <xsl:text>        //set state and apply if plc is 0
+</xsl:text>
+    <xsl:text>        this.plc_lock = true;
+</xsl:text>
+    <xsl:text>        if(this.state_plc == 0){
+</xsl:text>
+    <xsl:text>            this.apply_hmi_value(0, 1);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>        else{
-</xsl:text>
-    <xsl:text>            if (this.active_style &amp;&amp; this.inactive_style) {
-</xsl:text>
-    <xsl:text>                 this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>                 this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
-    <xsl:text>             }
-</xsl:text>
-    <xsl:text>             this.apply_hmi_value(0, 0);
+    <xsl:text>        //redraw button
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    on_press(evt) {
+</xsl:text>
+    <xsl:text>        //set graphic
+</xsl:text>
+    <xsl:text>        this.state_hmi = 1;
+</xsl:text>
+    <xsl:text>        //redraw button
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     init() {
+</xsl:text>
+    <xsl:text>        this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
+</xsl:text>
+    <xsl:text>        this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        if (this.active_style &amp;&amp; this.inactive_style) {
+</xsl:text>
+    <xsl:text>            this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>            this.inactive_elt.setAttribute("style", this.inactive_style);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>     }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>     init() {
-</xsl:text>
-    <xsl:text>        this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
-</xsl:text>
-    <xsl:text>        this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        if (this.active_style &amp;&amp; this.inactive_style) {
-</xsl:text>
-    <xsl:text>            this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
-    <xsl:text>        }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        this.element.setAttribute("onmousedown", "hmi_widgets["+this.element_id+"].on_mouse_down(evt)");
-</xsl:text>
-    <xsl:text>        this.element.setAttribute("onmouseup", "hmi_widgets["+this.element_id+"].on_mouse_up(evt)");
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
+</xsl:text>
+    <xsl:text>        this.element.setAttribute("onmousedown", "hmi_widgets['"+this.element_id+"'].on_press(evt)");
 </xsl:text>
     <xsl:text>     }
 </xsl:text>
@@ -1738,7 +1938,7 @@
 </xsl:text>
     <xsl:text>    handle_pos = undefined;
 </xsl:text>
-    <xsl:text>    svg_dist = undefined;
+    <xsl:text>    curr_value = 0;
 </xsl:text>
     <xsl:text>    drag = false;
 </xsl:text>
@@ -1750,13 +1950,47 @@
 </xsl:text>
     <xsl:text>    dispatch(value) {
 </xsl:text>
+    <xsl:text>        let [min,max,start,totallength] = this.range;
+</xsl:text>
+    <xsl:text>        //save current value inside widget
+</xsl:text>
+    <xsl:text>        this.curr_value = value;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //check if in range
+</xsl:text>
+    <xsl:text>        if (this.curr_value &gt; max){
+</xsl:text>
+    <xsl:text>            this.curr_value = max;
+</xsl:text>
+    <xsl:text>            this.apply_hmi_value(0, this.curr_value);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        else if (this.curr_value &lt; min){
+</xsl:text>
+    <xsl:text>            this.curr_value = min;
+</xsl:text>
+    <xsl:text>            this.apply_hmi_value(0, this.curr_value);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>        if(this.value_elt)
 </xsl:text>
     <xsl:text>            this.value_elt.textContent = String(value);
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        this.update_DOM(value, this.handle_elt);
+    <xsl:text>        //don't update if draging and setpoint ghost doesn't exist
+</xsl:text>
+    <xsl:text>        if(!this.drag || (this.setpoint_elt != undefined)){
+</xsl:text>
+    <xsl:text>            this.update_DOM(value, this.handle_elt);
+</xsl:text>
+    <xsl:text>        }
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -1774,6 +2008,8 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>        // show or hide ghost if exists
+</xsl:text>
     <xsl:text>        if(this.setpoint_elt != undefined){
 </xsl:text>
     <xsl:text>            if(this.last_drag!= this.drag){
@@ -1800,6 +2036,8 @@
 </xsl:text>
     <xsl:text>    on_release(evt) {
 </xsl:text>
+    <xsl:text>        //unbind events
+</xsl:text>
     <xsl:text>        window.removeEventListener("touchmove", this.on_bound_drag, true);
 </xsl:text>
     <xsl:text>        window.removeEventListener("mousemove", this.on_bound_drag, true);
@@ -1812,12 +2050,20 @@
 </xsl:text>
     <xsl:text>        window.removeEventListener("touchcancel", this.bound_on_release, true);
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //reset drag flag
+</xsl:text>
     <xsl:text>        if(this.drag){
 </xsl:text>
     <xsl:text>            this.drag = false;
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        // get final position
+</xsl:text>
     <xsl:text>        this.update_position(evt);
 </xsl:text>
     <xsl:text>    }
@@ -1826,10 +2072,14 @@
 </xsl:text>
     <xsl:text>    on_drag(evt){
 </xsl:text>
+    <xsl:text>        //ignore drag event for X amount of time and if not selected
+</xsl:text>
     <xsl:text>        if(this.enTimer &amp;&amp; this.drag){
 </xsl:text>
     <xsl:text>            this.update_position(evt);
 </xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>            //reset timer
 </xsl:text>
     <xsl:text>            this.enTimer = false;
@@ -1930,37 +2180,35 @@
 </xsl:text>
     <xsl:text>            if(fi&lt;fiEnd){
 </xsl:text>
-    <xsl:text>                this.svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
+    <xsl:text>               this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>            else if(fiEnd&lt;fi &amp;&amp; fi&lt;fiEnd+minMax){
 </xsl:text>
-    <xsl:text>                this.svg_dist = this.range[1];
+    <xsl:text>                this.curr_value = this.range[1];
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>            else{
 </xsl:text>
-    <xsl:text>                this.svg_dist = this.range[0];
+    <xsl:text>                this.curr_value = this.range[0];
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            this.apply_hmi_value(0, Math.ceil(this.svg_dist));
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            // update ghost cursor
-</xsl:text>
-    <xsl:text>            if(this.setpoint_elt != undefined){
-</xsl:text>
-    <xsl:text>                this.request_animate();
-</xsl:text>
-    <xsl:text>            }
+    <xsl:text>            //apply value to hmi
+</xsl:text>
+    <xsl:text>            this.apply_hmi_value(0, Math.ceil(this.curr_value));
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //redraw handle
+</xsl:text>
+    <xsl:text>            this.request_animate();
+</xsl:text>
+    <xsl:text>
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -1972,7 +2220,21 @@
 </xsl:text>
     <xsl:text>    animate(){
 </xsl:text>
-    <xsl:text>        this.update_DOM(this.svg_dist, this.setpoint_elt);
+    <xsl:text>        // redraw handle on screen refresh
+</xsl:text>
+    <xsl:text>        // check if setpoint(ghost) handle exsist otherwise update main handle
+</xsl:text>
+    <xsl:text>        if(this.setpoint_elt != undefined){
+</xsl:text>
+    <xsl:text>            this.update_DOM(this.curr_value, this.setpoint_elt);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        else{
+</xsl:text>
+    <xsl:text>            this.update_DOM(this.curr_value, this.handle_elt);
+</xsl:text>
+    <xsl:text>        }
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -1980,24 +2242,40 @@
 </xsl:text>
     <xsl:text>    on_select(evt){
 </xsl:text>
+    <xsl:text>        //enable drag flag and timer
+</xsl:text>
     <xsl:text>        this.drag = true;
 </xsl:text>
     <xsl:text>        this.enTimer = true;
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //bind events
+</xsl:text>
     <xsl:text>        window.addEventListener("touchmove", this.on_bound_drag, true);
 </xsl:text>
     <xsl:text>        window.addEventListener("mousemove", this.on_bound_drag, true);
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        window.addEventListener("mouseup", this.bound_on_release, true)
+    <xsl:text>        window.addEventListener("mouseup", this.bound_on_release, true);
 </xsl:text>
     <xsl:text>        window.addEventListener("touchend", this.bound_on_release, true);
 </xsl:text>
     <xsl:text>        window.addEventListener("touchcancel", this.bound_on_release, true);
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //update postion on mouse press
+</xsl:text>
     <xsl:text>        this.update_position(evt);
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //prevent next events
+</xsl:text>
+    <xsl:text>        evt.stopPropagation();
+</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>
@@ -2074,13 +2352,21 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        //init events
+    <xsl:text>        this.handle_elt.addEventListener("mousedown", this.bound_on_select);
 </xsl:text>
     <xsl:text>        this.element.addEventListener("mousedown", this.bound_on_select);
 </xsl:text>
     <xsl:text>        this.element.addEventListener("touchstart", this.bound_on_select);
 </xsl:text>
-    <xsl:text>
+    <xsl:text>        //touch recognised as page drag without next command
+</xsl:text>
+    <xsl:text>        document.body.addEventListener("touchstart", function(e){}, false);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //save ghost style
+</xsl:text>
+    <xsl:text>        //save ghost style
 </xsl:text>
     <xsl:text>        if(this.setpoint_elt != undefined){
 </xsl:text>
@@ -2092,22 +2378,6 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
-</xsl:text>
-    <xsl:text>        window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this))
-</xsl:text>
-    <xsl:text>        window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this));
-</xsl:text>
-    <xsl:text>        window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this));
-</xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>}
@@ -2124,13 +2394,66 @@
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
       <xsl:with-param name="labels">
-        <xsl:text>value min max</xsl:text>
+        <xsl:text>value min max setpoint</xsl:text>
       </xsl:with-param>
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
     <xsl:text>
 </xsl:text>
   </xsl:template>
+  <xsl:template mode="widget_class" match="widget[@type='CustomHtml']">
+    <xsl:text>class CustomHtmlWidget extends Widget{
+</xsl:text>
+    <xsl:text>    frequency = 5;
+</xsl:text>
+    <xsl:text>    widget_size = undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    dispatch(value) {
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        this.widget_size = this.container_elt.getBBox();
+</xsl:text>
+    <xsl:text>        this.element.innerHTML ='&lt;foreignObject x="'+
+</xsl:text>
+    <xsl:text>            this.widget_size.x+'" y="'+this.widget_size.y+
+</xsl:text>
+    <xsl:text>            '" width="'+this.widget_size.width+'" height="'+this.widget_size.height+'"&gt; '+
+</xsl:text>
+    <xsl:text>            this.code_elt.textContent+
+</xsl:text>
+    <xsl:text>            ' &lt;/foreignObject&gt;';
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template mode="widget_defs" match="widget[@type='CustomHtml']">
+    <xsl:param name="hmi_element"/>
+    <xsl:call-template name="defs_by_labels">
+      <xsl:with-param name="hmi_element" select="$hmi_element"/>
+      <xsl:with-param name="labels">
+        <xsl:text>container code</xsl:text>
+      </xsl:with-param>
+    </xsl:call-template>
+    <xsl:text>
+</xsl:text>
+  </xsl:template>
   <xsl:template mode="widget_class" match="widget[@type='Display']">
     <xsl:text>class DisplayWidget extends Widget{
 </xsl:text>
@@ -2162,7 +2485,9 @@
           <xsl:when test="@type='HMI_STRING'">
             <xsl:text>""</xsl:text>
           </xsl:when>
-          <xsl:otherwise>0</xsl:otherwise>
+          <xsl:otherwise>
+            <xsl:text>0</xsl:text>
+          </xsl:otherwise>
         </xsl:choose>
         <xsl:if test="position()!=last()">
           <xsl:text>,</xsl:text>
@@ -3501,7 +3826,7 @@
   <xsl:template mode="widget_class" match="widget[@type='JsonTable']">
     <xsl:text>class JsonTableWidget extends Widget{
 </xsl:text>
-    <xsl:text>    cache = [];
+    <xsl:text>    cache = [100,50];
 </xsl:text>
     <xsl:text>    do_http_request(...opt) {
 </xsl:text>
@@ -3509,7 +3834,9 @@
 </xsl:text>
     <xsl:text>            args: this.args,
 </xsl:text>
-    <xsl:text>            vars: this.cache,
+    <xsl:text>            range: this.cache[1],
+</xsl:text>
+    <xsl:text>            position: this.cache[2],
 </xsl:text>
     <xsl:text>            visible: this.visible,
 </xsl:text>
@@ -3848,6 +4175,8 @@
 </xsl:text>
     <xsl:text>        this.apply_hmi_value(2, position);
 </xsl:text>
+    <xsl:text>        this.apply_hmi_value(3, this.visible);
+</xsl:text>
     <xsl:text>        console.log(range,position,jdata);
 </xsl:text>
     <xsl:apply-templates mode="json_table_render_except_comments" select="$data_elt">
@@ -4756,7 +5085,11 @@
 </xsl:text>
     <xsl:text>    handle_orig = undefined;
 </xsl:text>
-    <xsl:text>    scroll_size = 10;
+    <xsl:text>    scroll_size = undefined;
+</xsl:text>
+    <xsl:text>    scroll_range = 0;
+</xsl:text>
+    <xsl:text>    scroll_visible = 7;
 </xsl:text>
     <xsl:text>    min_size = 0.07;
 </xsl:text>
@@ -4774,17 +5107,59 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    dispatch(value) {
-</xsl:text>
-    <xsl:text>        //save current value inside widget
-</xsl:text>
-    <xsl:text>        this.curr_value = value;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        if(this.value_elt)
-</xsl:text>
-    <xsl:text>            this.value_elt.textContent = String(value);
+    <xsl:text>    dispatch(value,oldval, index) {
+</xsl:text>
+    <xsl:text>        if (index == 0){
+</xsl:text>
+    <xsl:text>            let [min,max,start,totallength] = this.range;
+</xsl:text>
+    <xsl:text>            //save current value inside widget
+</xsl:text>
+    <xsl:text>            this.curr_value = value;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //check if in range
+</xsl:text>
+    <xsl:text>            if (this.curr_value &gt; max){
+</xsl:text>
+    <xsl:text>                this.curr_value = max;
+</xsl:text>
+    <xsl:text>                this.apply_hmi_value(0, this.curr_value);
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            else if (this.curr_value &lt; min){
+</xsl:text>
+    <xsl:text>                this.curr_value = min;
+</xsl:text>
+    <xsl:text>                this.apply_hmi_value(0, this.curr_value);
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            if(this.value_elt)
+</xsl:text>
+    <xsl:text>                this.value_elt.textContent = String(value);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        else if(index == 1){
+</xsl:text>
+    <xsl:text>            this.scroll_range = value;
+</xsl:text>
+    <xsl:text>            this.set_scroll();
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        else if(index == 2){
+</xsl:text>
+    <xsl:text>            this.scroll_visible = value;
+</xsl:text>
+    <xsl:text>            this.set_scroll();
+</xsl:text>
+    <xsl:text>        }
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -4792,7 +5167,7 @@
 </xsl:text>
     <xsl:text>        if(!this.drag || (this.setpoint_elt != undefined)){
 </xsl:text>
-    <xsl:text>            this.update_DOM(value, this.handle_elt);
+    <xsl:text>            this.update_DOM(this.curr_value, this.handle_elt);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -4800,6 +5175,34 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>    set_scroll(){
+</xsl:text>
+    <xsl:text>        //check if range is bigger than visible and set scroll size
+</xsl:text>
+    <xsl:text>        if(this.scroll_range &gt; this.scroll_visible){
+</xsl:text>
+    <xsl:text>            this.scroll_size = this.scroll_range - this.scroll_visible;
+</xsl:text>
+    <xsl:text>            this.range[0] = 0;
+</xsl:text>
+    <xsl:text>            this.range[1] = this.scroll_size;
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        else{
+</xsl:text>
+    <xsl:text>            this.scroll_size = 1;
+</xsl:text>
+    <xsl:text>            this.range[0] = 0;
+</xsl:text>
+    <xsl:text>            this.range[1] = 1;
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>    update_DOM(value, elt){
 </xsl:text>
     <xsl:text>        let [min,max,start,totallength] = this.range;
@@ -4946,7 +5349,7 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        window.removeEventListener("mouseup", this.bound_on_release, true)
+    <xsl:text>        window.removeEventListener("mouseup", this.bound_on_release, true);
 </xsl:text>
     <xsl:text>        window.removeEventListener("touchend", this.bound_on_release, true);
 </xsl:text>
@@ -5138,7 +5541,7 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        //check if in range
+    <xsl:text>        //check if in range and apply
 </xsl:text>
     <xsl:text>        if (this.curr_value &gt; max){
 </xsl:text>
@@ -5152,8 +5555,6 @@
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>        this.apply_hmi_value(0, this.curr_value);
 </xsl:text>
     <xsl:text>
@@ -5208,7 +5609,7 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>        window.addEventListener("mouseup", this.bound_on_release, true)
+    <xsl:text>        window.addEventListener("mouseup", this.bound_on_release, true);
 </xsl:text>
     <xsl:text>        window.addEventListener("touchend", this.bound_on_release, true);
 </xsl:text>
@@ -5264,7 +5665,11 @@
 </xsl:text>
     <xsl:text>        evt.stopPropagation();
 </xsl:text>
-    <xsl:text>    }
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -5286,6 +5691,8 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>        // save initial parameters
 </xsl:text>
     <xsl:text>        this.range_elt.style.strokeMiterlimit="0";
@@ -5318,7 +5725,13 @@
 </xsl:text>
     <xsl:text>        this.element.addEventListener("touchstart", this.bound_on_select);
 </xsl:text>
-    <xsl:text>
+    <xsl:text>        //touch recognised as page drag without next command
+</xsl:text>
+    <xsl:text>        document.body.addEventListener("touchstart", function(e){}, false);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //save ghost style
 </xsl:text>
     <xsl:text>        if(this.setpoint_elt != undefined){
 </xsl:text>
@@ -5425,68 +5838,84 @@
 </xsl:text>
     <xsl:text>    dispatch(value) {
 </xsl:text>
-    <xsl:text>        if(this.state != value){
-</xsl:text>
-    <xsl:text>            this.state = value;
-</xsl:text>
-    <xsl:text>            if (this.state) {
-</xsl:text>
-    <xsl:text>                this.active_elt.setAttribute("style", this.active_style);
-</xsl:text>
-    <xsl:text>                this.inactive_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            } else {
-</xsl:text>
-    <xsl:text>                this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
-    <xsl:text>                this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            }
+    <xsl:text>        this.state = value;
+</xsl:text>
+    <xsl:text>        //redraw toggle button
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    on_click(evt) {
+</xsl:text>
+    <xsl:text>        //toggle state and apply
+</xsl:text>
+    <xsl:text>        if (this.state) {
+</xsl:text>
+    <xsl:text>            this.state = 0;
+</xsl:text>
+    <xsl:text>        } else {
+</xsl:text>
+    <xsl:text>            this.state = 1;
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    on_click(evt) {
-</xsl:text>
-    <xsl:text>        if (this.state) {
+    <xsl:text>        this.apply_hmi_value(0, this.state);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //redraw toggle button
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>       // redraw toggle button on screen refresh
+</xsl:text>
+    <xsl:text>       if (this.state) {
+</xsl:text>
+    <xsl:text>           this.active_elt.setAttribute("style", this.active_style);
+</xsl:text>
+    <xsl:text>           this.inactive_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>       } else {
+</xsl:text>
+    <xsl:text>           this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+    <xsl:text>           this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>       }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
+</xsl:text>
+    <xsl:text>        this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        if (this.active_style &amp;&amp; this.inactive_style) {
+</xsl:text>
+    <xsl:text>            this.active_elt.setAttribute("style", "display:none");
 </xsl:text>
     <xsl:text>            this.inactive_elt.setAttribute("style", this.inactive_style);
 </xsl:text>
-    <xsl:text>            this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            this.state = 0;
-</xsl:text>
-    <xsl:text>        } else {
-</xsl:text>
-    <xsl:text>            this.active_elt.setAttribute("style", this.active_style);
-</xsl:text>
-    <xsl:text>            this.inactive_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            this.state = 1;
-</xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>        this.apply_hmi_value(0, this.state);
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    init() {
-</xsl:text>
-    <xsl:text>        this.active_style = this.active_elt.style.cssText;
-</xsl:text>
-    <xsl:text>        this.inactive_style = this.inactive_elt.style.cssText;
+    <xsl:text>
 </xsl:text>
     <xsl:text>        this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
 </xsl:text>
-    <xsl:text>        this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
-    <xsl:text>        this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>}
@@ -5499,6 +5928,7 @@
       <xsl:with-param name="labels">
         <xsl:text>active inactive</xsl:text>
       </xsl:with-param>
+      <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
     <xsl:text>
 </xsl:text>
--- a/svghmi/widget_display.ysl2	Mon Oct 19 09:29:11 2020 +0200
+++ b/svghmi/widget_display.ysl2	Mon Oct 19 10:15:26 2020 +0200
@@ -20,7 +20,7 @@
     const "field_initializer" foreach "path" {
         choose{
             when "@type='HMI_STRING'" > ""
-            otherwise 0
+            otherwise > 0
         }
         if "position()!=last()" > ,
     }
--- a/svghmi/widget_jsontable.ysl2	Mon Oct 19 09:29:11 2020 +0200
+++ b/svghmi/widget_jsontable.ysl2	Mon Oct 19 10:15:26 2020 +0200
@@ -3,11 +3,12 @@
 template "widget[@type='JsonTable']", mode="widget_class"
     ||
     class JsonTableWidget extends Widget{
-        cache = [];
+        cache = [100,50];
         do_http_request(...opt) {
             const query = {
                 args: this.args,
-                vars: this.cache,
+                range: this.cache[1],
+                position: this.cache[2],
                 visible: this.visible,
                 options: opt
             };
--- a/tests/svghmi/py_ext_0@py_ext/pyfile.xml	Mon Oct 19 09:29:11 2020 +0200
+++ b/tests/svghmi/py_ext_0@py_ext/pyfile.xml	Mon Oct 19 10:15:26 2020 +0200
@@ -30,8 +30,9 @@
     def render_POST(self, request):
         newstr = request.content.getvalue()
         newdata = json.loads(newstr)
-        vars = newdata[u'vars']
         args = newdata[u'args']
+        range_feedback = newdata[u'range']
+        slider_position = newdata[u'position']
         visible = newdata[u'visible']
         options = newdata[u'options']
 
@@ -40,14 +41,11 @@
             if action == "onClick[acknowledge]":
                 AlarmIndex[int(alarmid)][2] = "ack"
 
-        svars = (vars + [0,0])[:3]
-        range_feedback = svars[1]
-        slider_position = svars[2]
-        answer = self.renderTable(range_feedback, slider_position, visible, *(args+svars[3:]))
+        answer = self.renderTable(range_feedback, slider_position, visible)
         janswer = json.dumps(answer)
         return janswer
 
-    def renderTable(self, old_range, old_position, visible, *options):
+    def renderTable(self, old_range, old_position, visible):
         new_range = len(Alarms)
         delta = new_range - visible
         new_position = 0 if delta <= 0 else delta if old_position > delta else old_position