SVGHMI: provide request_animate() to Widget authors so that they can register redraw code when events lead to redraw. Widget member animate() is called when it is time to update DOM. svghmi
authorEdouard Tisserant
Mon, 10 Aug 2020 13:58:55 +0200
branchsvghmi
changeset 3019 497aac6522a3
parent 3018 22b969b409b0
child 3020 895bbeced72d
SVGHMI: provide request_animate() to Widget authors so that they can register redraw code when events lead to redraw. Widget member animate() is called when it is time to update DOM.
svghmi/gen_index_xhtml.xslt
svghmi/svghmi.js
svghmi/widgets_common.ysl2
--- a/svghmi/gen_index_xhtml.xslt	Mon Aug 10 11:30:06 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt	Mon Aug 10 13:58:55 2020 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
-<xsl:stylesheet xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:definitions="definitions" xmlns:ns="beremiz" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:exsl="http://exslt.org/common" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:preamble="preamble" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:str="http://exslt.org/strings" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:debug="debug" xmlns:svg="http://www.w3.org/2000/svg" xmlns:declarations="declarations" xmlns:cc="http://creativecommons.org/ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions">
-  <xsl:output cdata-section-elements="xhtml:script" method="xml"/>
+<xsl:stylesheet xmlns:ns="beremiz" xmlns:definitions="definitions" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:str="http://exslt.org/strings" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:exsl="http://exslt.org/common" xmlns:declarations="declarations" xmlns:debug="debug" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0">
+  <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
   <xsl:variable name="svg" select="/svg:svg"/>
   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
@@ -14,7 +14,7 @@
   </xsl:variable>
   <xsl:variable name="categories" select="exsl:node-set($_categories)"/>
   <xsl:variable name="_indexed_hmitree">
-    <xsl:apply-templates select="$hmitree" mode="index"/>
+    <xsl:apply-templates mode="index" select="$hmitree"/>
   </xsl:variable>
   <xsl:variable name="indexed_hmitree" select="exsl:node-set($_indexed_hmitree)"/>
   <preamble:hmi-tree/>
@@ -62,7 +62,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="*" mode="index">
+  <xsl:template mode="index" match="*">
     <xsl:param name="index" select="0"/>
     <xsl:param name="parentpath" select="''"/>
     <xsl:variable name="content">
@@ -95,7 +95,7 @@
               <xsl:copy/>
             </xsl:for-each>
           </xsl:copy>
-          <xsl:apply-templates select="*[1]" mode="index">
+          <xsl:apply-templates mode="index" select="*[1]">
             <xsl:with-param name="index" select="$index + 1"/>
             <xsl:with-param name="parentpath">
               <xsl:value-of select="$path"/>
@@ -103,7 +103,7 @@
           </xsl:apply-templates>
         </xsl:when>
         <xsl:otherwise>
-          <xsl:apply-templates select="*[1]" mode="index">
+          <xsl:apply-templates mode="index" select="*[1]">
             <xsl:with-param name="index" select="$index"/>
             <xsl:with-param name="parentpath">
               <xsl:value-of select="$path"/>
@@ -113,14 +113,14 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:copy-of select="$content"/>
-    <xsl:apply-templates select="following-sibling::*[1]" mode="index">
+    <xsl:apply-templates mode="index" select="following-sibling::*[1]">
       <xsl:with-param name="index" select="$index + count(exsl:node-set($content)/*)"/>
       <xsl:with-param name="parentpath">
         <xsl:value-of select="$parentpath"/>
       </xsl:with-param>
     </xsl:apply-templates>
   </xsl:template>
-  <xsl:template match="*" mode="parselabel">
+  <xsl:template mode="parselabel" match="*">
     <xsl:variable name="label" select="@inkscape:label"/>
     <xsl:variable name="description" select="substring-after($label,'HMI:')"/>
     <xsl:variable name="_args" select="substring-before($description,'@')"/>
@@ -184,7 +184,7 @@
     </xsl:if>
   </xsl:template>
   <xsl:variable name="_parsed_widgets">
-    <xsl:apply-templates select="$hmi_elements" mode="parselabel"/>
+    <xsl:apply-templates mode="parselabel" select="$hmi_elements"/>
   </xsl:variable>
   <xsl:variable name="parsed_widgets" select="exsl:node-set($_parsed_widgets)"/>
   <func:function name="func:widget">
@@ -203,7 +203,7 @@
     <xsl:variable name="class_b" select="$indexed_hmitree/*[@hmipath = $b]/@class"/>
     <func:result select="$class_a and $class_b and $class_a = $class_b"/>
   </func:function>
-  <xsl:template match="*" mode="testtree">
+  <xsl:template mode="testtree" match="*">
     <xsl:param name="indent" select="''"/>
     <xsl:value-of select="$indent"/>
     <xsl:text> </xsl:text>
@@ -217,7 +217,7 @@
     </xsl:for-each>
     <xsl:text>
 </xsl:text>
-    <xsl:apply-templates select="*" mode="testtree">
+    <xsl:apply-templates mode="testtree" select="*">
       <xsl:with-param name="indent">
         <xsl:value-of select="concat($indent,'&gt;')"/>
       </xsl:with-param>
@@ -235,18 +235,18 @@
 </xsl:text>
     <xsl:text>Raw HMI tree
 </xsl:text>
-    <xsl:apply-templates select="$hmitree" mode="testtree"/>
+    <xsl:apply-templates mode="testtree" select="$hmitree"/>
     <xsl:text>
 </xsl:text>
     <xsl:text>Indexed HMI tree
 </xsl:text>
-    <xsl:apply-templates select="$indexed_hmitree" mode="testtree"/>
+    <xsl:apply-templates mode="testtree" select="$indexed_hmitree"/>
     <xsl:text>
 </xsl:text>
     <xsl:text>Parsed Widgets
 </xsl:text>
     <xsl:copy-of select="_parsed_widgets"/>
-    <xsl:apply-templates select="$parsed_widgets" mode="testtree"/>
+    <xsl:apply-templates mode="testtree" select="$parsed_widgets"/>
     <xsl:text>
 </xsl:text>
   </xsl:template>
@@ -459,7 +459,7 @@
   <xsl:variable name="forEach_widgets_ids" select="$parsed_widgets/widget[@type = 'ForEach']/@id"/>
   <xsl:variable name="forEach_widgets" select="$hmi_elements[@id = $forEach_widgets_ids]"/>
   <xsl:variable name="in_forEach_widget_ids" select="func:refered_elements($forEach_widgets)[not(@id = $forEach_widgets_ids)]/@id"/>
-  <xsl:template match="svg:*" mode="page_desc">
+  <xsl:template mode="page_desc" match="svg:*">
     <xsl:variable name="desc" select="func:widget(@id)"/>
     <xsl:variable name="page" select="."/>
     <xsl:variable name="p" select="$geometry[@Id = $page/@id]"/>
@@ -559,7 +559,7 @@
     </xsl:for-each>
     <xsl:text>    }
 </xsl:text>
-    <xsl:apply-templates select="$parsed_widgets/widget[@id = $all_page_widgets/@id]" mode="per_page_widget_template">
+    <xsl:apply-templates mode="per_page_widget_template" select="$parsed_widgets/widget[@id = $all_page_widgets/@id]">
       <xsl:with-param name="page_desc" select="$desc"/>
     </xsl:apply-templates>
     <xsl:text>  }</xsl:text>
@@ -583,13 +583,13 @@
 </xsl:text>
     <xsl:text>var page_desc = {
 </xsl:text>
-    <xsl:apply-templates select="$hmi_pages" mode="page_desc"/>
+    <xsl:apply-templates mode="page_desc" select="$hmi_pages"/>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="*" mode="per_page_widget_template"/>
+  <xsl:template mode="per_page_widget_template" match="*"/>
   <debug:detachable-pages/>
   <xsl:template match="debug:detachable-pages">
     <xsl:text>
@@ -621,16 +621,16 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="@* | node()" mode="inline_svg">
+  <xsl:template mode="inline_svg" match="@* | node()">
     <xsl:if test="not(@id = $discardable_elements/@id)">
       <xsl:copy>
-        <xsl:apply-templates select="@* | node()" mode="inline_svg"/>
+        <xsl:apply-templates mode="inline_svg" select="@* | node()"/>
       </xsl:copy>
     </xsl:if>
   </xsl:template>
-  <xsl:template match="svg:svg/@width" mode="inline_svg"/>
-  <xsl:template match="svg:svg/@height" mode="inline_svg"/>
-  <xsl:template xmlns="http://www.w3.org/2000/svg" match="svg:svg" mode="inline_svg">
+  <xsl:template mode="inline_svg" match="svg:svg/@width"/>
+  <xsl:template mode="inline_svg" match="svg:svg/@height"/>
+  <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="svg:svg">
     <svg>
       <xsl:attribute name="preserveAspectRatio">
         <xsl:text>none</xsl:text>
@@ -641,15 +641,15 @@
       <xsl:attribute name="width">
         <xsl:text>100vw</xsl:text>
       </xsl:attribute>
-      <xsl:apply-templates select="@* | node()" mode="inline_svg"/>
+      <xsl:apply-templates mode="inline_svg" select="@* | node()"/>
     </svg>
   </xsl:template>
-  <xsl:template match="svg:svg[@viewBox!=concat('0 0 ', @width, ' ', @height)]" mode="inline_svg">
+  <xsl:template mode="inline_svg" match="svg:svg[@viewBox!=concat('0 0 ', @width, ' ', @height)]">
     <xsl:message terminate="yes">
       <xsl:text>ViewBox settings other than X=0, Y=0 and Scale=1 are not supported</xsl:text>
     </xsl:message>
   </xsl:template>
-  <xsl:template match="sodipodi:namedview[@units!='px' or @inkscape:document-units!='px']" mode="inline_svg">
+  <xsl:template mode="inline_svg" match="sodipodi:namedview[@units!='px' or @inkscape:document-units!='px']">
     <xsl:message terminate="yes">
       <xsl:text>All units must be set to "px" in Inkscape's document properties</xsl:text>
     </xsl:message>
@@ -658,7 +658,7 @@
   <xsl:variable name="hmi_lists" select="$hmi_elements[@id = $hmi_lists_descs/@id]"/>
   <xsl:variable name="targets_not_to_unlink" select="$hmi_elements[@id = $hmi_lists/@id]/descendant::svg:*"/>
   <xsl:variable name="to_unlink" select="$hmi_elements[not(@id = $hmi_pages/@id)]/descendant-or-self::svg:use"/>
-  <xsl:template xmlns="http://www.w3.org/2000/svg" match="svg:use" mode="inline_svg">
+  <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="svg:use">
     <xsl:variable name="targetid" select="substring-after(@xlink:href,'#')"/>
     <xsl:choose>
       <xsl:when test="@id = $to_unlink/@id and not($targetid = $targets_not_to_unlink/@id)">
@@ -668,7 +668,7 @@
       </xsl:when>
       <xsl:otherwise>
         <xsl:copy>
-          <xsl:apply-templates select="@* | node()" mode="inline_svg"/>
+          <xsl:apply-templates mode="inline_svg" select="@* | node()"/>
         </xsl:copy>
       </xsl:otherwise>
     </xsl:choose>
@@ -729,7 +729,7 @@
               <xsl:value-of select="$target/@transform"/>
             </xsl:attribute>
           </xsl:if>
-          <xsl:apply-templates select="$target/*" mode="unlink_clone">
+          <xsl:apply-templates mode="unlink_clone" select="$target/*">
             <xsl:with-param name="seed" select="@id"/>
           </xsl:apply-templates>
         </xsl:when>
@@ -739,14 +739,14 @@
               <xsl:value-of select="."/>
             </xsl:attribute>
           </xsl:for-each>
-          <xsl:apply-templates select="$target" mode="unlink_clone">
+          <xsl:apply-templates mode="unlink_clone" select="$target">
             <xsl:with-param name="seed" select="@id"/>
           </xsl:apply-templates>
         </xsl:otherwise>
       </xsl:choose>
     </g>
   </xsl:template>
-  <xsl:template xmlns="http://www.w3.org/2000/svg" match="@id" mode="unlink_clone">
+  <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="@id">
     <xsl:param name="seed"/>
     <xsl:attribute name="id">
       <xsl:value-of select="$seed"/>
@@ -754,10 +754,10 @@
       <xsl:value-of select="."/>
     </xsl:attribute>
   </xsl:template>
-  <xsl:template xmlns="http://www.w3.org/2000/svg" match="@*" mode="unlink_clone">
+  <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="@*">
     <xsl:copy/>
   </xsl:template>
-  <xsl:template xmlns="http://www.w3.org/2000/svg" match="svg:*" mode="unlink_clone">
+  <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="svg:*">
     <xsl:param name="seed"/>
     <xsl:choose>
       <xsl:when test="@id = $hmi_elements/@id">
@@ -769,7 +769,7 @@
       </xsl:when>
       <xsl:otherwise>
         <xsl:copy>
-          <xsl:apply-templates select="@* | node()" mode="unlink_clone">
+          <xsl:apply-templates mode="unlink_clone" select="@* | node()">
             <xsl:with-param name="seed" select="$seed"/>
           </xsl:apply-templates>
         </xsl:copy>
@@ -777,7 +777,7 @@
     </xsl:choose>
   </xsl:template>
   <xsl:variable name="result_svg">
-    <xsl:apply-templates select="/" mode="inline_svg"/>
+    <xsl:apply-templates mode="inline_svg" select="/"/>
   </xsl:variable>
   <xsl:variable name="result_svg_ns" select="exsl:node-set($result_svg)"/>
   <preamble:inline-svg/>
@@ -828,7 +828,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="svg:*" mode="hmi_widgets">
+  <xsl:template mode="hmi_widgets" match="svg:*">
     <xsl:variable name="widget" select="func:widget(@id)"/>
     <xsl:variable name="eltid" select="@id"/>
     <xsl:variable name="args">
@@ -876,7 +876,7 @@
     <xsl:value-of select="$indexes"/>
     <xsl:text>],{
 </xsl:text>
-    <xsl:apply-templates select="$widget" mode="widget_defs">
+    <xsl:apply-templates mode="widget_defs" select="$widget">
       <xsl:with-param name="hmi_element" select="."/>
     </xsl:apply-templates>
     <xsl:text>  })</xsl:text>
@@ -915,6 +915,10 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>var pending_widget_animates = [];
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>class Widget {
 </xsl:text>
     <xsl:text>    offset = 0;
@@ -923,6 +927,10 @@
 </xsl:text>
     <xsl:text>    unsubscribable = false;
 </xsl:text>
+    <xsl:text>    pending_animate = false;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>    constructor(elt_id,args,indexes,members){
 </xsl:text>
     <xsl:text>        this.element_id = elt_id;
@@ -1091,6 +1099,34 @@
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
+    <xsl:text>    
+</xsl:text>
+    <xsl:text>    _animate(){
+</xsl:text>
+    <xsl:text>        this.animate();
+</xsl:text>
+    <xsl:text>        this.pending_animate = false;
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    request_animate(){
+</xsl:text>
+    <xsl:text>        if(!this.pending_animate){
+</xsl:text>
+    <xsl:text>            pending_widget_animates.push(this);
+</xsl:text>
+    <xsl:text>            this.pending_animate = true;
+</xsl:text>
+    <xsl:text>            requestHMIAnimation();
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
@@ -1107,11 +1143,11 @@
     <xsl:text>
 </xsl:text>
     <xsl:variable name="used_widget_types" select="func:unique_types($parsed_widgets/widget)"/>
-    <xsl:apply-templates select="$used_widget_types" mode="widget_class"/>
-    <xsl:text>
-</xsl:text>
-  </xsl:template>
-  <xsl:template match="widget" mode="widget_class">
+    <xsl:apply-templates mode="widget_class" select="$used_widget_types"/>
+    <xsl:text>
+</xsl:text>
+  </xsl:template>
+  <xsl:template mode="widget_class" match="widget">
     <xsl:text>class </xsl:text>
     <xsl:value-of select="@type"/>
     <xsl:text>Widget extends Widget{
@@ -1137,7 +1173,7 @@
 </xsl:text>
     <xsl:text>var hmi_widgets = {
 </xsl:text>
-    <xsl:apply-templates select="$hmi_elements[@id = $excluded_ids]" mode="hmi_widgets"/>
+    <xsl:apply-templates mode="hmi_widgets" select="$hmi_elements[@id = $excluded_ids]"/>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
@@ -1231,7 +1267,7 @@
       </xsl:otherwise>
     </xsl:choose>
   </func:function>
-  <xsl:template match="widget[@type='Back']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Back']">
     <xsl:text>class BackWidget extends Widget{
 </xsl:text>
     <xsl:text>    on_click(evt) {
@@ -1257,7 +1293,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Button']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Button']">
     <xsl:text>t{
 </xsl:text>
     <xsl:text>5;
@@ -1331,7 +1367,7 @@
     <xsl:text>||
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Button']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Button']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -1343,7 +1379,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='CircularBar']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='CircularBar']">
     <xsl:param name="hmi_element"/>
     <xsl:text>frequency: 10,
 </xsl:text>
@@ -1439,7 +1475,7 @@
     <xsl:text>},
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='CircularSlider']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='CircularSlider']">
     <xsl:text>class CircularSliderWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -1614,7 +1650,7 @@
 </xsl:text>
     <xsl:text>                this.value_elt.textContent = String(Math.ceil(svg_dist));
 </xsl:text>
-    <xsl:text>            change_hmi_value(this.indexes[0], "="+Math.ceil(svg_dist));
+    <xsl:text>            this.apply_hmi_value(0, Math.ceil(svg_dist));
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -1735,7 +1771,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='CircularSlider']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='CircularSlider']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -1753,7 +1789,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Display']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Display']">
     <xsl:text>class DisplayWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -1771,7 +1807,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Display']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Display']">
     <xsl:param name="hmi_element"/>
     <xsl:if test="$hmi_element[not(self::svg:text)]">
       <xsl:message terminate="yes">
@@ -2262,7 +2298,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='DropDown']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='DropDown']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -2775,7 +2811,7 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='ForEach']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
     <xsl:param name="hmi_element"/>
     <xsl:if test="count(path) != 1">
       <xsl:message terminate="yes">
@@ -2889,7 +2925,7 @@
     <xsl:text>    item_offset: 0,
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='ForEach']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='ForEach']">
     <xsl:text>class ForEachWidget extends Widget{
 </xsl:text>
     <xsl:text>
@@ -3025,7 +3061,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Input']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Input']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -3109,7 +3145,7 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='JsonTable']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='JsonTable']">
     <xsl:text>class JsonTableWidget extends Widget{
 </xsl:text>
     <xsl:text>    do_http_request() {
@@ -3169,14 +3205,14 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="svg:*" mode="json_table_elt_render">
+  <xsl:template mode="json_table_elt_render" match="svg:*">
     <xsl:message terminate="yes">
       <xsl:text>JsonTable Widget can't contain element of type </xsl:text>
       <xsl:value-of select="local-name()"/>
       <xsl:text>.</xsl:text>
     </xsl:message>
   </xsl:template>
-  <xsl:template match="svg:use" mode="json_table_elt_render">
+  <xsl:template mode="json_table_elt_render" match="svg:use">
     <xsl:param name="value_expr"/>
     <xsl:variable name="targetid" select="substring-after(@xlink:href,'#')"/>
     <xsl:variable name="from_list" select="$hmi_lists[(@id | */@id) = $targetid]"/>
@@ -3198,7 +3234,7 @@
     <xsl:text>]);
 </xsl:text>
   </xsl:template>
-  <xsl:template match="svg:text" mode="json_table_elt_render">
+  <xsl:template mode="json_table_elt_render" match="svg:text">
     <xsl:param name="value_expr"/>
     <xsl:text>        id("</xsl:text>
     <xsl:value-of select="@id"/>
@@ -3207,16 +3243,16 @@
     <xsl:text>);
 </xsl:text>
   </xsl:template>
-  <xsl:template match="svg:*" mode="json_table_render">
+  <xsl:template mode="json_table_render" match="svg:*">
     <xsl:param name="objname"/>
-    <xsl:apply-templates select="." mode="json_table_elt_render">
+    <xsl:apply-templates mode="json_table_elt_render" select=".">
       <xsl:with-param name="value_expr">
         <xsl:value-of select="$objname"/>
         <xsl:value-of select="@inkscape:label"/>
       </xsl:with-param>
     </xsl:apply-templates>
   </xsl:template>
-  <xsl:template match="svg:g" mode="json_table_render">
+  <xsl:template mode="json_table_render" match="svg:g">
     <xsl:param name="objname"/>
     <xsl:text>        let obj_</xsl:text>
     <xsl:value-of select="@id"/>
@@ -3225,14 +3261,14 @@
     <xsl:value-of select="@inkscape:label"/>
     <xsl:text>;
 </xsl:text>
-    <xsl:apply-templates select="*[@inkscape:label]" mode="json_table_render">
+    <xsl:apply-templates mode="json_table_render" select="*[@inkscape:label]">
       <xsl:with-param name="objname">
         <xsl:text>obj_</xsl:text>
         <xsl:value-of select="@id"/>
       </xsl:with-param>
     </xsl:apply-templates>
   </xsl:template>
-  <xsl:template match="widget[@type='JsonTable']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='JsonTable']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -3250,7 +3286,7 @@
     <xsl:variable name="data_elt" select="$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']"/>
     <xsl:text>    spread_json_data: function(jdata) {
 </xsl:text>
-    <xsl:apply-templates select="$data_elt/*" mode="json_table_render">
+    <xsl:apply-templates mode="json_table_render" select="$data_elt/*">
       <xsl:with-param name="objname" select="'jdata'"/>
     </xsl:apply-templates>
     <xsl:text>    }
@@ -3276,7 +3312,7 @@
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
   </xsl:template>
-  <xsl:template match="widget[@type='Jump']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Jump']">
     <xsl:param name="hmi_element"/>
     <xsl:variable name="activity">
       <xsl:call-template name="jump_widget_activity">
@@ -3411,7 +3447,7 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Jump']" mode="per_page_widget_template">
+  <xsl:template mode="per_page_widget_template" match="widget[@type='Jump']">
     <xsl:param name="page_desc"/>
     <xsl:if test="path">
       <xsl:variable name="target_page_name">
@@ -3513,7 +3549,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Keypad']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Keypad']">
     <xsl:text>class KeypadWidget extends Widget{
 </xsl:text>
     <xsl:text>     moving = undefined;
@@ -3805,7 +3841,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Keypad']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Keypad']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -3884,7 +3920,7 @@
     <xsl:text>],
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='List']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='List']">
     <xsl:param name="hmi_element"/>
     <xsl:text>    items: {
 </xsl:text>
@@ -3899,7 +3935,7 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Meter']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Meter']">
     <xsl:param name="hmi_element"/>
     <xsl:text>    frequency: 10,
 </xsl:text>
@@ -3957,7 +3993,7 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='MultiState']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='MultiState']">
     <xsl:text>class MultiStateWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -4024,7 +4060,7 @@
 </xsl:text>
     <xsl:text>        //post value to plc
 </xsl:text>
-    <xsl:text>        change_hmi_value(this.indexes[0], "="+this.state);
+    <xsl:text>        this.apply_hmi_value(0, this.state);
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -4039,7 +4075,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='MultiState']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='MultiState']">
     <xsl:param name="hmi_element"/>
     <xsl:text>    choices: [
 </xsl:text>
@@ -4070,7 +4106,7 @@
     <xsl:text>    ],
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Slider']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Slider']">
     <xsl:text>class SliderWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -4083,19 +4119,43 @@
 </xsl:text>
     <xsl:text>    enTimer = false;
 </xsl:text>
+    <xsl:text>    svg_dist = 0
+</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.handle_position(value);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    handle_position(value){
+</xsl:text>
+    <xsl:text>        let [min,max,start,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.handle_elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    on_release(evt) {
+</xsl:text>
+    <xsl:text>        if(this.drag){
+</xsl:text>
+    <xsl:text>            this.drag = false;
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -4103,214 +4163,202 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    handle_position(value){
-</xsl:text>
-    <xsl:text>        let [min,max,start,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.handle_elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
+    <xsl:text>    update_position(evt){
+</xsl:text>
+    <xsl:text>        if(this.drag){
+</xsl:text>
+    <xsl:text>            var html_dist = 0;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //calculate size of widget in html
+</xsl:text>
+    <xsl:text>            var range_borders = this.range_elt.getBoundingClientRect();
+</xsl:text>
+    <xsl:text>            var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
+</xsl:text>
+    <xsl:text>            var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //get range and mouse coordinates
+</xsl:text>
+    <xsl:text>            var mouseX = undefined;
+</xsl:text>
+    <xsl:text>            var mouseY = undefined;
+</xsl:text>
+    <xsl:text>            if (evt.type.startsWith("touch")){
+</xsl:text>
+    <xsl:text>                mouseX = Math.ceil(evt.touches[0].clientX);
+</xsl:text>
+    <xsl:text>                mouseY = Math.ceil(evt.touches[0].clientY);
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            else{
+</xsl:text>
+    <xsl:text>                mouseX = evt.pageX;
+</xsl:text>
+    <xsl:text>                mouseY = evt.pageY;
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //get handle distance from mouse position
+</xsl:text>
+    <xsl:text>            if (minX &gt; mouseX &amp;&amp; minY &lt; mouseY){
+</xsl:text>
+    <xsl:text>                html_dist = 0;
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            else if (maxX &lt; mouseX &amp;&amp; maxY &gt; mouseY){
+</xsl:text>
+    <xsl:text>                html_dist = range_length;
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>            else{
+</xsl:text>
+    <xsl:text>                // calculate distace
+</xsl:text>
+    <xsl:text>                if(this.fi &gt; 0.7){
+</xsl:text>
+    <xsl:text>                    html_dist = (minY - mouseY)/Math.sin(this.fi);
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else{
+</xsl:text>
+    <xsl:text>                    html_dist = (mouseX - minX)/Math.cos(this.fi);
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>                //check if in range
+</xsl:text>
+    <xsl:text>                if (html_dist &gt; range_length){
+</xsl:text>
+    <xsl:text>                    html_dist = range_length;
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>                else if (html_dist &lt; 0){
+</xsl:text>
+    <xsl:text>                    html_dist = 0;
+</xsl:text>
+    <xsl:text>                }
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            this.svg_dist=(html_dist/range_length)*this.range[1];
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            //redraw handle
+</xsl:text>
+    <xsl:text>            //this.handle_position(svg_dist=(html_dist/range_length)*this.range[1]);
+</xsl:text>
+    <xsl:text>            //this.value_elt.textContent = String(Math.ceil(svg_dist));
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            if(this.enTimer){
+</xsl:text>
+    <xsl:text>                this.apply_hmi_value(0, Math.ceil(this.svg_dist));
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>                // TODO : update ghost cursor and call this.request_animate()
+</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>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    on_release(evt) {
-</xsl:text>
-    <xsl:text>        if(this.drag){
-</xsl:text>
-    <xsl:text>            this.drag = false;
-</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>        this.update_position(evt);
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    update_position(evt){
-</xsl:text>
-    <xsl:text>        if(this.drag &amp;&amp; this.enTimer){
-</xsl:text>
-    <xsl:text>            var html_dist = 0;
-</xsl:text>
-    <xsl:text>            var svg_dist = 0;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            //calculate size of widget in html
-</xsl:text>
-    <xsl:text>            var range_borders = this.range_elt.getBoundingClientRect();
-</xsl:text>
-    <xsl:text>            var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
-</xsl:text>
-    <xsl:text>            var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            //get range and mouse coordinates
-</xsl:text>
-    <xsl:text>            var mouseX = undefined;
-</xsl:text>
-    <xsl:text>            var mouseY = undefined;
-</xsl:text>
-    <xsl:text>            if (evt.type.startsWith("touch")){
-</xsl:text>
-    <xsl:text>                mouseX = Math.ceil(evt.touches[0].clientX);
-</xsl:text>
-    <xsl:text>                mouseY = Math.ceil(evt.touches[0].clientY);
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>            else{
-</xsl:text>
-    <xsl:text>                mouseX = evt.pageX;
-</xsl:text>
-    <xsl:text>                mouseY = evt.pageY;
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            //get handle distance from mouse position
-</xsl:text>
-    <xsl:text>            if (minX &gt; mouseX &amp;&amp; minY &lt; mouseY){
-</xsl:text>
-    <xsl:text>                html_dist = 0;
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>            else if (maxX &lt; mouseX &amp;&amp; maxY &gt; mouseY){
-</xsl:text>
-    <xsl:text>                html_dist = range_length;
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>            else{
-</xsl:text>
-    <xsl:text>                // calculate distace
-</xsl:text>
-    <xsl:text>                if(this.fi &gt; 0.7){
-</xsl:text>
-    <xsl:text>                    html_dist = (minY - mouseY)/Math.sin(this.fi);
-</xsl:text>
-    <xsl:text>                }
-</xsl:text>
-    <xsl:text>                else{
-</xsl:text>
-    <xsl:text>                    html_dist = (mouseX - minX)/Math.cos(this.fi);
-</xsl:text>
-    <xsl:text>                }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>                //check if in range
-</xsl:text>
-    <xsl:text>                if (html_dist &gt; range_length){
-</xsl:text>
-    <xsl:text>                    html_dist = range_length;
-</xsl:text>
-    <xsl:text>                }
-</xsl:text>
-    <xsl:text>                else if (html_dist &lt; 0){
-</xsl:text>
-    <xsl:text>                    html_dist = 0;
-</xsl:text>
-    <xsl:text>                }
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>            //redraw handle
-</xsl:text>
-    <xsl:text>            this.handle_position(svg_dist=(html_dist/range_length)*this.range[1]);
-</xsl:text>
-    <xsl:text>            this.value_elt.textContent = String(Math.ceil(svg_dist));
-</xsl:text>
-    <xsl:text>            change_hmi_value(this.indexes[0], "="+Math.ceil(svg_dist));
-</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>    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>
+</xsl:text>
+    <xsl:text>        this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
+</xsl:text>
+    <xsl:text>        let start = this.range_elt.getPointAtLength(0);
+</xsl:text>
+    <xsl:text>        let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
+</xsl:text>
+    <xsl:text>        this.fi = Math.atan2(start.y-end.y, end.x-start.x);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>
+</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>
+    <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>
-</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>        this.update_position(evt);
-</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>
-</xsl:text>
-    <xsl:text>        this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
-</xsl:text>
-    <xsl:text>        let start = this.range_elt.getPointAtLength(0);
-</xsl:text>
-    <xsl:text>        let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
-</xsl:text>
-    <xsl:text>        this.fi = Math.atan2(start.y-end.y, end.x-start.x);
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>
-</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>
-    <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>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Slider']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Slider']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -4328,7 +4376,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Switch']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='Switch']">
     <xsl:text>class SwitchWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -4354,7 +4402,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='Switch']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='Switch']">
     <xsl:param name="hmi_element"/>
     <xsl:text>    choices: [
 </xsl:text>
@@ -4385,7 +4433,7 @@
     <xsl:text>    ],
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='ToggleButton']" mode="widget_class">
+  <xsl:template mode="widget_class" match="widget[@type='ToggleButton']">
     <xsl:text>class ToggleButtonWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -4426,7 +4474,7 @@
 </xsl:text>
     <xsl:text>    on_click(evt) {
 </xsl:text>
-    <xsl:text>        change_hmi_value(this.indexes[0], "="+this.state);
+    <xsl:text>        this.apply_hmi_value(0, this.state);
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -4445,7 +4493,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template match="widget[@type='ToggleButton']" mode="widget_defs">
+  <xsl:template mode="widget_defs" match="widget[@type='ToggleButton']">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -4656,6 +4704,14 @@
 </xsl:text>
           <xsl:text>    apply_updates();
 </xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
+</xsl:text>
+          <xsl:text>    pending_widget_animates = [];
+</xsl:text>
+          <xsl:text>
+</xsl:text>
           <xsl:text>    requestAnimationFrameID = null;
 </xsl:text>
           <xsl:text>}
@@ -5252,7 +5308,7 @@
 </xsl:text>
           <xsl:text>var edit_callback;
 </xsl:text>
-          <xsl:text>function edit_value(path, valuetype, callback, initial,size) {
+          <xsl:text>function edit_value(path, valuetype, callback, initial, size) {
 </xsl:text>
           <xsl:text>
 </xsl:text>
@@ -5264,7 +5320,7 @@
 </xsl:text>
           <xsl:text>    let widget = hmi_widgets[keypadid];
 </xsl:text>
-          <xsl:text>    widget.start_edit(path, valuetype, callback, initial,size);
+          <xsl:text>    widget.start_edit(path, valuetype, callback, initial, size);
 </xsl:text>
           <xsl:text>};
 </xsl:text>
--- a/svghmi/svghmi.js	Mon Aug 10 11:30:06 2020 +0200
+++ b/svghmi/svghmi.js	Mon Aug 10 13:58:55 2020 +0200
@@ -76,6 +76,10 @@
     if(jumps_need_update) update_jumps();
 
     apply_updates();
+
+    pending_widget_animates.forEach(widget => widget._animate());
+    pending_widget_animates = [];
+
     requestAnimationFrameID = null;
 }
 
--- a/svghmi/widgets_common.ysl2	Mon Aug 10 11:30:06 2020 +0200
+++ b/svghmi/widgets_common.ysl2	Mon Aug 10 13:58:55 2020 +0200
@@ -64,10 +64,14 @@
 
 emit "preamble:widget-base-class" {
     ||    
+    var pending_widget_animates = [];
+
     class Widget {
         offset = 0;
         frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */
         unsubscribable = false;
+        pending_animate = false;
+
         constructor(elt_id,args,indexes,members){
             this.element_id = elt_id;
             this.element = id(elt_id);
@@ -152,6 +156,20 @@
                 console.log(err);
             }
         }
+        
+        _animate(){
+            this.animate();
+            this.pending_animate = false;
+        }
+
+        request_animate(){
+            if(!this.pending_animate){
+                pending_widget_animates.push(this);
+                this.pending_animate = true;
+                requestHMIAnimation();
+            }
+
+        }
     }
     ||
 }