Merged svghmi
authorEdouard Tisserant
Fri, 28 Aug 2020 11:31:18 +0200
branchsvghmi
changeset 3047 c113904f0e62
parent 3046 d1bb0c09e412 (current diff)
parent 3045 f6d428330e04 (diff)
child 3048 d46d545ff7b7
Merged
svghmi/gen_index_xhtml.xslt
svghmi/widget_keypad.ysl2
--- a/svghmi/gen_index_xhtml.xslt	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt	Fri Aug 28 11:31:18 2020 +0200
@@ -1583,10 +1583,98 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
+  <xsl:template mode="widget_class" match="widget[@type='CircularBar']">
+    <xsl:text>class CircularBarWidget extends Widget{
+</xsl:text>
+    <xsl:text>    frequency = 10;
+</xsl:text>
+    <xsl:text>    range = undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    dispatch(value) {
+</xsl:text>
+    <xsl:text>        if(this.value_elt)
+</xsl:text>
+    <xsl:text>            this.value_elt.textContent = String(value);
+</xsl:text>
+    <xsl:text>        let [min,max,start,end] = this.range;
+</xsl:text>
+    <xsl:text>        let [cx,cy] = this.center;
+</xsl:text>
+    <xsl:text>        let [rx,ry] = this.proportions;
+</xsl:text>
+    <xsl:text>        let tip = start + (end-start)*Number(value)/(max-min);
+</xsl:text>
+    <xsl:text>        let size = 0;
+</xsl:text>
+    <xsl:text>        if (tip-start &gt; Math.PI) {
+</xsl:text>
+    <xsl:text>            size = 1;
+</xsl:text>
+    <xsl:text>        } else {
+</xsl:text>
+    <xsl:text>            size = 0;
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        this.path_elt.setAttribute('d', "M "+(cx+rx*Math.cos(start))+","+(cy+ry*Math.sin(start))+" A "+rx+","+ry+" 0 "+size+" 1 "+(cx+rx*Math.cos(tip))+","+(cy+ry*Math.sin(tip)));
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        let start = Number(this.path_elt.getAttribute('sodipodi:start'));
+</xsl:text>
+    <xsl:text>        let end = Number(this.path_elt.getAttribute('sodipodi:end'));
+</xsl:text>
+    <xsl:text>        let cx = Number(this.path_elt.getAttribute('sodipodi:cx'));
+</xsl:text>
+    <xsl:text>        let cy = Number(this.path_elt.getAttribute('sodipodi:cy'));
+</xsl:text>
+    <xsl:text>        let rx = Number(this.path_elt.getAttribute('sodipodi:rx'));
+</xsl:text>
+    <xsl:text>        let ry = Number(this.path_elt.getAttribute('sodipodi:ry'));
+</xsl:text>
+    <xsl:text>        if (ry == 0) {
+</xsl:text>
+    <xsl:text>            ry = rx;
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        if (start &gt; end) {
+</xsl:text>
+    <xsl:text>            end = end + 2*Math.PI;
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        let min = this.min_elt ?
+</xsl:text>
+    <xsl:text>                  Number(this.min_elt.textContent) :
+</xsl:text>
+    <xsl:text>                  this.args.length &gt;= 1 ? this.args[0] : 0;
+</xsl:text>
+    <xsl:text>        let max = this.max_elt ?
+</xsl:text>
+    <xsl:text>                  Number(this.max_elt.textContent) :
+</xsl:text>
+    <xsl:text>                  this.args.length &gt;= 2 ? this.args[1] : 100;
+</xsl:text>
+    <xsl:text>        this.range = [min, max, start, end];
+</xsl:text>
+    <xsl:text>        this.center = [cx, cy];
+</xsl:text>
+    <xsl:text>        this.proportions = [rx, ry];
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
   <xsl:template mode="widget_defs" match="widget[@type='CircularBar']">
     <xsl:param name="hmi_element"/>
-    <xsl:text>frequency: 10,
-</xsl:text>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
       <xsl:with-param name="labels">
@@ -1600,83 +1688,7 @@
       </xsl:with-param>
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
-    <xsl:text>dispatch: function(value) {
-</xsl:text>
-    <xsl:text>    if(this.value_elt)
-</xsl:text>
-    <xsl:text>        this.value_elt.textContent = String(value);
-</xsl:text>
-    <xsl:text>    let [min,max,start,end] = this.range;
-</xsl:text>
-    <xsl:text>    let [cx,cy] = this.center;
-</xsl:text>
-    <xsl:text>    let [rx,ry] = this.proportions;
-</xsl:text>
-    <xsl:text>    let tip = start + (end-start)*Number(value)/(max-min);
-</xsl:text>
-    <xsl:text>    let size = 0;
-</xsl:text>
-    <xsl:text>    if (tip-start &gt; Math.PI) {
-</xsl:text>
-    <xsl:text>        size = 1;
-</xsl:text>
-    <xsl:text>    } else {
-</xsl:text>
-    <xsl:text>        size = 0;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    this.path_elt.setAttribute('d', "M "+(cx+rx*Math.cos(start))+","+(cy+ry*Math.sin(start))+" A "+rx+","+ry+" 0 "+size+" 1 "+(cx+rx*Math.cos(tip))+","+(cy+ry*Math.sin(tip)));
-</xsl:text>
-    <xsl:text>},
-</xsl:text>
-    <xsl:text>range: undefined,
-</xsl:text>
-    <xsl:text>init: function() {
-</xsl:text>
-    <xsl:text>    let start = Number(this.path_elt.getAttribute('sodipodi:start'));
-</xsl:text>
-    <xsl:text>    let end = Number(this.path_elt.getAttribute('sodipodi:end'));
-</xsl:text>
-    <xsl:text>    let cx = Number(this.path_elt.getAttribute('sodipodi:cx'));
-</xsl:text>
-    <xsl:text>    let cy = Number(this.path_elt.getAttribute('sodipodi:cy'));
-</xsl:text>
-    <xsl:text>    let rx = Number(this.path_elt.getAttribute('sodipodi:rx'));
-</xsl:text>
-    <xsl:text>    let ry = Number(this.path_elt.getAttribute('sodipodi:ry'));
-</xsl:text>
-    <xsl:text>    if (ry == 0) {
-</xsl:text>
-    <xsl:text>        ry = rx;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    if (start &gt; end) {
-</xsl:text>
-    <xsl:text>        end = end + 2*Math.PI;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    let min = this.min_elt ?
-</xsl:text>
-    <xsl:text>              Number(this.min_elt.textContent) :
-</xsl:text>
-    <xsl:text>              this.args.length &gt;= 1 ? this.args[0] : 0;
-</xsl:text>
-    <xsl:text>    let max = this.max_elt ?
-</xsl:text>
-    <xsl:text>              Number(this.max_elt.textContent) :
-</xsl:text>
-    <xsl:text>              this.args.length &gt;= 2 ? this.args[1] : 100;
-</xsl:text>
-    <xsl:text>    this.range = [min, max, start, end];
-</xsl:text>
-    <xsl:text>    this.center = [cx, cy];
-</xsl:text>
-    <xsl:text>    this.proportions = [rx, ry];
-</xsl:text>
-    <xsl:text>},
+    <xsl:text>
 </xsl:text>
   </xsl:template>
   <xsl:template mode="widget_class" match="widget[@type='CircularSlider']">
@@ -1690,23 +1702,59 @@
 </xsl:text>
     <xsl:text>    handle_pos = undefined;
 </xsl:text>
+    <xsl:text>    svg_dist = undefined;
+</xsl:text>
     <xsl:text>    drag = false;
 </xsl:text>
     <xsl:text>    enTimer = false;
 </xsl:text>
+    <xsl:text>    last_drag = false;
+</xsl:text>
     <xsl:text>
 </xsl:text>
     <xsl:text>    dispatch(value) {
 </xsl:text>
-    <xsl:text>        if(!this.drag){
-</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.handle_position(value);
+    <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>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    update_DOM(value, elt){
+</xsl:text>
+    <xsl:text>        let [min,max,totalDistance] = this.range;
+</xsl:text>
+    <xsl:text>        let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
+</xsl:text>
+    <xsl:text>        let tip = this.range_elt.getPointAtLength(length);
+</xsl:text>
+    <xsl:text>        elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        if(this.setpoint_elt != undefined){
+</xsl:text>
+    <xsl:text>            if(this.last_drag!= this.drag){
+</xsl:text>
+    <xsl:text>                if(this.drag){
+</xsl:text>
+    <xsl:text>                    this.setpoint_elt.setAttribute("style", this.setpoint_style);
+</xsl:text>
+    <xsl:text>                }else{
+</xsl:text>
+    <xsl:text>                    this.setpoint_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                this.last_drag = this.drag;
+</xsl:text>
+    <xsl:text>            }
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -1714,28 +1762,46 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    handle_position(value){
-</xsl:text>
-    <xsl:text>        let [min,max,totalDistance] = this.range;
-</xsl:text>
-    <xsl:text>        let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
-</xsl:text>
-    <xsl:text>        let tip = this.range_elt.getPointAtLength(length);
-</xsl:text>
-    <xsl:text>        this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>    on_release(evt) {
 </xsl:text>
+    <xsl:text>        window.removeEventListener("touchmove", this.on_bound_drag, true);
+</xsl:text>
+    <xsl:text>        window.removeEventListener("mousemove", this.on_bound_drag, true);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        window.removeEventListener("mouseup", this.bound_on_release, true)
+</xsl:text>
+    <xsl:text>        window.removeEventListener("touchend", this.bound_on_release, true);
+</xsl:text>
+    <xsl:text>        window.removeEventListener("touchcancel", this.bound_on_release, true);
+</xsl:text>
     <xsl:text>        if(this.drag){
 </xsl:text>
     <xsl:text>            this.drag = false;
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
+    <xsl:text>        this.update_position(evt);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    on_drag(evt){
+</xsl:text>
+    <xsl:text>        if(this.enTimer &amp;&amp; this.drag){
+</xsl:text>
+    <xsl:text>            this.update_position(evt);
+</xsl:text>
+    <xsl:text>            //reset timer
+</xsl:text>
+    <xsl:text>            this.enTimer = false;
+</xsl:text>
+    <xsl:text>            setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>
@@ -1828,41 +1894,37 @@
 </xsl:text>
     <xsl:text>            if(fi&lt;fiEnd){
 </xsl:text>
-    <xsl:text>                svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
+    <xsl:text>                this.svg_dist=(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>                svg_dist = this.range[1];
+    <xsl:text>                this.svg_dist = this.range[1];
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>            else{
 </xsl:text>
-    <xsl:text>                svg_dist = this.range[0];
+    <xsl:text>                this.svg_dist = this.range[0];
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>            //redraw handle --TODO is it fast enough if I just call change_hmi_value???
-</xsl:text>
-    <xsl:text>            this.handle_position(svg_dist);
-</xsl:text>
-    <xsl:text>            if(this.value_elt)
-</xsl:text>
-    <xsl:text>                this.value_elt.textContent = String(Math.ceil(svg_dist));
-</xsl:text>
-    <xsl:text>            this.apply_hmi_value(0, Math.ceil(svg_dist));
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            //reset timer
-</xsl:text>
-    <xsl:text>            this.enTimer = false;
-</xsl:text>
-    <xsl:text>            setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
+    <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>
     <xsl:text>        }
 </xsl:text>
@@ -1872,12 +1934,32 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>    animate(){
+</xsl:text>
+    <xsl:text>        this.update_DOM(this.svg_dist, this.setpoint_elt);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>    on_select(evt){
 </xsl:text>
     <xsl:text>        this.drag = true;
 </xsl:text>
     <xsl:text>        this.enTimer = true;
 </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>
+    <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>        this.update_position(evt);
 </xsl:text>
     <xsl:text>    }
@@ -1946,13 +2028,33 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>        //bind functions
+</xsl:text>
+    <xsl:text>        this.bound_on_select = this.on_select.bind(this);
+</xsl:text>
+    <xsl:text>        this.bound_on_release = this.on_release.bind(this);
+</xsl:text>
+    <xsl:text>        this.on_bound_drag = this.on_drag.bind(this);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>        //init events
 </xsl:text>
-    <xsl:text>        this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
-</xsl:text>
-    <xsl:text>        this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
-</xsl:text>
-    <xsl:text>        this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
+    <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>
+    <xsl:text>        if(this.setpoint_elt != undefined){
+</xsl:text>
+    <xsl:text>            this.setpoint_style = this.setpoint_elt.getAttribute("style");
+</xsl:text>
+    <xsl:text>            this.setpoint_elt.setAttribute("style", "display:none");
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -3973,7 +4075,7 @@
 </xsl:text>
     <xsl:text>     moving = undefined;
 </xsl:text>
-    <xsl:text>     enTimer = undefined;
+    <xsl:text>     click = undefined;
 </xsl:text>
     <xsl:text>     offset = undefined;
 </xsl:text>
@@ -3983,7 +4085,21 @@
 </xsl:text>
     <xsl:text>         this.moving = true;
 </xsl:text>
-    <xsl:text>         this.enTimer = true;
+    <xsl:text>
+</xsl:text>
+    <xsl:text>         // chatch window events
+</xsl:text>
+    <xsl:text>         window.addEventListener("touchmove", this.bound_on_drag, true);
+</xsl:text>
+    <xsl:text>         window.addEventListener("mousemove", this.bound_on_drag, true);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <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>
@@ -4017,7 +4133,23 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>     off_position_click(evt) {
+    <xsl:text>     on_release(evt) {
+</xsl:text>
+    <xsl:text>        //relase binds
+</xsl:text>
+    <xsl:text>        window.removeEventListener("touchmove", this.bound_on_drag, true);
+</xsl:text>
+    <xsl:text>        window.removeEventListener("mousemove", this.bound_on_drag, true);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        window.removeEventListener("mouseup", this.bound_on_release, true)
+</xsl:text>
+    <xsl:text>        window.removeEventListener("touchend", this.bound_on_release, true);
+</xsl:text>
+    <xsl:text>        window.removeEventListener("touchcancel", this.bound_on_release, true);
+</xsl:text>
+    <xsl:text>
 </xsl:text>
     <xsl:text>        if(this.moving)
 </xsl:text>
@@ -4027,63 +4159,65 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>     on_move(evt) {
-</xsl:text>
-    <xsl:text>         if(this.moving &amp;&amp; this.enTimer){
-</xsl:text>
-    <xsl:text>             //get keyboard pos in html
-</xsl:text>
-    <xsl:text>             let [eltid, tmpgrp] = current_modal;
-</xsl:text>
-    <xsl:text>             let [xcoord,ycoord] = this.coordinates;
-</xsl:text>
-    <xsl:text>             let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>             //get mouse coordinates
-</xsl:text>
-    <xsl:text>             var clickX = undefined;
-</xsl:text>
-    <xsl:text>             var clickY = undefined;
-</xsl:text>
-    <xsl:text>             if (evt.type == "touchmove"){
-</xsl:text>
-    <xsl:text>                 clickX = Math.ceil(evt.touches[0].clientX);
-</xsl:text>
-    <xsl:text>                 clickY = Math.ceil(evt.touches[0].clientY);
-</xsl:text>
-    <xsl:text>             }
-</xsl:text>
-    <xsl:text>             else{
-</xsl:text>
-    <xsl:text>                 clickX = evt.pageX;
-</xsl:text>
-    <xsl:text>                 clickY = evt.pageY;
-</xsl:text>
-    <xsl:text>             }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>             //translate keyboard position
-</xsl:text>
-    <xsl:text>             let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth;
-</xsl:text>
-    <xsl:text>             let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight;
-</xsl:text>
-    <xsl:text>             tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")");
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>             //reset timer
-</xsl:text>
-    <xsl:text>             this.enTimer = false;
-</xsl:text>
-    <xsl:text>             setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
+    <xsl:text>     on_drag(evt) {
+</xsl:text>
+    <xsl:text>         if(this.moving)
+</xsl:text>
+    <xsl:text>            //get mouse coordinates
+</xsl:text>
+    <xsl:text>            var clickX = undefined;
+</xsl:text>
+    <xsl:text>            var clickY = undefined;
+</xsl:text>
+    <xsl:text>            if (evt.type == "touchmove"){
+</xsl:text>
+    <xsl:text>                clickX = Math.ceil(evt.touches[0].clientX);
+</xsl:text>
+    <xsl:text>                clickY = Math.ceil(evt.touches[0].clientY);
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            else{
+</xsl:text>
+    <xsl:text>                clickX = evt.pageX;
+</xsl:text>
+    <xsl:text>                clickY = evt.pageY;
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            this.click = [clickX,clickY]
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //requeset redraw
+</xsl:text>
+    <xsl:text>            this.request_animate();
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     animate(){
+</xsl:text>
+    <xsl:text>        //get keyboard pos in html
+</xsl:text>
+    <xsl:text>        let [eltid, tmpgrp] = current_modal;
+</xsl:text>
+    <xsl:text>        let [xcoord,ycoord] = this.coordinates;
+</xsl:text>
+    <xsl:text>        let [clickX,clickY] = this.click;
+</xsl:text>
+    <xsl:text>        let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        //translate keyboard position
+</xsl:text>
+    <xsl:text>        let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth;
+</xsl:text>
+    <xsl:text>        let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight;
+</xsl:text>
+    <xsl:text>        tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")");
 </xsl:text>
     <xsl:text>     }
 </xsl:text>
@@ -4329,20 +4463,16 @@
     </xsl:for-each>
     <xsl:text>        if(this.position_elt){
 </xsl:text>
+    <xsl:text>           this.bound_on_release = this.on_release.bind(this);
+</xsl:text>
+    <xsl:text>           this.bound_on_drag = this.on_drag.bind(this);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>           this.position_elt.setAttribute("onmousedown", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)");
 </xsl:text>
     <xsl:text>           this.position_elt.setAttribute("ontouchstart", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)");
 </xsl:text>
-    <xsl:text>           window.addEventListener("mouseup", hmi_widgets[this.element_id].off_position_click.bind(this));
-</xsl:text>
-    <xsl:text>           window.addEventListener("touchend", hmi_widgets[this.element_id].off_position_click.bind(this));
-</xsl:text>
-    <xsl:text>           window.addEventListener("touchcancel", hmi_widgets[this.element_id].off_position_click.bind(this));
-</xsl:text>
-    <xsl:text>           window.addEventListener("mousemove", hmi_widgets[this.element_id].on_move.bind(this));
-</xsl:text>
-    <xsl:text>           window.addEventListener("touchmove", hmi_widgets[this.element_id].on_move.bind(this));
-</xsl:text>
     <xsl:text>       }
 </xsl:text>
     <xsl:text>    },
@@ -4388,10 +4518,62 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
+  <xsl:template mode="widget_class" match="widget[@type='Meter']">
+    <xsl:text>class MeterWidget extends Widget{
+</xsl:text>
+    <xsl:text>    frequency = 10;
+</xsl:text>
+    <xsl:text>    origin = undefined;
+</xsl:text>
+    <xsl:text>    range = undefined;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    dispatch(value) {
+</xsl:text>
+    <xsl:text>        if(this.value_elt)
+</xsl:text>
+    <xsl:text>            this.value_elt.textContent = String(value);
+</xsl:text>
+    <xsl:text>        let [min,max,totallength] = this.range;
+</xsl:text>
+    <xsl:text>        let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
+</xsl:text>
+    <xsl:text>        let tip = this.range_elt.getPointAtLength(length);
+</xsl:text>
+    <xsl:text>        this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    init() {
+</xsl:text>
+    <xsl:text>        let min = this.min_elt ?
+</xsl:text>
+    <xsl:text>                    Number(this.min_elt.textContent) :
+</xsl:text>
+    <xsl:text>                    this.args.length &gt;= 1 ? this.args[0] : 0;
+</xsl:text>
+    <xsl:text>        let max = this.max_elt ?
+</xsl:text>
+    <xsl:text>                    Number(this.max_elt.textContent) :
+</xsl:text>
+    <xsl:text>                    this.args.length &gt;= 2 ? this.args[1] : 100;
+</xsl:text>
+    <xsl:text>        this.range = [min, max, this.range_elt.getTotalLength()]
+</xsl:text>
+    <xsl:text>        this.origin = this.needle_elt.getPointAtLength(0);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
   <xsl:template mode="widget_defs" match="widget[@type='Meter']">
     <xsl:param name="hmi_element"/>
-    <xsl:text>    frequency: 10,
-</xsl:text>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
       <xsl:with-param name="labels">
@@ -4405,45 +4587,7 @@
       </xsl:with-param>
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
-    <xsl:text>    dispatch: function(value) {
-</xsl:text>
-    <xsl:text>        if(this.value_elt)
-</xsl:text>
-    <xsl:text>            this.value_elt.textContent = String(value);
-</xsl:text>
-    <xsl:text>        let [min,max,totallength] = this.range;
-</xsl:text>
-    <xsl:text>        let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
-</xsl:text>
-    <xsl:text>        let tip = this.range_elt.getPointAtLength(length);
-</xsl:text>
-    <xsl:text>        this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
-</xsl:text>
-    <xsl:text>    },
-</xsl:text>
-    <xsl:text>    origin: undefined,
-</xsl:text>
-    <xsl:text>    range: undefined,
-</xsl:text>
-    <xsl:text>    init: function() {
-</xsl:text>
-    <xsl:text>        let min = this.min_elt ?
-</xsl:text>
-    <xsl:text>                    Number(this.min_elt.textContent) :
-</xsl:text>
-    <xsl:text>                    this.args.length &gt;= 1 ? this.args[0] : 0;
-</xsl:text>
-    <xsl:text>        let max = this.max_elt ?
-</xsl:text>
-    <xsl:text>                    Number(this.max_elt.textContent) :
-</xsl:text>
-    <xsl:text>                    this.args.length &gt;= 2 ? this.args[1] : 100;
-</xsl:text>
-    <xsl:text>        this.range = [min, max, this.range_elt.getTotalLength()]
-</xsl:text>
-    <xsl:text>        this.origin = this.needle_elt.getPointAtLength(0);
-</xsl:text>
-    <xsl:text>    },
+    <xsl:text>
 </xsl:text>
   </xsl:template>
   <xsl:template mode="widget_class" match="widget[@type='MultiState']">
@@ -4568,6 +4712,8 @@
 </xsl:text>
     <xsl:text>    fi = undefined;
 </xsl:text>
+    <xsl:text>    svg_dist = undefined;
+</xsl:text>
     <xsl:text>    drag = false;
 </xsl:text>
     <xsl:text>    enTimer = false;
--- a/svghmi/widget_circularbar.ysl2	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/widget_circularbar.ysl2	Fri Aug 28 11:31:18 2020 +0200
@@ -1,48 +1,57 @@
 // widget_circularbar.ysl2
 
+template "widget[@type='CircularBar']", mode="widget_class"{
+    ||
+    class CircularBarWidget extends Widget{
+        frequency = 10;
+        range = undefined;
+
+        dispatch(value) {
+            if(this.value_elt)
+                this.value_elt.textContent = String(value);
+            let [min,max,start,end] = this.range;
+            let [cx,cy] = this.center;
+            let [rx,ry] = this.proportions;
+            let tip = start + (end-start)*Number(value)/(max-min);
+            let size = 0;
+            if (tip-start > Math.PI) {
+                size = 1;
+            } else {
+                size = 0;
+            }
+            this.path_elt.setAttribute('d', "M "+(cx+rx*Math.cos(start))+","+(cy+ry*Math.sin(start))+" A "+rx+","+ry+" 0 "+size+" 1 "+(cx+rx*Math.cos(tip))+","+(cy+ry*Math.sin(tip)));
+        }
+
+        init() {
+            let start = Number(this.path_elt.getAttribute('sodipodi:start'));
+            let end = Number(this.path_elt.getAttribute('sodipodi:end'));
+            let cx = Number(this.path_elt.getAttribute('sodipodi:cx'));
+            let cy = Number(this.path_elt.getAttribute('sodipodi:cy'));
+            let rx = Number(this.path_elt.getAttribute('sodipodi:rx'));
+            let ry = Number(this.path_elt.getAttribute('sodipodi:ry'));
+            if (ry == 0) {
+                ry = rx;
+            }
+            if (start > end) {
+                end = end + 2*Math.PI;
+            }
+            let min = this.min_elt ?
+                      Number(this.min_elt.textContent) :
+                      this.args.length >= 1 ? this.args[0] : 0;
+            let max = this.max_elt ?
+                      Number(this.max_elt.textContent) :
+                      this.args.length >= 2 ? this.args[1] : 100;
+            this.range = [min, max, start, end];
+            this.center = [cx, cy];
+            this.proportions = [rx, ry];
+        }
+    }
+    ||
+}
 
 template "widget[@type='CircularBar']", mode="widget_defs" {
     param "hmi_element";
-    | frequency: 10,
     labels("path");
     optional_labels("value min max");
-    | dispatch: function(value) {
-    |     if(this.value_elt)
-    |         this.value_elt.textContent = String(value);
-    |     let [min,max,start,end] = this.range;
-    |     let [cx,cy] = this.center;
-    |     let [rx,ry] = this.proportions;
-    |     let tip = start + (end-start)*Number(value)/(max-min);
-    |     let size = 0;
-    |     if (tip-start > Math.PI) {
-    |         size = 1;
-    |     } else {
-    |         size = 0;
-    |     }
-    |     this.path_elt.setAttribute('d', "M "+(cx+rx*Math.cos(start))+","+(cy+ry*Math.sin(start))+" A "+rx+","+ry+" 0 "+size+" 1 "+(cx+rx*Math.cos(tip))+","+(cy+ry*Math.sin(tip)));
-    | },
-    | range: undefined,
-    | init: function() {
-    |     let start = Number(this.path_elt.getAttribute('sodipodi:start'));
-    |     let end = Number(this.path_elt.getAttribute('sodipodi:end'));
-    |     let cx = Number(this.path_elt.getAttribute('sodipodi:cx'));
-    |     let cy = Number(this.path_elt.getAttribute('sodipodi:cy'));
-    |     let rx = Number(this.path_elt.getAttribute('sodipodi:rx'));
-    |     let ry = Number(this.path_elt.getAttribute('sodipodi:ry'));
-    |     if (ry == 0) {
-    |         ry = rx;
-    |     }
-    |     if (start > end) {
-    |         end = end + 2*Math.PI;
-    |     }
-    |     let min = this.min_elt ?
-    |               Number(this.min_elt.textContent) :
-    |               this.args.length >= 1 ? this.args[0] : 0;
-    |     let max = this.max_elt ?
-    |               Number(this.max_elt.textContent) :
-    |               this.args.length >= 2 ? this.args[1] : 100;
-    |     this.range = [min, max, start, end];
-    |     this.center = [cx, cy];
-    |     this.proportions = [rx, ry];
-    | },
+    |,
 }
\ No newline at end of file
--- a/svghmi/widget_circularslider.ysl2	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/widget_circularslider.ysl2	Fri Aug 28 11:31:18 2020 +0200
@@ -7,29 +7,56 @@
         range = undefined;
         circle = undefined;
         handle_pos = undefined;
+        svg_dist = undefined;
         drag = false;
         enTimer = false;
+        last_drag = false;
 
         dispatch(value) {
-            if(!this.drag){
-                if(this.value_elt)
-                    this.value_elt.textContent = String(value);
-
-                this.handle_position(value);
-            }
-        }
-
-        handle_position(value){
+            if(this.value_elt)
+                this.value_elt.textContent = String(value);
+
+            this.update_DOM(value, this.handle_elt);
+        }
+
+        update_DOM(value, elt){
             let [min,max,totalDistance] = this.range;
             let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
             let tip = this.range_elt.getPointAtLength(length);
-            this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
+            elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
+
+            if(this.setpoint_elt != undefined){
+                if(this.last_drag!= this.drag){
+                    if(this.drag){
+                        this.setpoint_elt.setAttribute("style", this.setpoint_style);
+                    }else{
+                        this.setpoint_elt.setAttribute("style", "display:none");
+                    }
+                    this.last_drag = this.drag;
+                }
+            }
         }
 
         on_release(evt) {
+            window.removeEventListener("touchmove", this.on_bound_drag, true);
+            window.removeEventListener("mousemove", this.on_bound_drag, true);
+
+            window.removeEventListener("mouseup", this.bound_on_release, true)
+            window.removeEventListener("touchend", this.bound_on_release, true);
+            window.removeEventListener("touchcancel", this.bound_on_release, true);
             if(this.drag){
                 this.drag = false;
             }
+            this.update_position(evt);
+        }
+
+        on_drag(evt){
+            if(this.enTimer && this.drag){
+                this.update_position(evt);
+                //reset timer
+                this.enTimer = false;
+                setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
+            }
         }
 
         update_position(evt){
@@ -76,31 +103,39 @@
 
                 //get handle distance from mouse position
                 if(fi<fiEnd){
-                    svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
+                    this.svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
                 }
                 else if(fiEnd<fi && fi<fiEnd+minMax){
-                    svg_dist = this.range[1];
+                    this.svg_dist = this.range[1];
                 }
                 else{
-                    svg_dist = this.range[0];
-                }
-
-                //redraw handle --TODO is it fast enough if I just call change_hmi_value???
-                this.handle_position(svg_dist);
-                if(this.value_elt)
-                    this.value_elt.textContent = String(Math.ceil(svg_dist));
-                this.apply_hmi_value(0, Math.ceil(svg_dist));
-
-                //reset timer
-                this.enTimer = false;
-                setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
-            }
-
+                    this.svg_dist = this.range[0];
+                }
+
+
+                this.apply_hmi_value(0, Math.ceil(this.svg_dist));
+
+                // update ghost cursor
+                if(this.setpoint_elt != undefined){
+                    this.request_animate();
+                }
+            }
+
+        }
+
+        animate(){
+            this.update_DOM(this.svg_dist, this.setpoint_elt);
         }
 
         on_select(evt){
             this.drag = true;
             this.enTimer = true;
+            window.addEventListener("touchmove", this.on_bound_drag, true);
+            window.addEventListener("mousemove", this.on_bound_drag, true);
+
+            window.addEventListener("mouseup", this.bound_on_release, true)
+            window.addEventListener("touchend", this.bound_on_release, true);
+            window.addEventListener("touchcancel", this.bound_on_release, true);
             this.update_position(evt);
         }
 
@@ -135,10 +170,20 @@
             this.handle_pos = this.range_elt.getPointAtLength(0);
             this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height];
 
+            //bind functions
+            this.bound_on_select = this.on_select.bind(this);
+            this.bound_on_release = this.on_release.bind(this);
+            this.on_bound_drag = this.on_drag.bind(this);
+
             //init events
-            this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
-            this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
-            this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
+            this.element.addEventListener("mousedown", this.bound_on_select);
+            this.element.addEventListener("touchstart", this.bound_on_select);
+
+            if(this.setpoint_elt != undefined){
+                this.setpoint_style = this.setpoint_elt.getAttribute("style");
+                this.setpoint_elt.setAttribute("style", "display:none");
+            }
+
 
             window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
             window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));
--- a/svghmi/widget_keypad.ysl2	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/widget_keypad.ysl2	Fri Aug 28 11:31:18 2020 +0200
@@ -17,12 +17,19 @@
     ||
     class KeypadWidget extends Widget{
          moving = undefined;
-         enTimer = undefined;
+         click = undefined;
          offset = undefined;
 
          on_position_click(evt) {
              this.moving = true;
-             this.enTimer = true;
+
+             // chatch window events
+             window.addEventListener("touchmove", this.bound_on_drag, true);
+             window.addEventListener("mousemove", this.bound_on_drag, true);
+
+             window.addEventListener("mouseup", this.bound_on_release, true)
+             window.addEventListener("touchend", this.bound_on_release, true);
+             window.addEventListener("touchcancel", this.bound_on_release, true);
 
              // get click position offset from widget x,y and save it to variable
              var keypad_borders = this.position_elt.getBoundingClientRect();
@@ -39,40 +46,49 @@
              this.offset=[clickX-keypad_borders.left,clickY-keypad_borders.top]
          }
 
-         off_position_click(evt) {
+         on_release(evt) {
+            //relase binds
+            window.removeEventListener("touchmove", this.bound_on_drag, true);
+            window.removeEventListener("mousemove", this.bound_on_drag, true);
+
+            window.removeEventListener("mouseup", this.bound_on_release, true)
+            window.removeEventListener("touchend", this.bound_on_release, true);
+            window.removeEventListener("touchcancel", this.bound_on_release, true);
+
             if(this.moving)
                 this.moving = false;
          }
 
-         on_move(evt) {
-             if(this.moving && this.enTimer){
-                 //get keyboard pos in html
-                 let [eltid, tmpgrp] = current_modal;
-                 let [xcoord,ycoord] = this.coordinates;
-                 let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
-
-                 //get mouse coordinates
-                 var clickX = undefined;
-                 var clickY = undefined;
-                 if (evt.type == "touchmove"){
-                     clickX = Math.ceil(evt.touches[0].clientX);
-                     clickY = Math.ceil(evt.touches[0].clientY);
-                 }
-                 else{
-                     clickX = evt.pageX;
-                     clickY = evt.pageY;
-                 }
-
-                 //translate keyboard position
-                 let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth;
-                 let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight;
-                 tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")");
-
-                 //reset timer
-                 this.enTimer = false;
-                 setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
-             }
-
+         on_drag(evt) {
+             if(this.moving)
+                //get mouse coordinates
+                var clickX = undefined;
+                var clickY = undefined;
+                if (evt.type == "touchmove"){
+                    clickX = Math.ceil(evt.touches[0].clientX);
+                    clickY = Math.ceil(evt.touches[0].clientY);
+                }
+                else{
+                    clickX = evt.pageX;
+                    clickY = evt.pageY;
+                }
+                this.click = [clickX,clickY]
+
+                //requeset redraw
+                this.request_animate();
+         }
+
+         animate(){
+            //get keyboard pos in html
+            let [eltid, tmpgrp] = current_modal;
+            let [xcoord,ycoord] = this.coordinates;
+            let [clickX,clickY] = this.click;
+            let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
+
+            //translate keyboard position
+            let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth;
+            let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight;
+            tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")");
          }
 
          on_key_click(symbols) {
@@ -185,15 +201,11 @@
     |             this.«.»_elt.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_«.»_click()");
     }
     |         if(this.position_elt){
+    |            this.bound_on_release = this.on_release.bind(this);
+    |            this.bound_on_drag = this.on_drag.bind(this);
+    |
     |            this.position_elt.setAttribute("onmousedown", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)");
     |            this.position_elt.setAttribute("ontouchstart", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)");
-
-    |            window.addEventListener("mouseup", hmi_widgets[this.element_id].off_position_click.bind(this));
-    |            window.addEventListener("touchend", hmi_widgets[this.element_id].off_position_click.bind(this));
-    |            window.addEventListener("touchcancel", hmi_widgets[this.element_id].off_position_click.bind(this));
-
-    |            window.addEventListener("mousemove", hmi_widgets[this.element_id].on_move.bind(this));
-    |            window.addEventListener("touchmove", hmi_widgets[this.element_id].on_move.bind(this));
     |        }
     |     },
     |
--- a/svghmi/widget_meter.ysl2	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/widget_meter.ysl2	Fri Aug 28 11:31:18 2020 +0200
@@ -1,31 +1,41 @@
 // widget_meter.ysl2
 
+template "widget[@type='Meter']", mode="widget_class"{
+    ||
+    class MeterWidget extends Widget{
+        frequency = 10;
+        origin = undefined;
+        range = undefined;
+
+        dispatch(value) {
+            if(this.value_elt)
+                this.value_elt.textContent = String(value);
+            let [min,max,totallength] = this.range;
+            let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
+            let tip = this.range_elt.getPointAtLength(length);
+            this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
+        }
+
+        init() {
+            let min = this.min_elt ?
+                        Number(this.min_elt.textContent) :
+                        this.args.length >= 1 ? this.args[0] : 0;
+            let max = this.max_elt ?
+                        Number(this.max_elt.textContent) :
+                        this.args.length >= 2 ? this.args[1] : 100;
+            this.range = [min, max, this.range_elt.getTotalLength()]
+            this.origin = this.needle_elt.getPointAtLength(0);
+        }
+
+    }
+    ||
+}
 
 template "widget[@type='Meter']", mode="widget_defs" {
     param "hmi_element";
-    |     frequency: 10,
     labels("needle range");
     optional_labels("value min max");
-    |     dispatch: function(value) {
-    |         if(this.value_elt)
-    |             this.value_elt.textContent = String(value);
-    |         let [min,max,totallength] = this.range;
-    |         let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
-    |         let tip = this.range_elt.getPointAtLength(length);
-    |         this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
-    |     },
-    |     origin: undefined,
-    |     range: undefined,
-    |     init: function() {
-    |         let min = this.min_elt ?
-    |                     Number(this.min_elt.textContent) :
-    |                     this.args.length >= 1 ? this.args[0] : 0;
-    |         let max = this.max_elt ?
-    |                     Number(this.max_elt.textContent) :
-    |                     this.args.length >= 2 ? this.args[1] : 100;
-    |         this.range = [min, max, this.range_elt.getTotalLength()]
-    |         this.origin = this.needle_elt.getPointAtLength(0);
-    |     },
+    |,
 }
 
 
--- a/svghmi/widget_slider.ysl2	Fri Aug 28 11:27:07 2020 +0200
+++ b/svghmi/widget_slider.ysl2	Fri Aug 28 11:31:18 2020 +0200
@@ -6,6 +6,7 @@
         frequency = 5;
         range = undefined;
         fi = undefined;
+        svg_dist = undefined;
         drag = false;
         enTimer = false;