svghmi/gen_index_xhtml.xslt
branchsvghmi
changeset 2941 ef13a4007538
parent 2940 034b6ce4f885
child 2942 b07ad97e6019
equal deleted inserted replaced
2940:034b6ce4f885 2941:ef13a4007538
     1 <?xml version="1.0"?>
     1 <?xml version="1.0"?>
     2 <xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:epilogue="epilogue" xmlns:svg="http://www.w3.org/2000/svg" xmlns:str="http://exslt.org/strings" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:exsl="http://exslt.org/common" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ns="beremiz" xmlns:cc="http://creativecommons.org/ns#" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:debug="debug" xmlns:dc="http://purl.org/dc/elements/1.1/" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue">
     2 <xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:epilogue="epilogue" xmlns:svg="http://www.w3.org/2000/svg" xmlns:str="http://exslt.org/strings" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:exsl="http://exslt.org/common" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ns="beremiz" xmlns:cc="http://creativecommons.org/ns#" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:debug="debug" xmlns:dc="http://purl.org/dc/elements/1.1/" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue">
     3   <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
     3   <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
       
     4   <xsl:variable name="svg" select="/svg:svg"/>
     4   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     5   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     5   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     6   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     6   <xsl:variable name="_categories">
     7   <xsl:variable name="_categories">
     7     <noindex>
     8     <noindex>
     8       <xsl:text>HMI_PLC_STATUS</xsl:text>
     9       <xsl:text>HMI_PLC_STATUS</xsl:text>
    14   <xsl:variable name="categories" select="exsl:node-set($_categories)"/>
    15   <xsl:variable name="categories" select="exsl:node-set($_categories)"/>
    15   <xsl:variable name="_indexed_hmitree">
    16   <xsl:variable name="_indexed_hmitree">
    16     <xsl:apply-templates mode="index" select="$hmitree"/>
    17     <xsl:apply-templates mode="index" select="$hmitree"/>
    17   </xsl:variable>
    18   </xsl:variable>
    18   <xsl:variable name="indexed_hmitree" select="exsl:node-set($_indexed_hmitree)"/>
    19   <xsl:variable name="indexed_hmitree" select="exsl:node-set($_indexed_hmitree)"/>
       
    20   <preamble:hmi-tree/>
       
    21   <xsl:template match="preamble:hmi-tree">
       
    22     <xsl:text>var hmi_hash = [</xsl:text>
       
    23     <xsl:value-of select="$hmitree/@hash"/>
       
    24     <xsl:text>];
       
    25 </xsl:text>
       
    26     <xsl:text>
       
    27 </xsl:text>
       
    28     <xsl:text>var heartbeat_index = </xsl:text>
       
    29     <xsl:value-of select="$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index"/>
       
    30     <xsl:text>;
       
    31 </xsl:text>
       
    32     <xsl:text>
       
    33 </xsl:text>
       
    34     <xsl:text>var hmitree_types = [
       
    35 </xsl:text>
       
    36     <xsl:for-each select="$indexed_hmitree/*">
       
    37       <xsl:text>    /* </xsl:text>
       
    38       <xsl:value-of select="@index"/>
       
    39       <xsl:text>  </xsl:text>
       
    40       <xsl:value-of select="@hmipath"/>
       
    41       <xsl:text> */ "</xsl:text>
       
    42       <xsl:value-of select="substring(local-name(), 5)"/>
       
    43       <xsl:text>"</xsl:text>
       
    44       <xsl:if test="position()!=last()">
       
    45         <xsl:text>,</xsl:text>
       
    46       </xsl:if>
       
    47       <xsl:text>
       
    48 </xsl:text>
       
    49     </xsl:for-each>
       
    50     <xsl:text>]
       
    51 </xsl:text>
       
    52     <xsl:text>
       
    53 </xsl:text>
       
    54   </xsl:template>
    19   <xsl:template mode="index" match="*">
    55   <xsl:template mode="index" match="*">
    20     <xsl:param name="index" select="0"/>
    56     <xsl:param name="index" select="0"/>
    21     <xsl:param name="parentpath" select="''"/>
    57     <xsl:param name="parentpath" select="''"/>
    22     <xsl:variable name="content">
    58     <xsl:variable name="content">
    23       <xsl:variable name="path">
    59       <xsl:variable name="path">
   260     <xsl:variable name="candidates" select="$geometry[@Id != $elt/@id]"/>
   296     <xsl:variable name="candidates" select="$geometry[@Id != $elt/@id]"/>
   261     <func:result select="$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or &#10;                          (not(@Id = $groups/@id) and (func:intersect($g, .) &gt; 0 ))]"/>
   297     <func:result select="$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or &#10;                          (not(@Id = $groups/@id) and (func:intersect($g, .) &gt; 0 ))]"/>
   262   </func:function>
   298   </func:function>
   263   <xsl:variable name="hmi_pages_descs" select="$parsed_widgets/widget[@type = 'Page']"/>
   299   <xsl:variable name="hmi_pages_descs" select="$parsed_widgets/widget[@type = 'Page']"/>
   264   <xsl:variable name="hmi_pages" select="$hmi_elements[@id = $hmi_pages_descs/@id]"/>
   300   <xsl:variable name="hmi_pages" select="$hmi_elements[@id = $hmi_pages_descs/@id]"/>
   265   <xsl:variable name="keypads_descs" select="$parsed_widgets/widget[@type = 'Keypad']"/>
       
   266   <xsl:variable name="keypads" select="$hmi_elements[@id = $keypads_descs/@id]"/>
       
   267   <xsl:variable name="default_page">
   301   <xsl:variable name="default_page">
   268     <xsl:choose>
   302     <xsl:choose>
   269       <xsl:when test="count($hmi_pages) &gt; 1">
   303       <xsl:when test="count($hmi_pages) &gt; 1">
   270         <xsl:choose>
   304         <xsl:choose>
   271           <xsl:when test="$hmi_pages_descs/arg[1]/@value = 'Home'">
   305           <xsl:when test="$hmi_pages_descs/arg[1]/@value = 'Home'">
   282       <xsl:otherwise>
   316       <xsl:otherwise>
   283         <xsl:value-of select="func:widget($hmi_pages/@id)/arg[1]/@value"/>
   317         <xsl:value-of select="func:widget($hmi_pages/@id)/arg[1]/@value"/>
   284       </xsl:otherwise>
   318       </xsl:otherwise>
   285     </xsl:choose>
   319     </xsl:choose>
   286   </xsl:variable>
   320   </xsl:variable>
       
   321   <preamble:default-page/>
       
   322   <xsl:template match="preamble:default-page">
       
   323     <xsl:text>
       
   324 </xsl:text>
       
   325     <xsl:text>var default_page = "</xsl:text>
       
   326     <xsl:value-of select="$default_page"/>
       
   327     <xsl:text>";
       
   328 </xsl:text>
       
   329   </xsl:template>
       
   330   <xsl:variable name="keypads_descs" select="$parsed_widgets/widget[@type = 'Keypad']"/>
       
   331   <xsl:variable name="keypads" select="$hmi_elements[@id = $keypads_descs/@id]"/>
   287   <func:function name="func:refered_elements">
   332   <func:function name="func:refered_elements">
   288     <xsl:param name="elems"/>
   333     <xsl:param name="elems"/>
   289     <xsl:variable name="descend" select="$elems/descendant-or-self::svg:*"/>
   334     <xsl:variable name="descend" select="$elems/descendant-or-self::svg:*"/>
   290     <xsl:variable name="clones" select="$descend[self::svg:use]"/>
   335     <xsl:variable name="clones" select="$descend[self::svg:use]"/>
   291     <xsl:variable name="originals" select="//svg:*[concat('#',@id) = $clones/@xlink:href]"/>
   336     <xsl:variable name="originals" select="//svg:*[concat('#',@id) = $clones/@xlink:href]"/>
   336       </xsl:otherwise>
   381       </xsl:otherwise>
   337     </xsl:choose>
   382     </xsl:choose>
   338   </func:function>
   383   </func:function>
   339   <xsl:variable name="_detachable_elements" select="func:detachable_elements($hmi_pages | $keypads)"/>
   384   <xsl:variable name="_detachable_elements" select="func:detachable_elements($hmi_pages | $keypads)"/>
   340   <xsl:variable name="detachable_elements" select="$_detachable_elements[not(ancestor::*/@id = $_detachable_elements/@id)]"/>
   385   <xsl:variable name="detachable_elements" select="$_detachable_elements[not(ancestor::*/@id = $_detachable_elements/@id)]"/>
       
   386   <epilogue:detachable-elements/>
       
   387   <xsl:template match="epilogue:detachable-elements">
       
   388     <xsl:text>
       
   389 </xsl:text>
       
   390     <xsl:text>var detachable_elements = {
       
   391 </xsl:text>
       
   392     <xsl:for-each select="$detachable_elements">
       
   393       <xsl:text>    "</xsl:text>
       
   394       <xsl:value-of select="@id"/>
       
   395       <xsl:text>":[id("</xsl:text>
       
   396       <xsl:value-of select="@id"/>
       
   397       <xsl:text>"), id("</xsl:text>
       
   398       <xsl:value-of select="../@id"/>
       
   399       <xsl:text>")]</xsl:text>
       
   400       <xsl:if test="position()!=last()">
       
   401         <xsl:text>,</xsl:text>
       
   402       </xsl:if>
       
   403       <xsl:text>
       
   404 </xsl:text>
       
   405     </xsl:for-each>
       
   406     <xsl:text>}
       
   407 </xsl:text>
       
   408   </xsl:template>
   341   <xsl:variable name="forEach_widgets_ids" select="$parsed_widgets/widget[@type = 'ForEach']/@id"/>
   409   <xsl:variable name="forEach_widgets_ids" select="$parsed_widgets/widget[@type = 'ForEach']/@id"/>
   342   <xsl:variable name="forEach_widgets" select="$hmi_elements[@id = $forEach_widgets_ids]"/>
   410   <xsl:variable name="forEach_widgets" select="$hmi_elements[@id = $forEach_widgets_ids]"/>
   343   <xsl:variable name="in_forEach_widget_ids" select="func:refered_elements($forEach_widgets)[not(@id = $forEach_widgets_ids)]/@id"/>
   411   <xsl:variable name="in_forEach_widget_ids" select="func:refered_elements($forEach_widgets)[not(@id = $forEach_widgets_ids)]/@id"/>
   344   <xsl:template mode="page_desc" match="svg:*">
   412   <xsl:template mode="page_desc" match="svg:*">
   345     <xsl:variable name="desc" select="func:widget(@id)"/>
   413     <xsl:variable name="desc" select="func:widget(@id)"/>
   457       <xsl:text>,</xsl:text>
   525       <xsl:text>,</xsl:text>
   458     </xsl:if>
   526     </xsl:if>
   459     <xsl:text>
   527     <xsl:text>
   460 </xsl:text>
   528 </xsl:text>
   461   </xsl:template>
   529   </xsl:template>
       
   530   <epilogue:page-desc/>
       
   531   <xsl:template match="epilogue:page-desc">
       
   532     <xsl:text>
       
   533 </xsl:text>
       
   534     <xsl:text>var page_desc = {
       
   535 </xsl:text>
       
   536     <xsl:apply-templates mode="page_desc" select="$hmi_pages"/>
       
   537     <xsl:text>}
       
   538 </xsl:text>
       
   539   </xsl:template>
   462   <xsl:template mode="per_page_widget_template" match="*"/>
   540   <xsl:template mode="per_page_widget_template" match="*"/>
   463   <debug:detachable-pages/>
   541   <debug:detachable-pages/>
   464   <xsl:template match="debug:detachable-pages">
   542   <xsl:template match="debug:detachable-pages">
       
   543     <xsl:text>
       
   544 </xsl:text>
   465     <xsl:text>DETACHABLES:
   545     <xsl:text>DETACHABLES:
   466 </xsl:text>
   546 </xsl:text>
   467     <xsl:for-each select="$detachable_elements">
   547     <xsl:for-each select="$detachable_elements">
   468       <xsl:text> </xsl:text>
   548       <xsl:text> </xsl:text>
   469       <xsl:value-of select="@id"/>
   549       <xsl:value-of select="@id"/>
   588   </xsl:template>
   668   </xsl:template>
   589   <xsl:variable name="result_svg">
   669   <xsl:variable name="result_svg">
   590     <xsl:apply-templates mode="inline_svg" select="/"/>
   670     <xsl:apply-templates mode="inline_svg" select="/"/>
   591   </xsl:variable>
   671   </xsl:variable>
   592   <xsl:variable name="result_svg_ns" select="exsl:node-set($result_svg)"/>
   672   <xsl:variable name="result_svg_ns" select="exsl:node-set($result_svg)"/>
   593   <debug:inline-svg/>
   673   <preamble:inline-svg/>
   594   <xsl:template match="debug:inline-svg">
   674   <xsl:template match="preamble:inline-svg">
       
   675     <xsl:text>let id = document.getElementById.bind(document);
       
   676 </xsl:text>
       
   677     <xsl:text>var svg_root = id("</xsl:text>
       
   678     <xsl:value-of select="$svg/@id"/>
       
   679     <xsl:text>");
       
   680 </xsl:text>
       
   681   </xsl:template>
       
   682   <debug:clone-unlinking/>
       
   683   <xsl:template match="debug:clone-unlinking">
       
   684     <xsl:text>
       
   685 </xsl:text>
   595     <xsl:text>Unlinked :
   686     <xsl:text>Unlinked :
   596 </xsl:text>
   687 </xsl:text>
   597     <xsl:for-each select="$to_unlink">
   688     <xsl:for-each select="$to_unlink">
   598       <xsl:value-of select="@id"/>
   689       <xsl:value-of select="@id"/>
   599       <xsl:text>
   690       <xsl:text>
   600 </xsl:text>
   691 </xsl:text>
   601     </xsl:for-each>
   692     </xsl:for-each>
   602   </xsl:template>
       
   603   <preamble:hmi-widget/>
       
   604   <xsl:template match="preamble:hmi-widget">
       
   605     <xsl:text>var hmi_widgets = {
       
   606 </xsl:text>
       
   607     <xsl:apply-templates mode="hmi_elements" select="$hmi_elements"/>
       
   608     <xsl:text>}
       
   609 </xsl:text>
       
   610   </xsl:template>
   693   </xsl:template>
   611   <xsl:template mode="hmi_elements" match="svg:*">
   694   <xsl:template mode="hmi_elements" match="svg:*">
   612     <xsl:variable name="widget" select="func:widget(@id)"/>
   695     <xsl:variable name="widget" select="func:widget(@id)"/>
   613     <xsl:variable name="eltid" select="@id"/>
   696     <xsl:variable name="eltid" select="@id"/>
   614     <xsl:text>  "</xsl:text>
   697     <xsl:text>  "</xsl:text>
   679     <xsl:text>  }</xsl:text>
   762     <xsl:text>  }</xsl:text>
   680     <xsl:if test="position()!=last()">
   763     <xsl:if test="position()!=last()">
   681       <xsl:text>,</xsl:text>
   764       <xsl:text>,</xsl:text>
   682     </xsl:if>
   765     </xsl:if>
   683     <xsl:text>
   766     <xsl:text>
       
   767 </xsl:text>
       
   768   </xsl:template>
       
   769   <preamble:hmi-elements/>
       
   770   <xsl:template match="preamble:hmi-elements">
       
   771     <xsl:text>var hmi_widgets = {
       
   772 </xsl:text>
       
   773     <xsl:apply-templates mode="hmi_elements" select="$hmi_elements"/>
       
   774     <xsl:text>}
   684 </xsl:text>
   775 </xsl:text>
   685   </xsl:template>
   776   </xsl:template>
   686   <xsl:template mode="widget_subscribe" match="widget">
   777   <xsl:template mode="widget_subscribe" match="widget">
   687     <xsl:text>    sub: subscribe,
   778     <xsl:text>    sub: subscribe,
   688 </xsl:text>
   779 </xsl:text>
  1754           <xsl:text>")</xsl:text>
  1845           <xsl:text>")</xsl:text>
  1755         </xsl:message>
  1846         </xsl:message>
  1756       </xsl:if>
  1847       </xsl:if>
  1757     </xsl:if>
  1848     </xsl:if>
  1758   </xsl:template>
  1849   </xsl:template>
       
  1850   <epilogue:keypad/>
       
  1851   <xsl:template match="epilogue:keypad">
       
  1852     <xsl:text>
       
  1853 </xsl:text>
       
  1854     <xsl:text>var keypads = {
       
  1855 </xsl:text>
       
  1856     <xsl:for-each select="$keypads_descs">
       
  1857       <xsl:variable name="keypad_id" select="@id"/>
       
  1858       <xsl:for-each select="arg">
       
  1859         <xsl:variable name="g" select="$geometry[@Id = $keypad_id]"/>
       
  1860         <xsl:text>    "</xsl:text>
       
  1861         <xsl:value-of select="@value"/>
       
  1862         <xsl:text>":["</xsl:text>
       
  1863         <xsl:value-of select="$keypad_id"/>
       
  1864         <xsl:text>", </xsl:text>
       
  1865         <xsl:value-of select="$g/@x"/>
       
  1866         <xsl:text>, </xsl:text>
       
  1867         <xsl:value-of select="$g/@y"/>
       
  1868         <xsl:text>],
       
  1869 </xsl:text>
       
  1870       </xsl:for-each>
       
  1871     </xsl:for-each>
       
  1872     <xsl:text>}
       
  1873 </xsl:text>
       
  1874   </xsl:template>
  1759   <xsl:template mode="widget_defs" match="widget[@type='Keypad']">
  1875   <xsl:template mode="widget_defs" match="widget[@type='Keypad']">
  1760     <xsl:param name="hmi_element"/>
  1876     <xsl:param name="hmi_element"/>
  1761     <xsl:call-template name="defs_by_labels">
  1877     <xsl:call-template name="defs_by_labels">
  1762       <xsl:with-param name="hmi_element" select="$hmi_element"/>
  1878       <xsl:with-param name="hmi_element" select="$hmi_element"/>
  1763       <xsl:with-param name="labels">
  1879       <xsl:with-param name="labels">
  2078 </xsl:text>
  2194 </xsl:text>
  2079     </xsl:for-each>
  2195     </xsl:for-each>
  2080     <xsl:text>    ],
  2196     <xsl:text>    ],
  2081 </xsl:text>
  2197 </xsl:text>
  2082   </xsl:template>
  2198   </xsl:template>
  2083   <xsl:template name="scripts">
       
  2084     <xsl:text>
       
  2085 </xsl:text>
       
  2086     <xsl:text>id = idstr =&gt; document.getElementById(idstr);
       
  2087 </xsl:text>
       
  2088     <xsl:text>
       
  2089 </xsl:text>
       
  2090     <xsl:apply-templates select="document('')/*/preamble:*"/>
       
  2091     <xsl:text>var hmi_hash = [</xsl:text>
       
  2092     <xsl:value-of select="$hmitree/@hash"/>
       
  2093     <xsl:text>];
       
  2094 </xsl:text>
       
  2095     <xsl:text>
       
  2096 </xsl:text>
       
  2097     <xsl:text>var heartbeat_index = </xsl:text>
       
  2098     <xsl:value-of select="$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index"/>
       
  2099     <xsl:text>;
       
  2100 </xsl:text>
       
  2101     <xsl:text>
       
  2102 </xsl:text>
       
  2103     <xsl:text>var hmitree_types = [
       
  2104 </xsl:text>
       
  2105     <xsl:for-each select="$indexed_hmitree/*">
       
  2106       <xsl:text>    /* </xsl:text>
       
  2107       <xsl:value-of select="@index"/>
       
  2108       <xsl:text>  </xsl:text>
       
  2109       <xsl:value-of select="@hmipath"/>
       
  2110       <xsl:text> */ "</xsl:text>
       
  2111       <xsl:value-of select="substring(local-name(), 5)"/>
       
  2112       <xsl:text>"</xsl:text>
       
  2113       <xsl:if test="position()!=last()">
       
  2114         <xsl:text>,</xsl:text>
       
  2115       </xsl:if>
       
  2116       <xsl:text>
       
  2117 </xsl:text>
       
  2118     </xsl:for-each>
       
  2119     <xsl:text>]
       
  2120 </xsl:text>
       
  2121     <xsl:text>
       
  2122 </xsl:text>
       
  2123     <xsl:text>var detachable_elements = {
       
  2124 </xsl:text>
       
  2125     <xsl:for-each select="$detachable_elements">
       
  2126       <xsl:text>    "</xsl:text>
       
  2127       <xsl:value-of select="@id"/>
       
  2128       <xsl:text>":[id("</xsl:text>
       
  2129       <xsl:value-of select="@id"/>
       
  2130       <xsl:text>"), id("</xsl:text>
       
  2131       <xsl:value-of select="../@id"/>
       
  2132       <xsl:text>")]</xsl:text>
       
  2133       <xsl:if test="position()!=last()">
       
  2134         <xsl:text>,</xsl:text>
       
  2135       </xsl:if>
       
  2136       <xsl:text>
       
  2137 </xsl:text>
       
  2138     </xsl:for-each>
       
  2139     <xsl:text>}
       
  2140 </xsl:text>
       
  2141     <xsl:text>
       
  2142 </xsl:text>
       
  2143     <xsl:text>var page_desc = {
       
  2144 </xsl:text>
       
  2145     <xsl:apply-templates mode="page_desc" select="$hmi_pages"/>
       
  2146     <xsl:text>}
       
  2147 </xsl:text>
       
  2148     <xsl:text>var keypads = {
       
  2149 </xsl:text>
       
  2150     <xsl:for-each select="$keypads_descs">
       
  2151       <xsl:variable name="keypad_id" select="@id"/>
       
  2152       <xsl:for-each select="arg">
       
  2153         <xsl:variable name="g" select="$geometry[@Id = $keypad_id]"/>
       
  2154         <xsl:text>    "</xsl:text>
       
  2155         <xsl:value-of select="@value"/>
       
  2156         <xsl:text>":["</xsl:text>
       
  2157         <xsl:value-of select="$keypad_id"/>
       
  2158         <xsl:text>", </xsl:text>
       
  2159         <xsl:value-of select="$g/@x"/>
       
  2160         <xsl:text>, </xsl:text>
       
  2161         <xsl:value-of select="$g/@y"/>
       
  2162         <xsl:text>],
       
  2163 </xsl:text>
       
  2164       </xsl:for-each>
       
  2165     </xsl:for-each>
       
  2166     <xsl:text>}
       
  2167 </xsl:text>
       
  2168     <xsl:text>
       
  2169 </xsl:text>
       
  2170     <xsl:text>var default_page = "</xsl:text>
       
  2171     <xsl:value-of select="$default_page"/>
       
  2172     <xsl:text>";
       
  2173 </xsl:text>
       
  2174     <xsl:text>var svg_root = id("</xsl:text>
       
  2175     <xsl:value-of select="/svg:svg/@id"/>
       
  2176     <xsl:text>");
       
  2177 </xsl:text>
       
  2178     <xsl:text>// svghmi.js
       
  2179 </xsl:text>
       
  2180     <xsl:text>
       
  2181 </xsl:text>
       
  2182     <xsl:text>var cache = hmitree_types.map(_ignored =&gt; undefined);
       
  2183 </xsl:text>
       
  2184     <xsl:text>var updates = {};
       
  2185 </xsl:text>
       
  2186     <xsl:text>var need_cache_apply = []; 
       
  2187 </xsl:text>
       
  2188     <xsl:text>var jumps_need_update = false;
       
  2189 </xsl:text>
       
  2190     <xsl:text>var jump_history = [[default_page, undefined]];
       
  2191 </xsl:text>
       
  2192     <xsl:text>
       
  2193 </xsl:text>
       
  2194     <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
       
  2195 </xsl:text>
       
  2196     <xsl:text>    try {
       
  2197 </xsl:text>
       
  2198     <xsl:text>        let idx = widget.offset ? index - widget.offset : index;
       
  2199 </xsl:text>
       
  2200     <xsl:text>        let idxidx = widget.indexes.indexOf(idx);
       
  2201 </xsl:text>
       
  2202     <xsl:text>        let d = widget.dispatch;
       
  2203 </xsl:text>
       
  2204     <xsl:text>        if(typeof(d) == "function" &amp;&amp; idxidx == 0){
       
  2205 </xsl:text>
       
  2206     <xsl:text>            d.call(widget, value, oldval);
       
  2207 </xsl:text>
       
  2208     <xsl:text>        }
       
  2209 </xsl:text>
       
  2210     <xsl:text>        else if(typeof(d) == "object" &amp;&amp; d.length &gt;= idxidx){
       
  2211 </xsl:text>
       
  2212     <xsl:text>            d[idxidx].call(widget, value, oldval);
       
  2213 </xsl:text>
       
  2214     <xsl:text>        }
       
  2215 </xsl:text>
       
  2216     <xsl:text>        /* else dispatch_0, ..., dispatch_n ? */
       
  2217 </xsl:text>
       
  2218     <xsl:text>        /*else {
       
  2219 </xsl:text>
       
  2220     <xsl:text>            throw new Error("Dunno how to dispatch to widget at index = " + index);
       
  2221 </xsl:text>
       
  2222     <xsl:text>        }*/
       
  2223 </xsl:text>
       
  2224     <xsl:text>    } catch(err) {
       
  2225 </xsl:text>
       
  2226     <xsl:text>        console.log(err);
       
  2227 </xsl:text>
       
  2228     <xsl:text>    }
       
  2229 </xsl:text>
       
  2230     <xsl:text>}
       
  2231 </xsl:text>
       
  2232     <xsl:text>
       
  2233 </xsl:text>
       
  2234     <xsl:text>function dispatch_value(index, value) {
       
  2235 </xsl:text>
       
  2236     <xsl:text>    let widgets = subscribers[index];
       
  2237 </xsl:text>
       
  2238     <xsl:text>
       
  2239 </xsl:text>
       
  2240     <xsl:text>    let oldval = cache[index];
       
  2241 </xsl:text>
       
  2242     <xsl:text>    cache[index] = value;
       
  2243 </xsl:text>
       
  2244     <xsl:text>
       
  2245 </xsl:text>
       
  2246     <xsl:text>    if(widgets.size &gt; 0) {
       
  2247 </xsl:text>
       
  2248     <xsl:text>        for(let widget of widgets){
       
  2249 </xsl:text>
       
  2250     <xsl:text>            dispatch_value_to_widget(widget, index, value, oldval);
       
  2251 </xsl:text>
       
  2252     <xsl:text>        }
       
  2253 </xsl:text>
       
  2254     <xsl:text>    }
       
  2255 </xsl:text>
       
  2256     <xsl:text>};
       
  2257 </xsl:text>
       
  2258     <xsl:text>
       
  2259 </xsl:text>
       
  2260     <xsl:text>function init_widgets() {
       
  2261 </xsl:text>
       
  2262     <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
       
  2263 </xsl:text>
       
  2264     <xsl:text>        let widget = hmi_widgets[id];
       
  2265 </xsl:text>
       
  2266     <xsl:text>        let init = widget.init;
       
  2267 </xsl:text>
       
  2268     <xsl:text>        if(typeof(init) == "function"){
       
  2269 </xsl:text>
       
  2270     <xsl:text>            try {
       
  2271 </xsl:text>
       
  2272     <xsl:text>                init.call(widget);
       
  2273 </xsl:text>
       
  2274     <xsl:text>            } catch(err) {
       
  2275 </xsl:text>
       
  2276     <xsl:text>                console.log(err);
       
  2277 </xsl:text>
       
  2278     <xsl:text>            }
       
  2279 </xsl:text>
       
  2280     <xsl:text>        }
       
  2281 </xsl:text>
       
  2282     <xsl:text>    });
       
  2283 </xsl:text>
       
  2284     <xsl:text>};
       
  2285 </xsl:text>
       
  2286     <xsl:text>
       
  2287 </xsl:text>
       
  2288     <xsl:text>// Open WebSocket to relative "/ws" address
       
  2289 </xsl:text>
       
  2290     <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
       
  2291 </xsl:text>
       
  2292     <xsl:text>ws.binaryType = 'arraybuffer';
       
  2293 </xsl:text>
       
  2294     <xsl:text>
       
  2295 </xsl:text>
       
  2296     <xsl:text>const dvgetters = {
       
  2297 </xsl:text>
       
  2298     <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  2299 </xsl:text>
       
  2300     <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  2301 </xsl:text>
       
  2302     <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  2303 </xsl:text>
       
  2304     <xsl:text>    STRING: (dv, offset) =&gt; {
       
  2305 </xsl:text>
       
  2306     <xsl:text>        size = dv.getInt8(offset);
       
  2307 </xsl:text>
       
  2308     <xsl:text>        return [
       
  2309 </xsl:text>
       
  2310     <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  2311 </xsl:text>
       
  2312     <xsl:text>                dv.buffer, /* original buffer */
       
  2313 </xsl:text>
       
  2314     <xsl:text>                offset + 1, /* string starts after size*/
       
  2315 </xsl:text>
       
  2316     <xsl:text>                size /* size of string */
       
  2317 </xsl:text>
       
  2318     <xsl:text>            )), size + 1]; /* total increment */
       
  2319 </xsl:text>
       
  2320     <xsl:text>    }
       
  2321 </xsl:text>
       
  2322     <xsl:text>};
       
  2323 </xsl:text>
       
  2324     <xsl:text>
       
  2325 </xsl:text>
       
  2326     <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
       
  2327 </xsl:text>
       
  2328     <xsl:text>function apply_updates() {
       
  2329 </xsl:text>
       
  2330     <xsl:text>    for(let index in updates){
       
  2331 </xsl:text>
       
  2332     <xsl:text>        // serving as a key, index becomes a string
       
  2333 </xsl:text>
       
  2334     <xsl:text>        // -&gt; pass Number(index) instead
       
  2335 </xsl:text>
       
  2336     <xsl:text>        dispatch_value(Number(index), updates[index]);
       
  2337 </xsl:text>
       
  2338     <xsl:text>        delete updates[index];
       
  2339 </xsl:text>
       
  2340     <xsl:text>    }
       
  2341 </xsl:text>
       
  2342     <xsl:text>}
       
  2343 </xsl:text>
       
  2344     <xsl:text>
       
  2345 </xsl:text>
       
  2346     <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
  2347 </xsl:text>
       
  2348     <xsl:text>var requestAnimationFrameID = null;
       
  2349 </xsl:text>
       
  2350     <xsl:text>function animate() {
       
  2351 </xsl:text>
       
  2352     <xsl:text>    // Do the page swith if any one pending
       
  2353 </xsl:text>
       
  2354     <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  2355 </xsl:text>
       
  2356     <xsl:text>        switch_visible_page(current_subscribed_page);
       
  2357 </xsl:text>
       
  2358     <xsl:text>    }
       
  2359 </xsl:text>
       
  2360     <xsl:text>
       
  2361 </xsl:text>
       
  2362     <xsl:text>    while(widget = need_cache_apply.pop()){
       
  2363 </xsl:text>
       
  2364     <xsl:text>        widget.apply_cache();
       
  2365 </xsl:text>
       
  2366     <xsl:text>    }
       
  2367 </xsl:text>
       
  2368     <xsl:text>
       
  2369 </xsl:text>
       
  2370     <xsl:text>    if(jumps_need_update) update_jumps();
       
  2371 </xsl:text>
       
  2372     <xsl:text>
       
  2373 </xsl:text>
       
  2374     <xsl:text>    apply_updates();
       
  2375 </xsl:text>
       
  2376     <xsl:text>    requestAnimationFrameID = null;
       
  2377 </xsl:text>
       
  2378     <xsl:text>}
       
  2379 </xsl:text>
       
  2380     <xsl:text>
       
  2381 </xsl:text>
       
  2382     <xsl:text>function requestHMIAnimation() {
       
  2383 </xsl:text>
       
  2384     <xsl:text>    if(requestAnimationFrameID == null){
       
  2385 </xsl:text>
       
  2386     <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  2387 </xsl:text>
       
  2388     <xsl:text>    }
       
  2389 </xsl:text>
       
  2390     <xsl:text>}
       
  2391 </xsl:text>
       
  2392     <xsl:text>
       
  2393 </xsl:text>
       
  2394     <xsl:text>// Message reception handler
       
  2395 </xsl:text>
       
  2396     <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  2397 </xsl:text>
       
  2398     <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  2399 </xsl:text>
       
  2400     <xsl:text>ws.onmessage = function (evt) {
       
  2401 </xsl:text>
       
  2402     <xsl:text>
       
  2403 </xsl:text>
       
  2404     <xsl:text>    let data = evt.data;
       
  2405 </xsl:text>
       
  2406     <xsl:text>    let dv = new DataView(data);
       
  2407 </xsl:text>
       
  2408     <xsl:text>    let i = 0;
       
  2409 </xsl:text>
       
  2410     <xsl:text>    try {
       
  2411 </xsl:text>
       
  2412     <xsl:text>        for(let hash_int of hmi_hash) {
       
  2413 </xsl:text>
       
  2414     <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  2415 </xsl:text>
       
  2416     <xsl:text>                throw new Error("Hash doesn't match");
       
  2417 </xsl:text>
       
  2418     <xsl:text>            };
       
  2419 </xsl:text>
       
  2420     <xsl:text>            i++;
       
  2421 </xsl:text>
       
  2422     <xsl:text>        };
       
  2423 </xsl:text>
       
  2424     <xsl:text>
       
  2425 </xsl:text>
       
  2426     <xsl:text>        while(i &lt; data.byteLength){
       
  2427 </xsl:text>
       
  2428     <xsl:text>            let index = dv.getUint32(i, true);
       
  2429 </xsl:text>
       
  2430     <xsl:text>            i += 4;
       
  2431 </xsl:text>
       
  2432     <xsl:text>            let iectype = hmitree_types[index];
       
  2433 </xsl:text>
       
  2434     <xsl:text>            if(iectype != undefined){
       
  2435 </xsl:text>
       
  2436     <xsl:text>                let dvgetter = dvgetters[iectype];
       
  2437 </xsl:text>
       
  2438     <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  2439 </xsl:text>
       
  2440     <xsl:text>                updates[index] = value;
       
  2441 </xsl:text>
       
  2442     <xsl:text>                i += bytesize;
       
  2443 </xsl:text>
       
  2444     <xsl:text>            } else {
       
  2445 </xsl:text>
       
  2446     <xsl:text>                throw new Error("Unknown index "+index);
       
  2447 </xsl:text>
       
  2448     <xsl:text>            }
       
  2449 </xsl:text>
       
  2450     <xsl:text>        };
       
  2451 </xsl:text>
       
  2452     <xsl:text>        // register for rendering on next frame, since there are updates
       
  2453 </xsl:text>
       
  2454     <xsl:text>        requestHMIAnimation();
       
  2455 </xsl:text>
       
  2456     <xsl:text>    } catch(err) {
       
  2457 </xsl:text>
       
  2458     <xsl:text>        // 1003 is for "Unsupported Data"
       
  2459 </xsl:text>
       
  2460     <xsl:text>        // ws.close(1003, err.message);
       
  2461 </xsl:text>
       
  2462     <xsl:text>
       
  2463 </xsl:text>
       
  2464     <xsl:text>        // TODO : remove debug alert ?
       
  2465 </xsl:text>
       
  2466     <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  2467 </xsl:text>
       
  2468     <xsl:text>
       
  2469 </xsl:text>
       
  2470     <xsl:text>        // force reload ignoring cache
       
  2471 </xsl:text>
       
  2472     <xsl:text>        location.reload(true);
       
  2473 </xsl:text>
       
  2474     <xsl:text>    }
       
  2475 </xsl:text>
       
  2476     <xsl:text>};
       
  2477 </xsl:text>
       
  2478     <xsl:text>
       
  2479 </xsl:text>
       
  2480     <xsl:text>
       
  2481 </xsl:text>
       
  2482     <xsl:text>function send_blob(data) {
       
  2483 </xsl:text>
       
  2484     <xsl:text>    if(data.length &gt; 0) {
       
  2485 </xsl:text>
       
  2486     <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
       
  2487 </xsl:text>
       
  2488     <xsl:text>    };
       
  2489 </xsl:text>
       
  2490     <xsl:text>};
       
  2491 </xsl:text>
       
  2492     <xsl:text>
       
  2493 </xsl:text>
       
  2494     <xsl:text>const typedarray_types = {
       
  2495 </xsl:text>
       
  2496     <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  2497 </xsl:text>
       
  2498     <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  2499 </xsl:text>
       
  2500     <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  2501 </xsl:text>
       
  2502     <xsl:text>    STRING: (str) =&gt; {
       
  2503 </xsl:text>
       
  2504     <xsl:text>        // beremiz default string max size is 128
       
  2505 </xsl:text>
       
  2506     <xsl:text>        str = str.slice(0,128);
       
  2507 </xsl:text>
       
  2508     <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  2509 </xsl:text>
       
  2510     <xsl:text>        binary[0] = str.length;
       
  2511 </xsl:text>
       
  2512     <xsl:text>        for(var i = 0; i &lt; str.length; i++){
       
  2513 </xsl:text>
       
  2514     <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  2515 </xsl:text>
       
  2516     <xsl:text>        }
       
  2517 </xsl:text>
       
  2518     <xsl:text>        return binary;
       
  2519 </xsl:text>
       
  2520     <xsl:text>    }
       
  2521 </xsl:text>
       
  2522     <xsl:text>    /* TODO */
       
  2523 </xsl:text>
       
  2524     <xsl:text>};
       
  2525 </xsl:text>
       
  2526     <xsl:text>
       
  2527 </xsl:text>
       
  2528     <xsl:text>function send_reset() {
       
  2529 </xsl:text>
       
  2530     <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  2531 </xsl:text>
       
  2532     <xsl:text>};
       
  2533 </xsl:text>
       
  2534     <xsl:text>
       
  2535 </xsl:text>
       
  2536     <xsl:text>// subscription state, as it should be in hmi server
       
  2537 </xsl:text>
       
  2538     <xsl:text>// hmitree indexed array of integers
       
  2539 </xsl:text>
       
  2540     <xsl:text>var subscriptions =  hmitree_types.map(_ignored =&gt; 0);
       
  2541 </xsl:text>
       
  2542     <xsl:text>
       
  2543 </xsl:text>
       
  2544     <xsl:text>// subscription state as needed by widget now
       
  2545 </xsl:text>
       
  2546     <xsl:text>// hmitree indexed array of Sets of widgets objects
       
  2547 </xsl:text>
       
  2548     <xsl:text>var subscribers = hmitree_types.map(_ignored =&gt; new Set());
       
  2549 </xsl:text>
       
  2550     <xsl:text>
       
  2551 </xsl:text>
       
  2552     <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  2553 </xsl:text>
       
  2554     <xsl:text>// Since dispatch directly calls change_hmi_value,
       
  2555 </xsl:text>
       
  2556     <xsl:text>// PLC will periodically send variable at given frequency
       
  2557 </xsl:text>
       
  2558     <xsl:text>subscribers[heartbeat_index].add({
       
  2559 </xsl:text>
       
  2560     <xsl:text>    /* type: "Watchdog", */
       
  2561 </xsl:text>
       
  2562     <xsl:text>    frequency: 1,
       
  2563 </xsl:text>
       
  2564     <xsl:text>    indexes: [heartbeat_index],
       
  2565 </xsl:text>
       
  2566     <xsl:text>    dispatch: function(value) {
       
  2567 </xsl:text>
       
  2568     <xsl:text>        change_hmi_value(heartbeat_index, "+1");
       
  2569 </xsl:text>
       
  2570     <xsl:text>    }
       
  2571 </xsl:text>
       
  2572     <xsl:text>});
       
  2573 </xsl:text>
       
  2574     <xsl:text>
       
  2575 </xsl:text>
       
  2576     <xsl:text>function update_subscriptions() {
       
  2577 </xsl:text>
       
  2578     <xsl:text>    let delta = [];
       
  2579 </xsl:text>
       
  2580     <xsl:text>    for(let index = 0; index &lt; subscribers.length; index++){
       
  2581 </xsl:text>
       
  2582     <xsl:text>        let widgets = subscribers[index];
       
  2583 </xsl:text>
       
  2584     <xsl:text>
       
  2585 </xsl:text>
       
  2586     <xsl:text>        // periods are in ms
       
  2587 </xsl:text>
       
  2588     <xsl:text>        let previous_period = subscriptions[index];
       
  2589 </xsl:text>
       
  2590     <xsl:text>
       
  2591 </xsl:text>
       
  2592     <xsl:text>        // subscribing with a zero period is unsubscribing
       
  2593 </xsl:text>
       
  2594     <xsl:text>        let new_period = 0;
       
  2595 </xsl:text>
       
  2596     <xsl:text>        if(widgets.size &gt; 0) {
       
  2597 </xsl:text>
       
  2598     <xsl:text>            let maxfreq = 0;
       
  2599 </xsl:text>
       
  2600     <xsl:text>            for(let widget of widgets)
       
  2601 </xsl:text>
       
  2602     <xsl:text>                if(maxfreq &lt; widget.frequency)
       
  2603 </xsl:text>
       
  2604     <xsl:text>                    maxfreq = widget.frequency;
       
  2605 </xsl:text>
       
  2606     <xsl:text>
       
  2607 </xsl:text>
       
  2608     <xsl:text>            if(maxfreq != 0)
       
  2609 </xsl:text>
       
  2610     <xsl:text>                new_period = 1000/maxfreq;
       
  2611 </xsl:text>
       
  2612     <xsl:text>        }
       
  2613 </xsl:text>
       
  2614     <xsl:text>
       
  2615 </xsl:text>
       
  2616     <xsl:text>        if(previous_period != new_period) {
       
  2617 </xsl:text>
       
  2618     <xsl:text>            subscriptions[index] = new_period;
       
  2619 </xsl:text>
       
  2620     <xsl:text>            delta.push(
       
  2621 </xsl:text>
       
  2622     <xsl:text>                new Uint8Array([2]), /* subscribe = 2 */
       
  2623 </xsl:text>
       
  2624     <xsl:text>                new Uint32Array([index]),
       
  2625 </xsl:text>
       
  2626     <xsl:text>                new Uint16Array([new_period]));
       
  2627 </xsl:text>
       
  2628     <xsl:text>        }
       
  2629 </xsl:text>
       
  2630     <xsl:text>    }
       
  2631 </xsl:text>
       
  2632     <xsl:text>    send_blob(delta);
       
  2633 </xsl:text>
       
  2634     <xsl:text>};
       
  2635 </xsl:text>
       
  2636     <xsl:text>
       
  2637 </xsl:text>
       
  2638     <xsl:text>function send_hmi_value(index, value) {
       
  2639 </xsl:text>
       
  2640     <xsl:text>    let iectype = hmitree_types[index];
       
  2641 </xsl:text>
       
  2642     <xsl:text>    let tobinary = typedarray_types[iectype];
       
  2643 </xsl:text>
       
  2644     <xsl:text>    send_blob([
       
  2645 </xsl:text>
       
  2646     <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
       
  2647 </xsl:text>
       
  2648     <xsl:text>        new Uint32Array([index]),
       
  2649 </xsl:text>
       
  2650     <xsl:text>        tobinary(value)]);
       
  2651 </xsl:text>
       
  2652     <xsl:text>
       
  2653 </xsl:text>
       
  2654     <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
  2655 </xsl:text>
       
  2656     <xsl:text>    // cache[index] = value;
       
  2657 </xsl:text>
       
  2658     <xsl:text>};
       
  2659 </xsl:text>
       
  2660     <xsl:text>
       
  2661 </xsl:text>
       
  2662     <xsl:text>function apply_hmi_value(index, new_val) {
       
  2663 </xsl:text>
       
  2664     <xsl:text>    let old_val = cache[index]
       
  2665 </xsl:text>
       
  2666     <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  2667 </xsl:text>
       
  2668     <xsl:text>        send_hmi_value(index, new_val);
       
  2669 </xsl:text>
       
  2670     <xsl:text>    return new_val;
       
  2671 </xsl:text>
       
  2672     <xsl:text>}
       
  2673 </xsl:text>
       
  2674     <xsl:text>
       
  2675 </xsl:text>
       
  2676     <xsl:text>function change_hmi_value(index, opstr) {
       
  2677 </xsl:text>
       
  2678     <xsl:text>    let op = opstr[0];
       
  2679 </xsl:text>
       
  2680     <xsl:text>    let given_val = opstr.slice(1);
       
  2681 </xsl:text>
       
  2682     <xsl:text>    let old_val = cache[index]
       
  2683 </xsl:text>
       
  2684     <xsl:text>    let new_val;
       
  2685 </xsl:text>
       
  2686     <xsl:text>    switch(op){
       
  2687 </xsl:text>
       
  2688     <xsl:text>      case "=":
       
  2689 </xsl:text>
       
  2690     <xsl:text>        eval("new_val"+opstr);
       
  2691 </xsl:text>
       
  2692     <xsl:text>        break;
       
  2693 </xsl:text>
       
  2694     <xsl:text>      case "+":
       
  2695 </xsl:text>
       
  2696     <xsl:text>      case "-":
       
  2697 </xsl:text>
       
  2698     <xsl:text>      case "*":
       
  2699 </xsl:text>
       
  2700     <xsl:text>      case "/":
       
  2701 </xsl:text>
       
  2702     <xsl:text>        if(old_val != undefined)
       
  2703 </xsl:text>
       
  2704     <xsl:text>            new_val = eval("old_val"+opstr);
       
  2705 </xsl:text>
       
  2706     <xsl:text>        break;
       
  2707 </xsl:text>
       
  2708     <xsl:text>    }
       
  2709 </xsl:text>
       
  2710     <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  2711 </xsl:text>
       
  2712     <xsl:text>        send_hmi_value(index, new_val);
       
  2713 </xsl:text>
       
  2714     <xsl:text>    return new_val;
       
  2715 </xsl:text>
       
  2716     <xsl:text>}
       
  2717 </xsl:text>
       
  2718     <xsl:text>
       
  2719 </xsl:text>
       
  2720     <xsl:text>var current_visible_page;
       
  2721 </xsl:text>
       
  2722     <xsl:text>var current_subscribed_page;
       
  2723 </xsl:text>
       
  2724     <xsl:text>var current_page_index;
       
  2725 </xsl:text>
       
  2726     <xsl:text>
       
  2727 </xsl:text>
       
  2728     <xsl:text>function prepare_svg() {
       
  2729 </xsl:text>
       
  2730     <xsl:text>    for(let eltid in detachable_elements){
       
  2731 </xsl:text>
       
  2732     <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
  2733 </xsl:text>
       
  2734     <xsl:text>        parent.removeChild(element);
       
  2735 </xsl:text>
       
  2736     <xsl:text>    }
       
  2737 </xsl:text>
       
  2738     <xsl:text>};
       
  2739 </xsl:text>
       
  2740     <xsl:text>
       
  2741 </xsl:text>
       
  2742     <xsl:text>function switch_page(page_name, page_index) {
       
  2743 </xsl:text>
       
  2744     <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  2745 </xsl:text>
       
  2746     <xsl:text>        /* page switch already going */
       
  2747 </xsl:text>
       
  2748     <xsl:text>        /* TODO LOG ERROR */
       
  2749 </xsl:text>
       
  2750     <xsl:text>        return false;
       
  2751 </xsl:text>
       
  2752     <xsl:text>    }
       
  2753 </xsl:text>
       
  2754     <xsl:text>
       
  2755 </xsl:text>
       
  2756     <xsl:text>    if(page_name == undefined)
       
  2757 </xsl:text>
       
  2758     <xsl:text>        page_name = current_subscribed_page;
       
  2759 </xsl:text>
       
  2760     <xsl:text>
       
  2761 </xsl:text>
       
  2762     <xsl:text>
       
  2763 </xsl:text>
       
  2764     <xsl:text>    let old_desc = page_desc[current_subscribed_page];
       
  2765 </xsl:text>
       
  2766     <xsl:text>    let new_desc = page_desc[page_name];
       
  2767 </xsl:text>
       
  2768     <xsl:text>
       
  2769 </xsl:text>
       
  2770     <xsl:text>    if(new_desc == undefined){
       
  2771 </xsl:text>
       
  2772     <xsl:text>        /* TODO LOG ERROR */
       
  2773 </xsl:text>
       
  2774     <xsl:text>        return false;
       
  2775 </xsl:text>
       
  2776     <xsl:text>    }
       
  2777 </xsl:text>
       
  2778     <xsl:text>
       
  2779 </xsl:text>
       
  2780     <xsl:text>    if(page_index == undefined){
       
  2781 </xsl:text>
       
  2782     <xsl:text>        page_index = new_desc.page_index;
       
  2783 </xsl:text>
       
  2784     <xsl:text>    }
       
  2785 </xsl:text>
       
  2786     <xsl:text>
       
  2787 </xsl:text>
       
  2788     <xsl:text>    if(old_desc){
       
  2789 </xsl:text>
       
  2790     <xsl:text>        old_desc.absolute_widgets.map(w=&gt;w.unsub());
       
  2791 </xsl:text>
       
  2792     <xsl:text>        old_desc.relative_widgets.map(w=&gt;w.unsub());
       
  2793 </xsl:text>
       
  2794     <xsl:text>    }
       
  2795 </xsl:text>
       
  2796     <xsl:text>    new_desc.absolute_widgets.map(w=&gt;w.sub());
       
  2797 </xsl:text>
       
  2798     <xsl:text>    var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
       
  2799 </xsl:text>
       
  2800     <xsl:text>    new_desc.relative_widgets.map(w=&gt;w.sub(new_offset));
       
  2801 </xsl:text>
       
  2802     <xsl:text>
       
  2803 </xsl:text>
       
  2804     <xsl:text>    update_subscriptions();
       
  2805 </xsl:text>
       
  2806     <xsl:text>
       
  2807 </xsl:text>
       
  2808     <xsl:text>    current_subscribed_page = page_name;
       
  2809 </xsl:text>
       
  2810     <xsl:text>    current_page_index = page_index;
       
  2811 </xsl:text>
       
  2812     <xsl:text>
       
  2813 </xsl:text>
       
  2814     <xsl:text>    jumps_need_update = true;
       
  2815 </xsl:text>
       
  2816     <xsl:text>
       
  2817 </xsl:text>
       
  2818     <xsl:text>    requestHMIAnimation();
       
  2819 </xsl:text>
       
  2820     <xsl:text>
       
  2821 </xsl:text>
       
  2822     <xsl:text>    jump_history.push([page_name, page_index]);
       
  2823 </xsl:text>
       
  2824     <xsl:text>    if(jump_history.length &gt; 42)
       
  2825 </xsl:text>
       
  2826     <xsl:text>        jump_history.shift();
       
  2827 </xsl:text>
       
  2828     <xsl:text>
       
  2829 </xsl:text>
       
  2830     <xsl:text>    return true;
       
  2831 </xsl:text>
       
  2832     <xsl:text>};
       
  2833 </xsl:text>
       
  2834     <xsl:text>
       
  2835 </xsl:text>
       
  2836     <xsl:text>function* chain(a,b){
       
  2837 </xsl:text>
       
  2838     <xsl:text>    yield* a;
       
  2839 </xsl:text>
       
  2840     <xsl:text>    yield* b;
       
  2841 </xsl:text>
       
  2842     <xsl:text>};
       
  2843 </xsl:text>
       
  2844     <xsl:text>
       
  2845 </xsl:text>
       
  2846     <xsl:text>function unsubscribe(){
       
  2847 </xsl:text>
       
  2848     <xsl:text>    /* remove subsribers */
       
  2849 </xsl:text>
       
  2850     <xsl:text>    for(let index of this.indexes){
       
  2851 </xsl:text>
       
  2852     <xsl:text>        let idx = index + this.offset;
       
  2853 </xsl:text>
       
  2854     <xsl:text>        subscribers[idx].delete(this);
       
  2855 </xsl:text>
       
  2856     <xsl:text>    }
       
  2857 </xsl:text>
       
  2858     <xsl:text>    this.offset = 0;
       
  2859 </xsl:text>
       
  2860     <xsl:text>}
       
  2861 </xsl:text>
       
  2862     <xsl:text>
       
  2863 </xsl:text>
       
  2864     <xsl:text>function subscribe(new_offset=0){
       
  2865 </xsl:text>
       
  2866     <xsl:text>    /* set the offset because relative */
       
  2867 </xsl:text>
       
  2868     <xsl:text>    this.offset = new_offset;
       
  2869 </xsl:text>
       
  2870     <xsl:text>    /* add this's subsribers */
       
  2871 </xsl:text>
       
  2872     <xsl:text>    for(let index of this.indexes){
       
  2873 </xsl:text>
       
  2874     <xsl:text>        subscribers[index + new_offset].add(this);
       
  2875 </xsl:text>
       
  2876     <xsl:text>    }
       
  2877 </xsl:text>
       
  2878     <xsl:text>    need_cache_apply.push(this); 
       
  2879 </xsl:text>
       
  2880     <xsl:text>}
       
  2881 </xsl:text>
       
  2882     <xsl:text>
       
  2883 </xsl:text>
       
  2884     <xsl:text>function foreach_unsubscribe(){
       
  2885 </xsl:text>
       
  2886     <xsl:text>    for(let item of this.items){
       
  2887 </xsl:text>
       
  2888     <xsl:text>        for(let widget of item) {
       
  2889 </xsl:text>
       
  2890     <xsl:text>            unsubscribe.call(widget);
       
  2891 </xsl:text>
       
  2892     <xsl:text>        }
       
  2893 </xsl:text>
       
  2894     <xsl:text>    }
       
  2895 </xsl:text>
       
  2896     <xsl:text>    this.offset = 0;
       
  2897 </xsl:text>
       
  2898     <xsl:text>}
       
  2899 </xsl:text>
       
  2900     <xsl:text>
       
  2901 </xsl:text>
       
  2902     <xsl:text>function foreach_widgets_do(new_offset, todo){
       
  2903 </xsl:text>
       
  2904     <xsl:text>    this.offset = new_offset;
       
  2905 </xsl:text>
       
  2906     <xsl:text>    for(let i = 0; i &lt; this.items.length; i++) {
       
  2907 </xsl:text>
       
  2908     <xsl:text>        let item = this.items[i];
       
  2909 </xsl:text>
       
  2910     <xsl:text>        let orig_item_index = this.index_pool[i];
       
  2911 </xsl:text>
       
  2912     <xsl:text>        let item_index = this.index_pool[i+this.item_offset];
       
  2913 </xsl:text>
       
  2914     <xsl:text>        let item_index_offset = item_index - orig_item_index;
       
  2915 </xsl:text>
       
  2916     <xsl:text>        for(let widget of item) {
       
  2917 </xsl:text>
       
  2918     <xsl:text>            todo.call(widget, new_offset + item_index_offset);
       
  2919 </xsl:text>
       
  2920     <xsl:text>        }
       
  2921 </xsl:text>
       
  2922     <xsl:text>    }
       
  2923 </xsl:text>
       
  2924     <xsl:text>}
       
  2925 </xsl:text>
       
  2926     <xsl:text>
       
  2927 </xsl:text>
       
  2928     <xsl:text>function foreach_subscribe(new_offset=0){
       
  2929 </xsl:text>
       
  2930     <xsl:text>    foreach_widgets_do.call(this, new_offset, subscribe);
       
  2931 </xsl:text>
       
  2932     <xsl:text>}
       
  2933 </xsl:text>
       
  2934     <xsl:text>
       
  2935 </xsl:text>
       
  2936     <xsl:text>function widget_apply_cache() {
       
  2937 </xsl:text>
       
  2938     <xsl:text>    for(let index of this.indexes){
       
  2939 </xsl:text>
       
  2940     <xsl:text>        /* dispatch current cache in newly opened page widgets */
       
  2941 </xsl:text>
       
  2942     <xsl:text>        let realindex = index+this.offset;
       
  2943 </xsl:text>
       
  2944     <xsl:text>        let cached_val = cache[realindex];
       
  2945 </xsl:text>
       
  2946     <xsl:text>        if(cached_val != undefined)
       
  2947 </xsl:text>
       
  2948     <xsl:text>            dispatch_value_to_widget(this, realindex, cached_val, cached_val);
       
  2949 </xsl:text>
       
  2950     <xsl:text>    }
       
  2951 </xsl:text>
       
  2952     <xsl:text>}
       
  2953 </xsl:text>
       
  2954     <xsl:text>
       
  2955 </xsl:text>
       
  2956     <xsl:text>function foreach_apply_cache() {
       
  2957 </xsl:text>
       
  2958     <xsl:text>    foreach_widgets_do.call(this, this.offset, widget_apply_cache);
       
  2959 </xsl:text>
       
  2960     <xsl:text>}
       
  2961 </xsl:text>
       
  2962     <xsl:text>
       
  2963 </xsl:text>
       
  2964     <xsl:text>function foreach_onclick(opstr, evt) {
       
  2965 </xsl:text>
       
  2966     <xsl:text>    new_item_offset = eval(String(this.item_offset)+opstr)
       
  2967 </xsl:text>
       
  2968     <xsl:text>    if(new_item_offset + this.items.length &gt; this.index_pool.length) {
       
  2969 </xsl:text>
       
  2970     <xsl:text>        if(this.item_offset + this.items.length == this.index_pool.length)
       
  2971 </xsl:text>
       
  2972     <xsl:text>            new_item_offset = 0;
       
  2973 </xsl:text>
       
  2974     <xsl:text>        else
       
  2975 </xsl:text>
       
  2976     <xsl:text>            new_item_offset = this.index_pool.length - this.items.length;
       
  2977 </xsl:text>
       
  2978     <xsl:text>    } else if(new_item_offset &lt; 0) {
       
  2979 </xsl:text>
       
  2980     <xsl:text>        if(this.item_offset == 0)
       
  2981 </xsl:text>
       
  2982     <xsl:text>            new_item_offset = this.index_pool.length - this.items.length;
       
  2983 </xsl:text>
       
  2984     <xsl:text>        else
       
  2985 </xsl:text>
       
  2986     <xsl:text>            new_item_offset = 0;
       
  2987 </xsl:text>
       
  2988     <xsl:text>    }
       
  2989 </xsl:text>
       
  2990     <xsl:text>    this.item_offset = new_item_offset;
       
  2991 </xsl:text>
       
  2992     <xsl:text>    off = this.offset;
       
  2993 </xsl:text>
       
  2994     <xsl:text>    foreach_unsubscribe.call(this);
       
  2995 </xsl:text>
       
  2996     <xsl:text>    foreach_subscribe.call(this,off);
       
  2997 </xsl:text>
       
  2998     <xsl:text>    update_subscriptions();
       
  2999 </xsl:text>
       
  3000     <xsl:text>    need_cache_apply.push(this);
       
  3001 </xsl:text>
       
  3002     <xsl:text>    jumps_need_update = true;
       
  3003 </xsl:text>
       
  3004     <xsl:text>    requestHMIAnimation();
       
  3005 </xsl:text>
       
  3006     <xsl:text>}
       
  3007 </xsl:text>
       
  3008     <xsl:text>
       
  3009 </xsl:text>
       
  3010     <xsl:text>
       
  3011 </xsl:text>
       
  3012     <xsl:text>function switch_visible_page(page_name) {
       
  3013 </xsl:text>
       
  3014     <xsl:text>
       
  3015 </xsl:text>
       
  3016     <xsl:text>    let old_desc = page_desc[current_visible_page];
       
  3017 </xsl:text>
       
  3018     <xsl:text>    let new_desc = page_desc[page_name];
       
  3019 </xsl:text>
       
  3020     <xsl:text>
       
  3021 </xsl:text>
       
  3022     <xsl:text>    if(old_desc){
       
  3023 </xsl:text>
       
  3024     <xsl:text>        for(let eltid in old_desc.required_detachables){
       
  3025 </xsl:text>
       
  3026     <xsl:text>            if(!(eltid in new_desc.required_detachables)){
       
  3027 </xsl:text>
       
  3028     <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
       
  3029 </xsl:text>
       
  3030     <xsl:text>                parent.removeChild(element);
       
  3031 </xsl:text>
       
  3032     <xsl:text>            }
       
  3033 </xsl:text>
       
  3034     <xsl:text>        }
       
  3035 </xsl:text>
       
  3036     <xsl:text>        for(let eltid in new_desc.required_detachables){
       
  3037 </xsl:text>
       
  3038     <xsl:text>            if(!(eltid in old_desc.required_detachables)){
       
  3039 </xsl:text>
       
  3040     <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
       
  3041 </xsl:text>
       
  3042     <xsl:text>                parent.appendChild(element);
       
  3043 </xsl:text>
       
  3044     <xsl:text>            }
       
  3045 </xsl:text>
       
  3046     <xsl:text>        }
       
  3047 </xsl:text>
       
  3048     <xsl:text>    }else{
       
  3049 </xsl:text>
       
  3050     <xsl:text>        for(let eltid in new_desc.required_detachables){
       
  3051 </xsl:text>
       
  3052     <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
       
  3053 </xsl:text>
       
  3054     <xsl:text>            parent.appendChild(element);
       
  3055 </xsl:text>
       
  3056     <xsl:text>        }
       
  3057 </xsl:text>
       
  3058     <xsl:text>    }
       
  3059 </xsl:text>
       
  3060     <xsl:text>
       
  3061 </xsl:text>
       
  3062     <xsl:text>    svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
       
  3063 </xsl:text>
       
  3064     <xsl:text>    current_visible_page = page_name;
       
  3065 </xsl:text>
       
  3066     <xsl:text>};
       
  3067 </xsl:text>
       
  3068     <xsl:text>
       
  3069 </xsl:text>
       
  3070     <xsl:text>function update_jumps() {
       
  3071 </xsl:text>
       
  3072     <xsl:text>    page_desc[current_visible_page].jumps.map(w=&gt;w.notify_page_change(current_visible_page,current_page_index));
       
  3073 </xsl:text>
       
  3074     <xsl:text>    jumps_need_update = false;
       
  3075 </xsl:text>
       
  3076     <xsl:text>};
       
  3077 </xsl:text>
       
  3078     <xsl:text>
       
  3079 </xsl:text>
       
  3080     <xsl:text>
       
  3081 </xsl:text>
       
  3082     <xsl:text>// Once connection established
       
  3083 </xsl:text>
       
  3084     <xsl:text>ws.onopen = function (evt) {
       
  3085 </xsl:text>
       
  3086     <xsl:text>    init_widgets();
       
  3087 </xsl:text>
       
  3088     <xsl:text>    send_reset();
       
  3089 </xsl:text>
       
  3090     <xsl:text>    // show main page
       
  3091 </xsl:text>
       
  3092     <xsl:text>    prepare_svg();
       
  3093 </xsl:text>
       
  3094     <xsl:text>    switch_page(default_page);
       
  3095 </xsl:text>
       
  3096     <xsl:text>};
       
  3097 </xsl:text>
       
  3098     <xsl:text>
       
  3099 </xsl:text>
       
  3100     <xsl:text>ws.onclose = function (evt) {
       
  3101 </xsl:text>
       
  3102     <xsl:text>    // TODO : add visible notification while waiting for reload
       
  3103 </xsl:text>
       
  3104     <xsl:text>    console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s.");
       
  3105 </xsl:text>
       
  3106     <xsl:text>    // TODO : re-enable auto reload when not in debug
       
  3107 </xsl:text>
       
  3108     <xsl:text>    //window.setTimeout(() =&gt; location.reload(true), 10000);
       
  3109 </xsl:text>
       
  3110     <xsl:text>    alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+".");
       
  3111 </xsl:text>
       
  3112     <xsl:text>
       
  3113 </xsl:text>
       
  3114     <xsl:text>};
       
  3115 </xsl:text>
       
  3116     <xsl:text>
       
  3117 </xsl:text>
       
  3118     <xsl:text>var xmlns = "http://www.w3.org/2000/svg";
       
  3119 </xsl:text>
       
  3120     <xsl:text>var edit_callback;
       
  3121 </xsl:text>
       
  3122     <xsl:text>function edit_value(path, valuetype, callback, initial) {
       
  3123 </xsl:text>
       
  3124     <xsl:text>
       
  3125 </xsl:text>
       
  3126     <xsl:text>    let [keypadid, xcoord, ycoord] = keypads[valuetype];
       
  3127 </xsl:text>
       
  3128     <xsl:text>    console.log('XXX TODO : Edit value', path, valuetype, callback, initial, keypadid);
       
  3129 </xsl:text>
       
  3130     <xsl:text>    edit_callback = callback;
       
  3131 </xsl:text>
       
  3132     <xsl:text>    let widget = hmi_widgets[keypadid];
       
  3133 </xsl:text>
       
  3134     <xsl:text>    widget.start_edit(path, valuetype, callback, initial);
       
  3135 </xsl:text>
       
  3136     <xsl:text>};
       
  3137 </xsl:text>
       
  3138     <xsl:text>
       
  3139 </xsl:text>
       
  3140     <xsl:text>var current_modal; /* TODO stack ?*/
       
  3141 </xsl:text>
       
  3142     <xsl:text>
       
  3143 </xsl:text>
       
  3144     <xsl:text>function show_modal() {
       
  3145 </xsl:text>
       
  3146     <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
  3147 </xsl:text>
       
  3148     <xsl:text>
       
  3149 </xsl:text>
       
  3150     <xsl:text>    tmpgrp = document.createElementNS(xmlns,"g");
       
  3151 </xsl:text>
       
  3152     <xsl:text>    tmpgrpattr = document.createAttribute("transform");
       
  3153 </xsl:text>
       
  3154     <xsl:text>
       
  3155 </xsl:text>
       
  3156     <xsl:text>    let [xcoord,ycoord] = this.coordinates;
       
  3157 </xsl:text>
       
  3158     <xsl:text>    let [xdest,ydest] = page_desc[current_visible_page].bbox;
       
  3159 </xsl:text>
       
  3160     <xsl:text>    tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")";
       
  3161 </xsl:text>
       
  3162     <xsl:text>    tmpgrp.setAttributeNode(tmpgrpattr);
       
  3163 </xsl:text>
       
  3164     <xsl:text>
       
  3165 </xsl:text>
       
  3166     <xsl:text>    tmpgrp.appendChild(element);
       
  3167 </xsl:text>
       
  3168     <xsl:text>    parent.appendChild(tmpgrp);
       
  3169 </xsl:text>
       
  3170     <xsl:text>
       
  3171 </xsl:text>
       
  3172     <xsl:text>    current_modal = [this.element.id, tmpgrp];
       
  3173 </xsl:text>
       
  3174     <xsl:text>};
       
  3175 </xsl:text>
       
  3176     <xsl:text>
       
  3177 </xsl:text>
       
  3178     <xsl:text>function end_modal() {
       
  3179 </xsl:text>
       
  3180     <xsl:text>    let [eltid, tmpgrp] = current_modal;
       
  3181 </xsl:text>
       
  3182     <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
  3183 </xsl:text>
       
  3184     <xsl:text>
       
  3185 </xsl:text>
       
  3186     <xsl:text>    parent.removeChild(tmpgrp);
       
  3187 </xsl:text>
       
  3188     <xsl:text>
       
  3189 </xsl:text>
       
  3190     <xsl:text>    current_modal = undefined;
       
  3191 </xsl:text>
       
  3192     <xsl:text>};
       
  3193 </xsl:text>
       
  3194     <xsl:text>
       
  3195 </xsl:text>
       
  3196     <xsl:text>function widget_active_activable(eltsub) {
       
  3197 </xsl:text>
       
  3198     <xsl:text>    if(eltsub.inactive_style === undefined)
       
  3199 </xsl:text>
       
  3200     <xsl:text>        eltsub.inactive_style = eltsub.inactive.getAttribute("style");
       
  3201 </xsl:text>
       
  3202     <xsl:text>    eltsub.inactive.setAttribute("style", "display:none");
       
  3203 </xsl:text>
       
  3204     <xsl:text>    if(eltsub.active_style !== undefined)
       
  3205 </xsl:text>
       
  3206     <xsl:text>            eltsub.active.setAttribute("style", eltsub.active_style);
       
  3207 </xsl:text>
       
  3208     <xsl:text>    console.log("active", eltsub);
       
  3209 </xsl:text>
       
  3210     <xsl:text>};
       
  3211 </xsl:text>
       
  3212     <xsl:text>function widget_inactive_activable(eltsub) {
       
  3213 </xsl:text>
       
  3214     <xsl:text>    if(eltsub.active_style === undefined)
       
  3215 </xsl:text>
       
  3216     <xsl:text>        eltsub.active_style = eltsub.active.getAttribute("style");
       
  3217 </xsl:text>
       
  3218     <xsl:text>    eltsub.active.setAttribute("style", "display:none");
       
  3219 </xsl:text>
       
  3220     <xsl:text>    if(eltsub.inactive_style !== undefined)
       
  3221 </xsl:text>
       
  3222     <xsl:text>            eltsub.inactive.setAttribute("style", eltsub.inactive_style);
       
  3223 </xsl:text>
       
  3224     <xsl:text>    console.log("inactive", eltsub);
       
  3225 </xsl:text>
       
  3226     <xsl:text>};
       
  3227 </xsl:text>
       
  3228     <xsl:apply-templates select="document('')/*/epilogue:*"/>
       
  3229   </xsl:template>
       
  3230   <xsl:template match="/">
  2199   <xsl:template match="/">
  3231     <xsl:comment>
  2200     <xsl:comment>
  3232       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  2201       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  3233     </xsl:comment>
  2202     </xsl:comment>
  3234     <xsl:for-each select="document('')/*/debug:*">
  2203     <xsl:for-each select="document('')/*/debug:*">
  3242     <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
  2211     <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
  3243       <head/>
  2212       <head/>
  3244       <body style="margin:0;overflow:hidden;">
  2213       <body style="margin:0;overflow:hidden;">
  3245         <xsl:copy-of select="$result_svg"/>
  2214         <xsl:copy-of select="$result_svg"/>
  3246         <script>
  2215         <script>
  3247           <xsl:call-template name="scripts"/>
  2216           <xsl:apply-templates select="document('')/*/preamble:*"/>
       
  2217           <xsl:apply-templates select="document('')/*/epilogue:*"/>
       
  2218           <xsl:text>// svghmi.js
       
  2219 </xsl:text>
       
  2220           <xsl:text>
       
  2221 </xsl:text>
       
  2222           <xsl:text>var cache = hmitree_types.map(_ignored =&gt; undefined);
       
  2223 </xsl:text>
       
  2224           <xsl:text>var updates = {};
       
  2225 </xsl:text>
       
  2226           <xsl:text>var need_cache_apply = []; 
       
  2227 </xsl:text>
       
  2228           <xsl:text>var jumps_need_update = false;
       
  2229 </xsl:text>
       
  2230           <xsl:text>var jump_history = [[default_page, undefined]];
       
  2231 </xsl:text>
       
  2232           <xsl:text>
       
  2233 </xsl:text>
       
  2234           <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
       
  2235 </xsl:text>
       
  2236           <xsl:text>    try {
       
  2237 </xsl:text>
       
  2238           <xsl:text>        let idx = widget.offset ? index - widget.offset : index;
       
  2239 </xsl:text>
       
  2240           <xsl:text>        let idxidx = widget.indexes.indexOf(idx);
       
  2241 </xsl:text>
       
  2242           <xsl:text>        let d = widget.dispatch;
       
  2243 </xsl:text>
       
  2244           <xsl:text>        if(typeof(d) == "function" &amp;&amp; idxidx == 0){
       
  2245 </xsl:text>
       
  2246           <xsl:text>            d.call(widget, value, oldval);
       
  2247 </xsl:text>
       
  2248           <xsl:text>        }
       
  2249 </xsl:text>
       
  2250           <xsl:text>        else if(typeof(d) == "object" &amp;&amp; d.length &gt;= idxidx){
       
  2251 </xsl:text>
       
  2252           <xsl:text>            d[idxidx].call(widget, value, oldval);
       
  2253 </xsl:text>
       
  2254           <xsl:text>        }
       
  2255 </xsl:text>
       
  2256           <xsl:text>        /* else dispatch_0, ..., dispatch_n ? */
       
  2257 </xsl:text>
       
  2258           <xsl:text>        /*else {
       
  2259 </xsl:text>
       
  2260           <xsl:text>            throw new Error("Dunno how to dispatch to widget at index = " + index);
       
  2261 </xsl:text>
       
  2262           <xsl:text>        }*/
       
  2263 </xsl:text>
       
  2264           <xsl:text>    } catch(err) {
       
  2265 </xsl:text>
       
  2266           <xsl:text>        console.log(err);
       
  2267 </xsl:text>
       
  2268           <xsl:text>    }
       
  2269 </xsl:text>
       
  2270           <xsl:text>}
       
  2271 </xsl:text>
       
  2272           <xsl:text>
       
  2273 </xsl:text>
       
  2274           <xsl:text>function dispatch_value(index, value) {
       
  2275 </xsl:text>
       
  2276           <xsl:text>    let widgets = subscribers[index];
       
  2277 </xsl:text>
       
  2278           <xsl:text>
       
  2279 </xsl:text>
       
  2280           <xsl:text>    let oldval = cache[index];
       
  2281 </xsl:text>
       
  2282           <xsl:text>    cache[index] = value;
       
  2283 </xsl:text>
       
  2284           <xsl:text>
       
  2285 </xsl:text>
       
  2286           <xsl:text>    if(widgets.size &gt; 0) {
       
  2287 </xsl:text>
       
  2288           <xsl:text>        for(let widget of widgets){
       
  2289 </xsl:text>
       
  2290           <xsl:text>            dispatch_value_to_widget(widget, index, value, oldval);
       
  2291 </xsl:text>
       
  2292           <xsl:text>        }
       
  2293 </xsl:text>
       
  2294           <xsl:text>    }
       
  2295 </xsl:text>
       
  2296           <xsl:text>};
       
  2297 </xsl:text>
       
  2298           <xsl:text>
       
  2299 </xsl:text>
       
  2300           <xsl:text>function init_widgets() {
       
  2301 </xsl:text>
       
  2302           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
       
  2303 </xsl:text>
       
  2304           <xsl:text>        let widget = hmi_widgets[id];
       
  2305 </xsl:text>
       
  2306           <xsl:text>        let init = widget.init;
       
  2307 </xsl:text>
       
  2308           <xsl:text>        if(typeof(init) == "function"){
       
  2309 </xsl:text>
       
  2310           <xsl:text>            try {
       
  2311 </xsl:text>
       
  2312           <xsl:text>                init.call(widget);
       
  2313 </xsl:text>
       
  2314           <xsl:text>            } catch(err) {
       
  2315 </xsl:text>
       
  2316           <xsl:text>                console.log(err);
       
  2317 </xsl:text>
       
  2318           <xsl:text>            }
       
  2319 </xsl:text>
       
  2320           <xsl:text>        }
       
  2321 </xsl:text>
       
  2322           <xsl:text>    });
       
  2323 </xsl:text>
       
  2324           <xsl:text>};
       
  2325 </xsl:text>
       
  2326           <xsl:text>
       
  2327 </xsl:text>
       
  2328           <xsl:text>// Open WebSocket to relative "/ws" address
       
  2329 </xsl:text>
       
  2330           <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
       
  2331 </xsl:text>
       
  2332           <xsl:text>ws.binaryType = 'arraybuffer';
       
  2333 </xsl:text>
       
  2334           <xsl:text>
       
  2335 </xsl:text>
       
  2336           <xsl:text>const dvgetters = {
       
  2337 </xsl:text>
       
  2338           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  2339 </xsl:text>
       
  2340           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  2341 </xsl:text>
       
  2342           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  2343 </xsl:text>
       
  2344           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  2345 </xsl:text>
       
  2346           <xsl:text>        size = dv.getInt8(offset);
       
  2347 </xsl:text>
       
  2348           <xsl:text>        return [
       
  2349 </xsl:text>
       
  2350           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  2351 </xsl:text>
       
  2352           <xsl:text>                dv.buffer, /* original buffer */
       
  2353 </xsl:text>
       
  2354           <xsl:text>                offset + 1, /* string starts after size*/
       
  2355 </xsl:text>
       
  2356           <xsl:text>                size /* size of string */
       
  2357 </xsl:text>
       
  2358           <xsl:text>            )), size + 1]; /* total increment */
       
  2359 </xsl:text>
       
  2360           <xsl:text>    }
       
  2361 </xsl:text>
       
  2362           <xsl:text>};
       
  2363 </xsl:text>
       
  2364           <xsl:text>
       
  2365 </xsl:text>
       
  2366           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
       
  2367 </xsl:text>
       
  2368           <xsl:text>function apply_updates() {
       
  2369 </xsl:text>
       
  2370           <xsl:text>    for(let index in updates){
       
  2371 </xsl:text>
       
  2372           <xsl:text>        // serving as a key, index becomes a string
       
  2373 </xsl:text>
       
  2374           <xsl:text>        // -&gt; pass Number(index) instead
       
  2375 </xsl:text>
       
  2376           <xsl:text>        dispatch_value(Number(index), updates[index]);
       
  2377 </xsl:text>
       
  2378           <xsl:text>        delete updates[index];
       
  2379 </xsl:text>
       
  2380           <xsl:text>    }
       
  2381 </xsl:text>
       
  2382           <xsl:text>}
       
  2383 </xsl:text>
       
  2384           <xsl:text>
       
  2385 </xsl:text>
       
  2386           <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
  2387 </xsl:text>
       
  2388           <xsl:text>var requestAnimationFrameID = null;
       
  2389 </xsl:text>
       
  2390           <xsl:text>function animate() {
       
  2391 </xsl:text>
       
  2392           <xsl:text>    // Do the page swith if any one pending
       
  2393 </xsl:text>
       
  2394           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  2395 </xsl:text>
       
  2396           <xsl:text>        switch_visible_page(current_subscribed_page);
       
  2397 </xsl:text>
       
  2398           <xsl:text>    }
       
  2399 </xsl:text>
       
  2400           <xsl:text>
       
  2401 </xsl:text>
       
  2402           <xsl:text>    while(widget = need_cache_apply.pop()){
       
  2403 </xsl:text>
       
  2404           <xsl:text>        widget.apply_cache();
       
  2405 </xsl:text>
       
  2406           <xsl:text>    }
       
  2407 </xsl:text>
       
  2408           <xsl:text>
       
  2409 </xsl:text>
       
  2410           <xsl:text>    if(jumps_need_update) update_jumps();
       
  2411 </xsl:text>
       
  2412           <xsl:text>
       
  2413 </xsl:text>
       
  2414           <xsl:text>    apply_updates();
       
  2415 </xsl:text>
       
  2416           <xsl:text>    requestAnimationFrameID = null;
       
  2417 </xsl:text>
       
  2418           <xsl:text>}
       
  2419 </xsl:text>
       
  2420           <xsl:text>
       
  2421 </xsl:text>
       
  2422           <xsl:text>function requestHMIAnimation() {
       
  2423 </xsl:text>
       
  2424           <xsl:text>    if(requestAnimationFrameID == null){
       
  2425 </xsl:text>
       
  2426           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  2427 </xsl:text>
       
  2428           <xsl:text>    }
       
  2429 </xsl:text>
       
  2430           <xsl:text>}
       
  2431 </xsl:text>
       
  2432           <xsl:text>
       
  2433 </xsl:text>
       
  2434           <xsl:text>// Message reception handler
       
  2435 </xsl:text>
       
  2436           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  2437 </xsl:text>
       
  2438           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  2439 </xsl:text>
       
  2440           <xsl:text>ws.onmessage = function (evt) {
       
  2441 </xsl:text>
       
  2442           <xsl:text>
       
  2443 </xsl:text>
       
  2444           <xsl:text>    let data = evt.data;
       
  2445 </xsl:text>
       
  2446           <xsl:text>    let dv = new DataView(data);
       
  2447 </xsl:text>
       
  2448           <xsl:text>    let i = 0;
       
  2449 </xsl:text>
       
  2450           <xsl:text>    try {
       
  2451 </xsl:text>
       
  2452           <xsl:text>        for(let hash_int of hmi_hash) {
       
  2453 </xsl:text>
       
  2454           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  2455 </xsl:text>
       
  2456           <xsl:text>                throw new Error("Hash doesn't match");
       
  2457 </xsl:text>
       
  2458           <xsl:text>            };
       
  2459 </xsl:text>
       
  2460           <xsl:text>            i++;
       
  2461 </xsl:text>
       
  2462           <xsl:text>        };
       
  2463 </xsl:text>
       
  2464           <xsl:text>
       
  2465 </xsl:text>
       
  2466           <xsl:text>        while(i &lt; data.byteLength){
       
  2467 </xsl:text>
       
  2468           <xsl:text>            let index = dv.getUint32(i, true);
       
  2469 </xsl:text>
       
  2470           <xsl:text>            i += 4;
       
  2471 </xsl:text>
       
  2472           <xsl:text>            let iectype = hmitree_types[index];
       
  2473 </xsl:text>
       
  2474           <xsl:text>            if(iectype != undefined){
       
  2475 </xsl:text>
       
  2476           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  2477 </xsl:text>
       
  2478           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  2479 </xsl:text>
       
  2480           <xsl:text>                updates[index] = value;
       
  2481 </xsl:text>
       
  2482           <xsl:text>                i += bytesize;
       
  2483 </xsl:text>
       
  2484           <xsl:text>            } else {
       
  2485 </xsl:text>
       
  2486           <xsl:text>                throw new Error("Unknown index "+index);
       
  2487 </xsl:text>
       
  2488           <xsl:text>            }
       
  2489 </xsl:text>
       
  2490           <xsl:text>        };
       
  2491 </xsl:text>
       
  2492           <xsl:text>        // register for rendering on next frame, since there are updates
       
  2493 </xsl:text>
       
  2494           <xsl:text>        requestHMIAnimation();
       
  2495 </xsl:text>
       
  2496           <xsl:text>    } catch(err) {
       
  2497 </xsl:text>
       
  2498           <xsl:text>        // 1003 is for "Unsupported Data"
       
  2499 </xsl:text>
       
  2500           <xsl:text>        // ws.close(1003, err.message);
       
  2501 </xsl:text>
       
  2502           <xsl:text>
       
  2503 </xsl:text>
       
  2504           <xsl:text>        // TODO : remove debug alert ?
       
  2505 </xsl:text>
       
  2506           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  2507 </xsl:text>
       
  2508           <xsl:text>
       
  2509 </xsl:text>
       
  2510           <xsl:text>        // force reload ignoring cache
       
  2511 </xsl:text>
       
  2512           <xsl:text>        location.reload(true);
       
  2513 </xsl:text>
       
  2514           <xsl:text>    }
       
  2515 </xsl:text>
       
  2516           <xsl:text>};
       
  2517 </xsl:text>
       
  2518           <xsl:text>
       
  2519 </xsl:text>
       
  2520           <xsl:text>
       
  2521 </xsl:text>
       
  2522           <xsl:text>function send_blob(data) {
       
  2523 </xsl:text>
       
  2524           <xsl:text>    if(data.length &gt; 0) {
       
  2525 </xsl:text>
       
  2526           <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
       
  2527 </xsl:text>
       
  2528           <xsl:text>    };
       
  2529 </xsl:text>
       
  2530           <xsl:text>};
       
  2531 </xsl:text>
       
  2532           <xsl:text>
       
  2533 </xsl:text>
       
  2534           <xsl:text>const typedarray_types = {
       
  2535 </xsl:text>
       
  2536           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  2537 </xsl:text>
       
  2538           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  2539 </xsl:text>
       
  2540           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  2541 </xsl:text>
       
  2542           <xsl:text>    STRING: (str) =&gt; {
       
  2543 </xsl:text>
       
  2544           <xsl:text>        // beremiz default string max size is 128
       
  2545 </xsl:text>
       
  2546           <xsl:text>        str = str.slice(0,128);
       
  2547 </xsl:text>
       
  2548           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  2549 </xsl:text>
       
  2550           <xsl:text>        binary[0] = str.length;
       
  2551 </xsl:text>
       
  2552           <xsl:text>        for(var i = 0; i &lt; str.length; i++){
       
  2553 </xsl:text>
       
  2554           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  2555 </xsl:text>
       
  2556           <xsl:text>        }
       
  2557 </xsl:text>
       
  2558           <xsl:text>        return binary;
       
  2559 </xsl:text>
       
  2560           <xsl:text>    }
       
  2561 </xsl:text>
       
  2562           <xsl:text>    /* TODO */
       
  2563 </xsl:text>
       
  2564           <xsl:text>};
       
  2565 </xsl:text>
       
  2566           <xsl:text>
       
  2567 </xsl:text>
       
  2568           <xsl:text>function send_reset() {
       
  2569 </xsl:text>
       
  2570           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  2571 </xsl:text>
       
  2572           <xsl:text>};
       
  2573 </xsl:text>
       
  2574           <xsl:text>
       
  2575 </xsl:text>
       
  2576           <xsl:text>// subscription state, as it should be in hmi server
       
  2577 </xsl:text>
       
  2578           <xsl:text>// hmitree indexed array of integers
       
  2579 </xsl:text>
       
  2580           <xsl:text>var subscriptions =  hmitree_types.map(_ignored =&gt; 0);
       
  2581 </xsl:text>
       
  2582           <xsl:text>
       
  2583 </xsl:text>
       
  2584           <xsl:text>// subscription state as needed by widget now
       
  2585 </xsl:text>
       
  2586           <xsl:text>// hmitree indexed array of Sets of widgets objects
       
  2587 </xsl:text>
       
  2588           <xsl:text>var subscribers = hmitree_types.map(_ignored =&gt; new Set());
       
  2589 </xsl:text>
       
  2590           <xsl:text>
       
  2591 </xsl:text>
       
  2592           <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  2593 </xsl:text>
       
  2594           <xsl:text>// Since dispatch directly calls change_hmi_value,
       
  2595 </xsl:text>
       
  2596           <xsl:text>// PLC will periodically send variable at given frequency
       
  2597 </xsl:text>
       
  2598           <xsl:text>subscribers[heartbeat_index].add({
       
  2599 </xsl:text>
       
  2600           <xsl:text>    /* type: "Watchdog", */
       
  2601 </xsl:text>
       
  2602           <xsl:text>    frequency: 1,
       
  2603 </xsl:text>
       
  2604           <xsl:text>    indexes: [heartbeat_index],
       
  2605 </xsl:text>
       
  2606           <xsl:text>    dispatch: function(value) {
       
  2607 </xsl:text>
       
  2608           <xsl:text>        change_hmi_value(heartbeat_index, "+1");
       
  2609 </xsl:text>
       
  2610           <xsl:text>    }
       
  2611 </xsl:text>
       
  2612           <xsl:text>});
       
  2613 </xsl:text>
       
  2614           <xsl:text>
       
  2615 </xsl:text>
       
  2616           <xsl:text>function update_subscriptions() {
       
  2617 </xsl:text>
       
  2618           <xsl:text>    let delta = [];
       
  2619 </xsl:text>
       
  2620           <xsl:text>    for(let index = 0; index &lt; subscribers.length; index++){
       
  2621 </xsl:text>
       
  2622           <xsl:text>        let widgets = subscribers[index];
       
  2623 </xsl:text>
       
  2624           <xsl:text>
       
  2625 </xsl:text>
       
  2626           <xsl:text>        // periods are in ms
       
  2627 </xsl:text>
       
  2628           <xsl:text>        let previous_period = subscriptions[index];
       
  2629 </xsl:text>
       
  2630           <xsl:text>
       
  2631 </xsl:text>
       
  2632           <xsl:text>        // subscribing with a zero period is unsubscribing
       
  2633 </xsl:text>
       
  2634           <xsl:text>        let new_period = 0;
       
  2635 </xsl:text>
       
  2636           <xsl:text>        if(widgets.size &gt; 0) {
       
  2637 </xsl:text>
       
  2638           <xsl:text>            let maxfreq = 0;
       
  2639 </xsl:text>
       
  2640           <xsl:text>            for(let widget of widgets)
       
  2641 </xsl:text>
       
  2642           <xsl:text>                if(maxfreq &lt; widget.frequency)
       
  2643 </xsl:text>
       
  2644           <xsl:text>                    maxfreq = widget.frequency;
       
  2645 </xsl:text>
       
  2646           <xsl:text>
       
  2647 </xsl:text>
       
  2648           <xsl:text>            if(maxfreq != 0)
       
  2649 </xsl:text>
       
  2650           <xsl:text>                new_period = 1000/maxfreq;
       
  2651 </xsl:text>
       
  2652           <xsl:text>        }
       
  2653 </xsl:text>
       
  2654           <xsl:text>
       
  2655 </xsl:text>
       
  2656           <xsl:text>        if(previous_period != new_period) {
       
  2657 </xsl:text>
       
  2658           <xsl:text>            subscriptions[index] = new_period;
       
  2659 </xsl:text>
       
  2660           <xsl:text>            delta.push(
       
  2661 </xsl:text>
       
  2662           <xsl:text>                new Uint8Array([2]), /* subscribe = 2 */
       
  2663 </xsl:text>
       
  2664           <xsl:text>                new Uint32Array([index]),
       
  2665 </xsl:text>
       
  2666           <xsl:text>                new Uint16Array([new_period]));
       
  2667 </xsl:text>
       
  2668           <xsl:text>        }
       
  2669 </xsl:text>
       
  2670           <xsl:text>    }
       
  2671 </xsl:text>
       
  2672           <xsl:text>    send_blob(delta);
       
  2673 </xsl:text>
       
  2674           <xsl:text>};
       
  2675 </xsl:text>
       
  2676           <xsl:text>
       
  2677 </xsl:text>
       
  2678           <xsl:text>function send_hmi_value(index, value) {
       
  2679 </xsl:text>
       
  2680           <xsl:text>    let iectype = hmitree_types[index];
       
  2681 </xsl:text>
       
  2682           <xsl:text>    let tobinary = typedarray_types[iectype];
       
  2683 </xsl:text>
       
  2684           <xsl:text>    send_blob([
       
  2685 </xsl:text>
       
  2686           <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
       
  2687 </xsl:text>
       
  2688           <xsl:text>        new Uint32Array([index]),
       
  2689 </xsl:text>
       
  2690           <xsl:text>        tobinary(value)]);
       
  2691 </xsl:text>
       
  2692           <xsl:text>
       
  2693 </xsl:text>
       
  2694           <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
  2695 </xsl:text>
       
  2696           <xsl:text>    // cache[index] = value;
       
  2697 </xsl:text>
       
  2698           <xsl:text>};
       
  2699 </xsl:text>
       
  2700           <xsl:text>
       
  2701 </xsl:text>
       
  2702           <xsl:text>function apply_hmi_value(index, new_val) {
       
  2703 </xsl:text>
       
  2704           <xsl:text>    let old_val = cache[index]
       
  2705 </xsl:text>
       
  2706           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  2707 </xsl:text>
       
  2708           <xsl:text>        send_hmi_value(index, new_val);
       
  2709 </xsl:text>
       
  2710           <xsl:text>    return new_val;
       
  2711 </xsl:text>
       
  2712           <xsl:text>}
       
  2713 </xsl:text>
       
  2714           <xsl:text>
       
  2715 </xsl:text>
       
  2716           <xsl:text>function change_hmi_value(index, opstr) {
       
  2717 </xsl:text>
       
  2718           <xsl:text>    let op = opstr[0];
       
  2719 </xsl:text>
       
  2720           <xsl:text>    let given_val = opstr.slice(1);
       
  2721 </xsl:text>
       
  2722           <xsl:text>    let old_val = cache[index]
       
  2723 </xsl:text>
       
  2724           <xsl:text>    let new_val;
       
  2725 </xsl:text>
       
  2726           <xsl:text>    switch(op){
       
  2727 </xsl:text>
       
  2728           <xsl:text>      case "=":
       
  2729 </xsl:text>
       
  2730           <xsl:text>        eval("new_val"+opstr);
       
  2731 </xsl:text>
       
  2732           <xsl:text>        break;
       
  2733 </xsl:text>
       
  2734           <xsl:text>      case "+":
       
  2735 </xsl:text>
       
  2736           <xsl:text>      case "-":
       
  2737 </xsl:text>
       
  2738           <xsl:text>      case "*":
       
  2739 </xsl:text>
       
  2740           <xsl:text>      case "/":
       
  2741 </xsl:text>
       
  2742           <xsl:text>        if(old_val != undefined)
       
  2743 </xsl:text>
       
  2744           <xsl:text>            new_val = eval("old_val"+opstr);
       
  2745 </xsl:text>
       
  2746           <xsl:text>        break;
       
  2747 </xsl:text>
       
  2748           <xsl:text>    }
       
  2749 </xsl:text>
       
  2750           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  2751 </xsl:text>
       
  2752           <xsl:text>        send_hmi_value(index, new_val);
       
  2753 </xsl:text>
       
  2754           <xsl:text>    return new_val;
       
  2755 </xsl:text>
       
  2756           <xsl:text>}
       
  2757 </xsl:text>
       
  2758           <xsl:text>
       
  2759 </xsl:text>
       
  2760           <xsl:text>var current_visible_page;
       
  2761 </xsl:text>
       
  2762           <xsl:text>var current_subscribed_page;
       
  2763 </xsl:text>
       
  2764           <xsl:text>var current_page_index;
       
  2765 </xsl:text>
       
  2766           <xsl:text>
       
  2767 </xsl:text>
       
  2768           <xsl:text>function prepare_svg() {
       
  2769 </xsl:text>
       
  2770           <xsl:text>    for(let eltid in detachable_elements){
       
  2771 </xsl:text>
       
  2772           <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
  2773 </xsl:text>
       
  2774           <xsl:text>        parent.removeChild(element);
       
  2775 </xsl:text>
       
  2776           <xsl:text>    }
       
  2777 </xsl:text>
       
  2778           <xsl:text>};
       
  2779 </xsl:text>
       
  2780           <xsl:text>
       
  2781 </xsl:text>
       
  2782           <xsl:text>function switch_page(page_name, page_index) {
       
  2783 </xsl:text>
       
  2784           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  2785 </xsl:text>
       
  2786           <xsl:text>        /* page switch already going */
       
  2787 </xsl:text>
       
  2788           <xsl:text>        /* TODO LOG ERROR */
       
  2789 </xsl:text>
       
  2790           <xsl:text>        return false;
       
  2791 </xsl:text>
       
  2792           <xsl:text>    }
       
  2793 </xsl:text>
       
  2794           <xsl:text>
       
  2795 </xsl:text>
       
  2796           <xsl:text>    if(page_name == undefined)
       
  2797 </xsl:text>
       
  2798           <xsl:text>        page_name = current_subscribed_page;
       
  2799 </xsl:text>
       
  2800           <xsl:text>
       
  2801 </xsl:text>
       
  2802           <xsl:text>
       
  2803 </xsl:text>
       
  2804           <xsl:text>    let old_desc = page_desc[current_subscribed_page];
       
  2805 </xsl:text>
       
  2806           <xsl:text>    let new_desc = page_desc[page_name];
       
  2807 </xsl:text>
       
  2808           <xsl:text>
       
  2809 </xsl:text>
       
  2810           <xsl:text>    if(new_desc == undefined){
       
  2811 </xsl:text>
       
  2812           <xsl:text>        /* TODO LOG ERROR */
       
  2813 </xsl:text>
       
  2814           <xsl:text>        return false;
       
  2815 </xsl:text>
       
  2816           <xsl:text>    }
       
  2817 </xsl:text>
       
  2818           <xsl:text>
       
  2819 </xsl:text>
       
  2820           <xsl:text>    if(page_index == undefined){
       
  2821 </xsl:text>
       
  2822           <xsl:text>        page_index = new_desc.page_index;
       
  2823 </xsl:text>
       
  2824           <xsl:text>    }
       
  2825 </xsl:text>
       
  2826           <xsl:text>
       
  2827 </xsl:text>
       
  2828           <xsl:text>    if(old_desc){
       
  2829 </xsl:text>
       
  2830           <xsl:text>        old_desc.absolute_widgets.map(w=&gt;w.unsub());
       
  2831 </xsl:text>
       
  2832           <xsl:text>        old_desc.relative_widgets.map(w=&gt;w.unsub());
       
  2833 </xsl:text>
       
  2834           <xsl:text>    }
       
  2835 </xsl:text>
       
  2836           <xsl:text>    new_desc.absolute_widgets.map(w=&gt;w.sub());
       
  2837 </xsl:text>
       
  2838           <xsl:text>    var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
       
  2839 </xsl:text>
       
  2840           <xsl:text>    new_desc.relative_widgets.map(w=&gt;w.sub(new_offset));
       
  2841 </xsl:text>
       
  2842           <xsl:text>
       
  2843 </xsl:text>
       
  2844           <xsl:text>    update_subscriptions();
       
  2845 </xsl:text>
       
  2846           <xsl:text>
       
  2847 </xsl:text>
       
  2848           <xsl:text>    current_subscribed_page = page_name;
       
  2849 </xsl:text>
       
  2850           <xsl:text>    current_page_index = page_index;
       
  2851 </xsl:text>
       
  2852           <xsl:text>
       
  2853 </xsl:text>
       
  2854           <xsl:text>    jumps_need_update = true;
       
  2855 </xsl:text>
       
  2856           <xsl:text>
       
  2857 </xsl:text>
       
  2858           <xsl:text>    requestHMIAnimation();
       
  2859 </xsl:text>
       
  2860           <xsl:text>
       
  2861 </xsl:text>
       
  2862           <xsl:text>    jump_history.push([page_name, page_index]);
       
  2863 </xsl:text>
       
  2864           <xsl:text>    if(jump_history.length &gt; 42)
       
  2865 </xsl:text>
       
  2866           <xsl:text>        jump_history.shift();
       
  2867 </xsl:text>
       
  2868           <xsl:text>
       
  2869 </xsl:text>
       
  2870           <xsl:text>    return true;
       
  2871 </xsl:text>
       
  2872           <xsl:text>};
       
  2873 </xsl:text>
       
  2874           <xsl:text>
       
  2875 </xsl:text>
       
  2876           <xsl:text>function* chain(a,b){
       
  2877 </xsl:text>
       
  2878           <xsl:text>    yield* a;
       
  2879 </xsl:text>
       
  2880           <xsl:text>    yield* b;
       
  2881 </xsl:text>
       
  2882           <xsl:text>};
       
  2883 </xsl:text>
       
  2884           <xsl:text>
       
  2885 </xsl:text>
       
  2886           <xsl:text>function unsubscribe(){
       
  2887 </xsl:text>
       
  2888           <xsl:text>    /* remove subsribers */
       
  2889 </xsl:text>
       
  2890           <xsl:text>    for(let index of this.indexes){
       
  2891 </xsl:text>
       
  2892           <xsl:text>        let idx = index + this.offset;
       
  2893 </xsl:text>
       
  2894           <xsl:text>        subscribers[idx].delete(this);
       
  2895 </xsl:text>
       
  2896           <xsl:text>    }
       
  2897 </xsl:text>
       
  2898           <xsl:text>    this.offset = 0;
       
  2899 </xsl:text>
       
  2900           <xsl:text>}
       
  2901 </xsl:text>
       
  2902           <xsl:text>
       
  2903 </xsl:text>
       
  2904           <xsl:text>function subscribe(new_offset=0){
       
  2905 </xsl:text>
       
  2906           <xsl:text>    /* set the offset because relative */
       
  2907 </xsl:text>
       
  2908           <xsl:text>    this.offset = new_offset;
       
  2909 </xsl:text>
       
  2910           <xsl:text>    /* add this's subsribers */
       
  2911 </xsl:text>
       
  2912           <xsl:text>    for(let index of this.indexes){
       
  2913 </xsl:text>
       
  2914           <xsl:text>        subscribers[index + new_offset].add(this);
       
  2915 </xsl:text>
       
  2916           <xsl:text>    }
       
  2917 </xsl:text>
       
  2918           <xsl:text>    need_cache_apply.push(this); 
       
  2919 </xsl:text>
       
  2920           <xsl:text>}
       
  2921 </xsl:text>
       
  2922           <xsl:text>
       
  2923 </xsl:text>
       
  2924           <xsl:text>function foreach_unsubscribe(){
       
  2925 </xsl:text>
       
  2926           <xsl:text>    for(let item of this.items){
       
  2927 </xsl:text>
       
  2928           <xsl:text>        for(let widget of item) {
       
  2929 </xsl:text>
       
  2930           <xsl:text>            unsubscribe.call(widget);
       
  2931 </xsl:text>
       
  2932           <xsl:text>        }
       
  2933 </xsl:text>
       
  2934           <xsl:text>    }
       
  2935 </xsl:text>
       
  2936           <xsl:text>    this.offset = 0;
       
  2937 </xsl:text>
       
  2938           <xsl:text>}
       
  2939 </xsl:text>
       
  2940           <xsl:text>
       
  2941 </xsl:text>
       
  2942           <xsl:text>function foreach_widgets_do(new_offset, todo){
       
  2943 </xsl:text>
       
  2944           <xsl:text>    this.offset = new_offset;
       
  2945 </xsl:text>
       
  2946           <xsl:text>    for(let i = 0; i &lt; this.items.length; i++) {
       
  2947 </xsl:text>
       
  2948           <xsl:text>        let item = this.items[i];
       
  2949 </xsl:text>
       
  2950           <xsl:text>        let orig_item_index = this.index_pool[i];
       
  2951 </xsl:text>
       
  2952           <xsl:text>        let item_index = this.index_pool[i+this.item_offset];
       
  2953 </xsl:text>
       
  2954           <xsl:text>        let item_index_offset = item_index - orig_item_index;
       
  2955 </xsl:text>
       
  2956           <xsl:text>        for(let widget of item) {
       
  2957 </xsl:text>
       
  2958           <xsl:text>            todo.call(widget, new_offset + item_index_offset);
       
  2959 </xsl:text>
       
  2960           <xsl:text>        }
       
  2961 </xsl:text>
       
  2962           <xsl:text>    }
       
  2963 </xsl:text>
       
  2964           <xsl:text>}
       
  2965 </xsl:text>
       
  2966           <xsl:text>
       
  2967 </xsl:text>
       
  2968           <xsl:text>function foreach_subscribe(new_offset=0){
       
  2969 </xsl:text>
       
  2970           <xsl:text>    foreach_widgets_do.call(this, new_offset, subscribe);
       
  2971 </xsl:text>
       
  2972           <xsl:text>}
       
  2973 </xsl:text>
       
  2974           <xsl:text>
       
  2975 </xsl:text>
       
  2976           <xsl:text>function widget_apply_cache() {
       
  2977 </xsl:text>
       
  2978           <xsl:text>    for(let index of this.indexes){
       
  2979 </xsl:text>
       
  2980           <xsl:text>        /* dispatch current cache in newly opened page widgets */
       
  2981 </xsl:text>
       
  2982           <xsl:text>        let realindex = index+this.offset;
       
  2983 </xsl:text>
       
  2984           <xsl:text>        let cached_val = cache[realindex];
       
  2985 </xsl:text>
       
  2986           <xsl:text>        if(cached_val != undefined)
       
  2987 </xsl:text>
       
  2988           <xsl:text>            dispatch_value_to_widget(this, realindex, cached_val, cached_val);
       
  2989 </xsl:text>
       
  2990           <xsl:text>    }
       
  2991 </xsl:text>
       
  2992           <xsl:text>}
       
  2993 </xsl:text>
       
  2994           <xsl:text>
       
  2995 </xsl:text>
       
  2996           <xsl:text>function foreach_apply_cache() {
       
  2997 </xsl:text>
       
  2998           <xsl:text>    foreach_widgets_do.call(this, this.offset, widget_apply_cache);
       
  2999 </xsl:text>
       
  3000           <xsl:text>}
       
  3001 </xsl:text>
       
  3002           <xsl:text>
       
  3003 </xsl:text>
       
  3004           <xsl:text>function foreach_onclick(opstr, evt) {
       
  3005 </xsl:text>
       
  3006           <xsl:text>    new_item_offset = eval(String(this.item_offset)+opstr)
       
  3007 </xsl:text>
       
  3008           <xsl:text>    if(new_item_offset + this.items.length &gt; this.index_pool.length) {
       
  3009 </xsl:text>
       
  3010           <xsl:text>        if(this.item_offset + this.items.length == this.index_pool.length)
       
  3011 </xsl:text>
       
  3012           <xsl:text>            new_item_offset = 0;
       
  3013 </xsl:text>
       
  3014           <xsl:text>        else
       
  3015 </xsl:text>
       
  3016           <xsl:text>            new_item_offset = this.index_pool.length - this.items.length;
       
  3017 </xsl:text>
       
  3018           <xsl:text>    } else if(new_item_offset &lt; 0) {
       
  3019 </xsl:text>
       
  3020           <xsl:text>        if(this.item_offset == 0)
       
  3021 </xsl:text>
       
  3022           <xsl:text>            new_item_offset = this.index_pool.length - this.items.length;
       
  3023 </xsl:text>
       
  3024           <xsl:text>        else
       
  3025 </xsl:text>
       
  3026           <xsl:text>            new_item_offset = 0;
       
  3027 </xsl:text>
       
  3028           <xsl:text>    }
       
  3029 </xsl:text>
       
  3030           <xsl:text>    this.item_offset = new_item_offset;
       
  3031 </xsl:text>
       
  3032           <xsl:text>    off = this.offset;
       
  3033 </xsl:text>
       
  3034           <xsl:text>    foreach_unsubscribe.call(this);
       
  3035 </xsl:text>
       
  3036           <xsl:text>    foreach_subscribe.call(this,off);
       
  3037 </xsl:text>
       
  3038           <xsl:text>    update_subscriptions();
       
  3039 </xsl:text>
       
  3040           <xsl:text>    need_cache_apply.push(this);
       
  3041 </xsl:text>
       
  3042           <xsl:text>    jumps_need_update = true;
       
  3043 </xsl:text>
       
  3044           <xsl:text>    requestHMIAnimation();
       
  3045 </xsl:text>
       
  3046           <xsl:text>}
       
  3047 </xsl:text>
       
  3048           <xsl:text>
       
  3049 </xsl:text>
       
  3050           <xsl:text>
       
  3051 </xsl:text>
       
  3052           <xsl:text>function switch_visible_page(page_name) {
       
  3053 </xsl:text>
       
  3054           <xsl:text>
       
  3055 </xsl:text>
       
  3056           <xsl:text>    let old_desc = page_desc[current_visible_page];
       
  3057 </xsl:text>
       
  3058           <xsl:text>    let new_desc = page_desc[page_name];
       
  3059 </xsl:text>
       
  3060           <xsl:text>
       
  3061 </xsl:text>
       
  3062           <xsl:text>    if(old_desc){
       
  3063 </xsl:text>
       
  3064           <xsl:text>        for(let eltid in old_desc.required_detachables){
       
  3065 </xsl:text>
       
  3066           <xsl:text>            if(!(eltid in new_desc.required_detachables)){
       
  3067 </xsl:text>
       
  3068           <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
       
  3069 </xsl:text>
       
  3070           <xsl:text>                parent.removeChild(element);
       
  3071 </xsl:text>
       
  3072           <xsl:text>            }
       
  3073 </xsl:text>
       
  3074           <xsl:text>        }
       
  3075 </xsl:text>
       
  3076           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
  3077 </xsl:text>
       
  3078           <xsl:text>            if(!(eltid in old_desc.required_detachables)){
       
  3079 </xsl:text>
       
  3080           <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
       
  3081 </xsl:text>
       
  3082           <xsl:text>                parent.appendChild(element);
       
  3083 </xsl:text>
       
  3084           <xsl:text>            }
       
  3085 </xsl:text>
       
  3086           <xsl:text>        }
       
  3087 </xsl:text>
       
  3088           <xsl:text>    }else{
       
  3089 </xsl:text>
       
  3090           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
  3091 </xsl:text>
       
  3092           <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
       
  3093 </xsl:text>
       
  3094           <xsl:text>            parent.appendChild(element);
       
  3095 </xsl:text>
       
  3096           <xsl:text>        }
       
  3097 </xsl:text>
       
  3098           <xsl:text>    }
       
  3099 </xsl:text>
       
  3100           <xsl:text>
       
  3101 </xsl:text>
       
  3102           <xsl:text>    svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
       
  3103 </xsl:text>
       
  3104           <xsl:text>    current_visible_page = page_name;
       
  3105 </xsl:text>
       
  3106           <xsl:text>};
       
  3107 </xsl:text>
       
  3108           <xsl:text>
       
  3109 </xsl:text>
       
  3110           <xsl:text>function update_jumps() {
       
  3111 </xsl:text>
       
  3112           <xsl:text>    page_desc[current_visible_page].jumps.map(w=&gt;w.notify_page_change(current_visible_page,current_page_index));
       
  3113 </xsl:text>
       
  3114           <xsl:text>    jumps_need_update = false;
       
  3115 </xsl:text>
       
  3116           <xsl:text>};
       
  3117 </xsl:text>
       
  3118           <xsl:text>
       
  3119 </xsl:text>
       
  3120           <xsl:text>
       
  3121 </xsl:text>
       
  3122           <xsl:text>// Once connection established
       
  3123 </xsl:text>
       
  3124           <xsl:text>ws.onopen = function (evt) {
       
  3125 </xsl:text>
       
  3126           <xsl:text>    init_widgets();
       
  3127 </xsl:text>
       
  3128           <xsl:text>    send_reset();
       
  3129 </xsl:text>
       
  3130           <xsl:text>    // show main page
       
  3131 </xsl:text>
       
  3132           <xsl:text>    prepare_svg();
       
  3133 </xsl:text>
       
  3134           <xsl:text>    switch_page(default_page);
       
  3135 </xsl:text>
       
  3136           <xsl:text>};
       
  3137 </xsl:text>
       
  3138           <xsl:text>
       
  3139 </xsl:text>
       
  3140           <xsl:text>ws.onclose = function (evt) {
       
  3141 </xsl:text>
       
  3142           <xsl:text>    // TODO : add visible notification while waiting for reload
       
  3143 </xsl:text>
       
  3144           <xsl:text>    console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s.");
       
  3145 </xsl:text>
       
  3146           <xsl:text>    // TODO : re-enable auto reload when not in debug
       
  3147 </xsl:text>
       
  3148           <xsl:text>    //window.setTimeout(() =&gt; location.reload(true), 10000);
       
  3149 </xsl:text>
       
  3150           <xsl:text>    alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+".");
       
  3151 </xsl:text>
       
  3152           <xsl:text>
       
  3153 </xsl:text>
       
  3154           <xsl:text>};
       
  3155 </xsl:text>
       
  3156           <xsl:text>
       
  3157 </xsl:text>
       
  3158           <xsl:text>var xmlns = "http://www.w3.org/2000/svg";
       
  3159 </xsl:text>
       
  3160           <xsl:text>var edit_callback;
       
  3161 </xsl:text>
       
  3162           <xsl:text>function edit_value(path, valuetype, callback, initial) {
       
  3163 </xsl:text>
       
  3164           <xsl:text>
       
  3165 </xsl:text>
       
  3166           <xsl:text>    let [keypadid, xcoord, ycoord] = keypads[valuetype];
       
  3167 </xsl:text>
       
  3168           <xsl:text>    console.log('XXX TODO : Edit value', path, valuetype, callback, initial, keypadid);
       
  3169 </xsl:text>
       
  3170           <xsl:text>    edit_callback = callback;
       
  3171 </xsl:text>
       
  3172           <xsl:text>    let widget = hmi_widgets[keypadid];
       
  3173 </xsl:text>
       
  3174           <xsl:text>    widget.start_edit(path, valuetype, callback, initial);
       
  3175 </xsl:text>
       
  3176           <xsl:text>};
       
  3177 </xsl:text>
       
  3178           <xsl:text>
       
  3179 </xsl:text>
       
  3180           <xsl:text>var current_modal; /* TODO stack ?*/
       
  3181 </xsl:text>
       
  3182           <xsl:text>
       
  3183 </xsl:text>
       
  3184           <xsl:text>function show_modal() {
       
  3185 </xsl:text>
       
  3186           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
  3187 </xsl:text>
       
  3188           <xsl:text>
       
  3189 </xsl:text>
       
  3190           <xsl:text>    tmpgrp = document.createElementNS(xmlns,"g");
       
  3191 </xsl:text>
       
  3192           <xsl:text>    tmpgrpattr = document.createAttribute("transform");
       
  3193 </xsl:text>
       
  3194           <xsl:text>
       
  3195 </xsl:text>
       
  3196           <xsl:text>    let [xcoord,ycoord] = this.coordinates;
       
  3197 </xsl:text>
       
  3198           <xsl:text>    let [xdest,ydest] = page_desc[current_visible_page].bbox;
       
  3199 </xsl:text>
       
  3200           <xsl:text>    tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")";
       
  3201 </xsl:text>
       
  3202           <xsl:text>    tmpgrp.setAttributeNode(tmpgrpattr);
       
  3203 </xsl:text>
       
  3204           <xsl:text>
       
  3205 </xsl:text>
       
  3206           <xsl:text>    tmpgrp.appendChild(element);
       
  3207 </xsl:text>
       
  3208           <xsl:text>    parent.appendChild(tmpgrp);
       
  3209 </xsl:text>
       
  3210           <xsl:text>
       
  3211 </xsl:text>
       
  3212           <xsl:text>    current_modal = [this.element.id, tmpgrp];
       
  3213 </xsl:text>
       
  3214           <xsl:text>};
       
  3215 </xsl:text>
       
  3216           <xsl:text>
       
  3217 </xsl:text>
       
  3218           <xsl:text>function end_modal() {
       
  3219 </xsl:text>
       
  3220           <xsl:text>    let [eltid, tmpgrp] = current_modal;
       
  3221 </xsl:text>
       
  3222           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
  3223 </xsl:text>
       
  3224           <xsl:text>
       
  3225 </xsl:text>
       
  3226           <xsl:text>    parent.removeChild(tmpgrp);
       
  3227 </xsl:text>
       
  3228           <xsl:text>
       
  3229 </xsl:text>
       
  3230           <xsl:text>    current_modal = undefined;
       
  3231 </xsl:text>
       
  3232           <xsl:text>};
       
  3233 </xsl:text>
       
  3234           <xsl:text>
       
  3235 </xsl:text>
       
  3236           <xsl:text>function widget_active_activable(eltsub) {
       
  3237 </xsl:text>
       
  3238           <xsl:text>    if(eltsub.inactive_style === undefined)
       
  3239 </xsl:text>
       
  3240           <xsl:text>        eltsub.inactive_style = eltsub.inactive.getAttribute("style");
       
  3241 </xsl:text>
       
  3242           <xsl:text>    eltsub.inactive.setAttribute("style", "display:none");
       
  3243 </xsl:text>
       
  3244           <xsl:text>    if(eltsub.active_style !== undefined)
       
  3245 </xsl:text>
       
  3246           <xsl:text>            eltsub.active.setAttribute("style", eltsub.active_style);
       
  3247 </xsl:text>
       
  3248           <xsl:text>    console.log("active", eltsub);
       
  3249 </xsl:text>
       
  3250           <xsl:text>};
       
  3251 </xsl:text>
       
  3252           <xsl:text>function widget_inactive_activable(eltsub) {
       
  3253 </xsl:text>
       
  3254           <xsl:text>    if(eltsub.active_style === undefined)
       
  3255 </xsl:text>
       
  3256           <xsl:text>        eltsub.active_style = eltsub.active.getAttribute("style");
       
  3257 </xsl:text>
       
  3258           <xsl:text>    eltsub.active.setAttribute("style", "display:none");
       
  3259 </xsl:text>
       
  3260           <xsl:text>    if(eltsub.inactive_style !== undefined)
       
  3261 </xsl:text>
       
  3262           <xsl:text>            eltsub.inactive.setAttribute("style", eltsub.inactive_style);
       
  3263 </xsl:text>
       
  3264           <xsl:text>    console.log("inactive", eltsub);
       
  3265 </xsl:text>
       
  3266           <xsl:text>};
       
  3267 </xsl:text>
  3248         </script>
  3268         </script>
  3249       </body>
  3269       </body>
  3250     </html>
  3270     </html>
  3251   </xsl:template>
  3271   </xsl:template>
  3252 </xsl:stylesheet>
  3272 </xsl:stylesheet>