svghmi/gen_index_xhtml.xslt
changeset 3495 f422d3d71f89
parent 3418 a1d9a0353053
child 3507 e87a2daace80
equal deleted inserted replaced
3494:435259844a64 3495:f422d3d71f89
   205         </xsl:attribute>
   205         </xsl:attribute>
   206         <xsl:attribute name="type">
   206         <xsl:attribute name="type">
   207           <xsl:value-of select="$type"/>
   207           <xsl:value-of select="$type"/>
   208         </xsl:attribute>
   208         </xsl:attribute>
   209         <xsl:if test="$freq">
   209         <xsl:if test="$freq">
       
   210           <xsl:if test="not(regexp:test($freq,'^[0-9]*(\.[0-9]+)?[smh]?'))">
       
   211             <xsl:message terminate="yes">
       
   212               <xsl:text>Widget id:</xsl:text>
       
   213               <xsl:value-of select="$id"/>
       
   214               <xsl:text> label:</xsl:text>
       
   215               <xsl:value-of select="$label"/>
       
   216               <xsl:text> has wrong syntax of frequency forcing </xsl:text>
       
   217               <xsl:value-of select="$freq"/>
       
   218             </xsl:message>
       
   219           </xsl:if>
   210           <xsl:attribute name="freq">
   220           <xsl:attribute name="freq">
   211             <xsl:value-of select="$freq"/>
   221             <xsl:value-of select="$freq"/>
   212           </xsl:attribute>
   222           </xsl:attribute>
   213         </xsl:if>
   223         </xsl:if>
   214         <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
   224         <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
  1246       </xsl:for-each>
  1256       </xsl:for-each>
  1247     </xsl:variable>
  1257     </xsl:variable>
  1248     <xsl:variable name="freq">
  1258     <xsl:variable name="freq">
  1249       <xsl:choose>
  1259       <xsl:choose>
  1250         <xsl:when test="$widget/@freq">
  1260         <xsl:when test="$widget/@freq">
       
  1261           <xsl:text>"</xsl:text>
  1251           <xsl:value-of select="$widget/@freq"/>
  1262           <xsl:value-of select="$widget/@freq"/>
       
  1263           <xsl:text>"</xsl:text>
  1252         </xsl:when>
  1264         </xsl:when>
  1253         <xsl:otherwise>
  1265         <xsl:otherwise>
  1254           <xsl:text>undefined</xsl:text>
  1266           <xsl:text>undefined</xsl:text>
  1255         </xsl:otherwise>
  1267         </xsl:otherwise>
  1256       </xsl:choose>
  1268       </xsl:choose>
  1475 </xsl:text>
  1487 </xsl:text>
  1476     <xsl:text>        this.bound_unhinibit = this.unhinibit.bind(this);
  1488     <xsl:text>        this.bound_unhinibit = this.unhinibit.bind(this);
  1477 </xsl:text>
  1489 </xsl:text>
  1478     <xsl:text>        this.forced_frequency = freq;
  1490     <xsl:text>        this.forced_frequency = freq;
  1479 </xsl:text>
  1491 </xsl:text>
       
  1492     <xsl:text>        this.clip = true;
       
  1493 </xsl:text>
       
  1494     <xsl:text>    }
       
  1495 </xsl:text>
       
  1496     <xsl:text>
       
  1497 </xsl:text>
       
  1498     <xsl:text>    do_init(){
       
  1499 </xsl:text>
       
  1500     <xsl:text>        let forced = this.forced_frequency;
       
  1501 </xsl:text>
       
  1502     <xsl:text>        if(forced !== undefined){
       
  1503 </xsl:text>
       
  1504     <xsl:text>            /*
       
  1505 </xsl:text>
       
  1506     <xsl:text>            once every 10 seconds : 10s
       
  1507 </xsl:text>
       
  1508     <xsl:text>            once per minute : 1m
       
  1509 </xsl:text>
       
  1510     <xsl:text>            once per hour : 1h
       
  1511 </xsl:text>
       
  1512     <xsl:text>            once per day : 1d
       
  1513 </xsl:text>
       
  1514     <xsl:text>            */
       
  1515 </xsl:text>
       
  1516     <xsl:text>            let unit = forced.slice(-1);
       
  1517 </xsl:text>
       
  1518     <xsl:text>            let factor = {
       
  1519 </xsl:text>
       
  1520     <xsl:text>                "s":1,
       
  1521 </xsl:text>
       
  1522     <xsl:text>                "m":60,
       
  1523 </xsl:text>
       
  1524     <xsl:text>                "h":3600,
       
  1525 </xsl:text>
       
  1526     <xsl:text>                "d":86400}[unit];
       
  1527 </xsl:text>
       
  1528     <xsl:text>
       
  1529 </xsl:text>
       
  1530     <xsl:text>            this.frequency = factor ? 1/(factor * Number(forced.slice(0,-1)))
       
  1531 </xsl:text>
       
  1532     <xsl:text>                                      : Number(forced);
       
  1533 </xsl:text>
       
  1534     <xsl:text>        }
       
  1535 </xsl:text>
       
  1536     <xsl:text>
       
  1537 </xsl:text>
       
  1538     <xsl:text>        let init = this.init;
       
  1539 </xsl:text>
       
  1540     <xsl:text>        if(typeof(init) == "function"){
       
  1541 </xsl:text>
       
  1542     <xsl:text>            try {
       
  1543 </xsl:text>
       
  1544     <xsl:text>                init.call(this);
       
  1545 </xsl:text>
       
  1546     <xsl:text>            } catch(err) {
       
  1547 </xsl:text>
       
  1548     <xsl:text>                console.log(err);
       
  1549 </xsl:text>
       
  1550     <xsl:text>            }
       
  1551 </xsl:text>
       
  1552     <xsl:text>        }
       
  1553 </xsl:text>
  1480     <xsl:text>    }
  1554     <xsl:text>    }
  1481 </xsl:text>
  1555 </xsl:text>
  1482     <xsl:text>
  1556     <xsl:text>
  1483 </xsl:text>
  1557 </xsl:text>
  1484     <xsl:text>    unsub(){
  1558     <xsl:text>    unsub(){
  1647 </xsl:text>
  1721 </xsl:text>
  1648     <xsl:text>        let old_val = cache[realindex];
  1722     <xsl:text>        let old_val = cache[realindex];
  1649 </xsl:text>
  1723 </xsl:text>
  1650     <xsl:text>        let new_val = eval_operation_string(old_val, opstr);
  1724     <xsl:text>        let new_val = eval_operation_string(old_val, opstr);
  1651 </xsl:text>
  1725 </xsl:text>
  1652     <xsl:text>        new_val = this.clip_min_max(index, new_val);
  1726     <xsl:text>        if(this.clip)
       
  1727 </xsl:text>
       
  1728     <xsl:text>            new_val = this.clip_min_max(index, new_val);
  1653 </xsl:text>
  1729 </xsl:text>
  1654     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1730     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1655 </xsl:text>
  1731 </xsl:text>
  1656     <xsl:text>    }
  1732     <xsl:text>    }
  1657 </xsl:text>
  1733 </xsl:text>
  1661 </xsl:text>
  1737 </xsl:text>
  1662     <xsl:text>        let realindex = this.get_variable_index(index);
  1738     <xsl:text>        let realindex = this.get_variable_index(index);
  1663 </xsl:text>
  1739 </xsl:text>
  1664     <xsl:text>        if(realindex == undefined) return undefined;
  1740     <xsl:text>        if(realindex == undefined) return undefined;
  1665 </xsl:text>
  1741 </xsl:text>
  1666     <xsl:text>        new_val = this.clip_min_max(index, new_val);
  1742     <xsl:text>        if(this.clip)
       
  1743 </xsl:text>
       
  1744     <xsl:text>            new_val = this.clip_min_max(index, new_val);
  1667 </xsl:text>
  1745 </xsl:text>
  1668     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1746     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1669 </xsl:text>
  1747 </xsl:text>
  1670     <xsl:text>    }
  1748     <xsl:text>    }
  1671 </xsl:text>
  1749 </xsl:text>
  1881     <xsl:param name="mandatory" select="'yes'"/>
  1959     <xsl:param name="mandatory" select="'yes'"/>
  1882     <xsl:param name="subelements" select="/.."/>
  1960     <xsl:param name="subelements" select="/.."/>
  1883     <xsl:param name="hmi_element"/>
  1961     <xsl:param name="hmi_element"/>
  1884     <xsl:variable name="widget_type" select="@type"/>
  1962     <xsl:variable name="widget_type" select="@type"/>
  1885     <xsl:for-each select="str:split($labels)">
  1963     <xsl:for-each select="str:split($labels)">
  1886       <xsl:variable name="name" select="."/>
  1964       <xsl:variable name="absolute" select="starts-with(., '/')"/>
  1887       <xsl:variable name="elt" select="$result_widgets[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]"/>
  1965       <xsl:variable name="name" select="substring(.,number($absolute)+1)"/>
       
  1966       <xsl:variable name="widget" select="$result_widgets[@id = $hmi_element/@id]"/>
       
  1967       <xsl:variable name="elt" select="($widget//*[not($absolute) and @inkscape:label=$name] | $widget/*[$absolute and @inkscape:label=$name])[1]"/>
  1888       <xsl:choose>
  1968       <xsl:choose>
  1889         <xsl:when test="not($elt/@id)">
  1969         <xsl:when test="not($elt/@id)">
  1890           <xsl:if test="$mandatory='yes'">
  1970           <xsl:if test="$mandatory='yes'">
  1891             <xsl:message terminate="yes">
  1971             <xsl:message terminate="yes">
  1892               <xsl:value-of select="$widget_type"/>
  1972               <xsl:value-of select="$widget_type"/>
  2398   <xsl:template mode="actions" match="state">
  2478   <xsl:template mode="actions" match="state">
  2399     <xsl:text>    </xsl:text>
  2479     <xsl:text>    </xsl:text>
  2400     <xsl:value-of select="@name"/>
  2480     <xsl:value-of select="@name"/>
  2401     <xsl:text>_action(){
  2481     <xsl:text>_action(){
  2402 </xsl:text>
  2482 </xsl:text>
  2403     <xsl:text>console.log("Entering state </xsl:text>
       
  2404     <xsl:value-of select="@name"/>
       
  2405     <xsl:text>", this.frequency);
       
  2406 </xsl:text>
       
  2407     <xsl:apply-templates mode="actions" select="*"/>
  2483     <xsl:apply-templates mode="actions" select="*"/>
  2408     <xsl:text>    }
  2484     <xsl:text>    }
  2409 </xsl:text>
  2485 </xsl:text>
  2410   </xsl:template>
  2486   </xsl:template>
  2411   <xsl:template mode="actions" match="show">
  2487   <xsl:template mode="actions" match="show">
  2422     <xsl:text>);
  2498     <xsl:text>);
  2423 </xsl:text>
  2499 </xsl:text>
  2424   </xsl:template>
  2500   </xsl:template>
  2425   <xsl:template name="generated_button_class">
  2501   <xsl:template name="generated_button_class">
  2426     <xsl:param name="fsm"/>
  2502     <xsl:param name="fsm"/>
  2427     <xsl:text>    frequency = 5;
       
  2428 </xsl:text>
       
  2429     <xsl:text>    display = "inactive";
  2503     <xsl:text>    display = "inactive";
  2430 </xsl:text>
  2504 </xsl:text>
  2431     <xsl:text>    state = "init";
  2505     <xsl:text>    state = "init";
  2432 </xsl:text>
  2506 </xsl:text>
  2433     <xsl:text>    dispatch(value) {
  2507     <xsl:text>    dispatch(value) {
  2489   </xsl:template>
  2563   </xsl:template>
  2490   <xsl:template match="widget[@type='Button']" mode="widget_class">
  2564   <xsl:template match="widget[@type='Button']" mode="widget_class">
  2491     <xsl:text>class </xsl:text>
  2565     <xsl:text>class </xsl:text>
  2492     <xsl:text>ButtonWidget</xsl:text>
  2566     <xsl:text>ButtonWidget</xsl:text>
  2493     <xsl:text> extends Widget{
  2567     <xsl:text> extends Widget{
       
  2568 </xsl:text>
       
  2569     <xsl:text>    frequency = 5;
  2494 </xsl:text>
  2570 </xsl:text>
  2495     <xsl:variable name="fsm" select="exsl:node-set($_button_fsm)"/>
  2571     <xsl:variable name="fsm" select="exsl:node-set($_button_fsm)"/>
  2496     <xsl:call-template name="generated_button_class">
  2572     <xsl:call-template name="generated_button_class">
  2497       <xsl:with-param name="fsm" select="$fsm"/>
  2573       <xsl:with-param name="fsm" select="$fsm"/>
  2498     </xsl:call-template>
  2574     </xsl:call-template>
  2511   </xsl:template>
  2587   </xsl:template>
  2512   <xsl:template match="widget[@type='PushButton']" mode="widget_class">
  2588   <xsl:template match="widget[@type='PushButton']" mode="widget_class">
  2513     <xsl:text>class </xsl:text>
  2589     <xsl:text>class </xsl:text>
  2514     <xsl:text>PushButtonWidget</xsl:text>
  2590     <xsl:text>PushButtonWidget</xsl:text>
  2515     <xsl:text> extends Widget{
  2591     <xsl:text> extends Widget{
       
  2592 </xsl:text>
       
  2593     <xsl:text>    frequency = 20;
  2516 </xsl:text>
  2594 </xsl:text>
  2517     <xsl:variable name="fsm" select="exsl:node-set($_push_button_fsm)"/>
  2595     <xsl:variable name="fsm" select="exsl:node-set($_push_button_fsm)"/>
  2518     <xsl:call-template name="generated_button_class">
  2596     <xsl:call-template name="generated_button_class">
  2519       <xsl:with-param name="fsm" select="$fsm"/>
  2597       <xsl:with-param name="fsm" select="$fsm"/>
  2520     </xsl:call-template>
  2598     </xsl:call-template>
  6114   <xsl:template match="widget[@type='PathSlider']" mode="widget_desc">
  6192   <xsl:template match="widget[@type='PathSlider']" mode="widget_desc">
  6115     <type>
  6193     <type>
  6116       <xsl:value-of select="@type"/>
  6194       <xsl:value-of select="@type"/>
  6117     </type>
  6195     </type>
  6118     <longdesc>
  6196     <longdesc>
  6119       <xsl:text>PathSlider - 
  6197       <xsl:text>PathSlider -
  6120 </xsl:text>
  6198 </xsl:text>
  6121     </longdesc>
  6199     </longdesc>
  6122     <shortdesc>
  6200     <shortdesc>
  6123       <xsl:text>Slide an SVG element along a path by dragging it</xsl:text>
  6201       <xsl:text>Slide an SVG element along a path by dragging it</xsl:text>
  6124     </shortdesc>
  6202     </shortdesc>
  6157 </xsl:text>
  6235 </xsl:text>
  6158     <xsl:text>    precision = undefined;
  6236     <xsl:text>    precision = undefined;
  6159 </xsl:text>
  6237 </xsl:text>
  6160     <xsl:text>    origPt = undefined;
  6238     <xsl:text>    origPt = undefined;
  6161 </xsl:text>
  6239 </xsl:text>
  6162     <xsl:text>    
  6240     <xsl:text>
  6163 </xsl:text>
  6241 </xsl:text>
  6164     <xsl:text>
  6242     <xsl:text>
  6165 </xsl:text>
  6243 </xsl:text>
  6166     <xsl:text>    scanPath() {
  6244     <xsl:text>    scanPath() {
  6167 </xsl:text>
  6245 </xsl:text>
  6241 </xsl:text>
  6319 </xsl:text>
  6242     <xsl:text>          bestLength = beforeLength,
  6320     <xsl:text>          bestLength = beforeLength,
  6243 </xsl:text>
  6321 </xsl:text>
  6244     <xsl:text>          bestDistance = beforeDistance;
  6322     <xsl:text>          bestDistance = beforeDistance;
  6245 </xsl:text>
  6323 </xsl:text>
  6246     <xsl:text>        } else if ((afterLength = bestLength + precision) &lt;= this.pathLength &amp;&amp; 
  6324     <xsl:text>        } else if ((afterLength = bestLength + precision) &lt;= this.pathLength &amp;&amp;
  6247 </xsl:text>
  6325 </xsl:text>
  6248     <xsl:text>                   (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) &lt; bestDistance) {
  6326     <xsl:text>                   (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) &lt; bestDistance) {
  6249 </xsl:text>
  6327 </xsl:text>
  6250     <xsl:text>          bestPoint = afterPoint,
  6328     <xsl:text>          bestPoint = afterPoint,
  6251 </xsl:text>
  6329 </xsl:text>
  7625 </xsl:text>
  7703 </xsl:text>
  7626     <xsl:text>
  7704     <xsl:text>
  7627 </xsl:text>
  7705 </xsl:text>
  7628     <xsl:text>    activate(val) {
  7706     <xsl:text>    activate(val) {
  7629 </xsl:text>
  7707 </xsl:text>
  7630     <xsl:text>        let [active, inactive] = val ? ["none",""] : ["", "none"];
  7708     <xsl:text>        let [active, inactive] = val ? ["","none"] : ["none", ""];
  7631 </xsl:text>
  7709 </xsl:text>
  7632     <xsl:text>        if (this.active_elt)
  7710     <xsl:text>        if (this.active_elt)
  7633 </xsl:text>
  7711 </xsl:text>
  7634     <xsl:text>            this.active_elt.style.display = active;
  7712     <xsl:text>            this.active_elt.style.display = active;
  7635 </xsl:text>
  7713 </xsl:text>
  7749 </xsl:text>
  7827 </xsl:text>
  7750           <xsl:text>        text: /^[^%]+/,
  7828           <xsl:text>        text: /^[^%]+/,
  7751 </xsl:text>
  7829 </xsl:text>
  7752           <xsl:text>        modulo: /^%{2}/,
  7830           <xsl:text>        modulo: /^%{2}/,
  7753 </xsl:text>
  7831 </xsl:text>
  7754           <xsl:text>        placeholder: /^%(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,
  7832           <xsl:text>        placeholder: /^%(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxXD])/,
  7755 </xsl:text>
  7833 </xsl:text>
  7756           <xsl:text>        key: /^([a-z_][a-z_\d]*)/i,
  7834           <xsl:text>        key: /^([a-z_][a-z_\d]*)/i,
  7757 </xsl:text>
  7835 </xsl:text>
  7758           <xsl:text>        key_access: /^\.([a-z_][a-z_\d]*)/i,
  7836           <xsl:text>        key_access: /^\.([a-z_][a-z_\d]*)/i,
  7759 </xsl:text>
  7837 </xsl:text>
  7875 </xsl:text>
  7953 </xsl:text>
  7876           <xsl:text>                        arg = parseInt(arg, 10)
  7954           <xsl:text>                        arg = parseInt(arg, 10)
  7877 </xsl:text>
  7955 </xsl:text>
  7878           <xsl:text>                        break
  7956           <xsl:text>                        break
  7879 </xsl:text>
  7957 </xsl:text>
       
  7958           <xsl:text>                    case 'D':
       
  7959 </xsl:text>
       
  7960           <xsl:text>                        /*  
       
  7961 </xsl:text>
       
  7962           <xsl:text>
       
  7963 </xsl:text>
       
  7964           <xsl:text>                            select date format with width
       
  7965 </xsl:text>
       
  7966           <xsl:text>                            select time format with precision
       
  7967 </xsl:text>
       
  7968           <xsl:text>                            %D  =&gt; 13:31 AM (default)
       
  7969 </xsl:text>
       
  7970           <xsl:text>                            %1D  =&gt; 13:31 AM
       
  7971 </xsl:text>
       
  7972           <xsl:text>                            %.1D  =&gt; 07/07/20
       
  7973 </xsl:text>
       
  7974           <xsl:text>                            %1.1D  =&gt; 07/07/20, 13:31 AM
       
  7975 </xsl:text>
       
  7976           <xsl:text>                            %1.2D  =&gt; 07/07/20, 13:31:55 AM
       
  7977 </xsl:text>
       
  7978           <xsl:text>                            %2.2D  =&gt; May 5, 2022, 9:29:16 AM
       
  7979 </xsl:text>
       
  7980           <xsl:text>                            %3.3D  =&gt; May 5, 2022 at 9:28:16 AM GMT+2
       
  7981 </xsl:text>
       
  7982           <xsl:text>                            %4.4D  =&gt; Thursday, May 5, 2022 at 9:26:59 AM Central European Summer Time
       
  7983 </xsl:text>
       
  7984           <xsl:text>
       
  7985 </xsl:text>
       
  7986           <xsl:text>                            see meaning of DateTimeFormat's options "datestyle" and "timestyle" in MDN 
       
  7987 </xsl:text>
       
  7988           <xsl:text>                        */
       
  7989 </xsl:text>
       
  7990           <xsl:text>
       
  7991 </xsl:text>
       
  7992           <xsl:text>                        let [datestyle, timestyle] = [ph.width, ph.precision].map(val =&gt; ({
       
  7993 </xsl:text>
       
  7994           <xsl:text>                            1: "short",
       
  7995 </xsl:text>
       
  7996           <xsl:text>                            2: "medium",
       
  7997 </xsl:text>
       
  7998           <xsl:text>                            3: "long",
       
  7999 </xsl:text>
       
  8000           <xsl:text>                            4: "full"
       
  8001 </xsl:text>
       
  8002           <xsl:text>                        }[val]));
       
  8003 </xsl:text>
       
  8004           <xsl:text>
       
  8005 </xsl:text>
       
  8006           <xsl:text>                        if(timestyle === undefined &amp;&amp; datestyle === undefined){
       
  8007 </xsl:text>
       
  8008           <xsl:text>                            timestyle = "short";
       
  8009 </xsl:text>
       
  8010           <xsl:text>                        }
       
  8011 </xsl:text>
       
  8012           <xsl:text>
       
  8013 </xsl:text>
       
  8014           <xsl:text>                        let options = {
       
  8015 </xsl:text>
       
  8016           <xsl:text>                            dateStyle: datestyle,
       
  8017 </xsl:text>
       
  8018           <xsl:text>                            timeStyle: timestyle,
       
  8019 </xsl:text>
       
  8020           <xsl:text>                            hour12: false
       
  8021 </xsl:text>
       
  8022           <xsl:text>                        }
       
  8023 </xsl:text>
       
  8024           <xsl:text>
       
  8025 </xsl:text>
       
  8026           <xsl:text>                        /* get lang from globals */
       
  8027 </xsl:text>
       
  8028           <xsl:text>                        let lang = get_current_lang_code();
       
  8029 </xsl:text>
       
  8030           <xsl:text>                        arg = Date(arg).toLocaleString('en-US', options);
       
  8031 </xsl:text>
       
  8032           <xsl:text>                        
       
  8033 </xsl:text>
       
  8034           <xsl:text>                        /*    
       
  8035 </xsl:text>
       
  8036           <xsl:text>                            TODO: select with padding char
       
  8037 </xsl:text>
       
  8038           <xsl:text>                                  a: absolute time and date (default)
       
  8039 </xsl:text>
       
  8040           <xsl:text>                                  r: relative time
       
  8041 </xsl:text>
       
  8042           <xsl:text>                        */
       
  8043 </xsl:text>
       
  8044           <xsl:text>
       
  8045 </xsl:text>
       
  8046           <xsl:text>                        break
       
  8047 </xsl:text>
  7880           <xsl:text>                    case 'j':
  8048           <xsl:text>                    case 'j':
  7881 </xsl:text>
  8049 </xsl:text>
  7882           <xsl:text>                        arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0)
  8050           <xsl:text>                        arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0)
  7883 </xsl:text>
  8051 </xsl:text>
  7884           <xsl:text>                        break
  8052           <xsl:text>                        break
  8219 </xsl:text>
  8387 </xsl:text>
  8220           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
  8388           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
  8221 </xsl:text>
  8389 </xsl:text>
  8222           <xsl:text>        let widget = hmi_widgets[id];
  8390           <xsl:text>        let widget = hmi_widgets[id];
  8223 </xsl:text>
  8391 </xsl:text>
  8224           <xsl:text>        let init = widget.init;
  8392           <xsl:text>        widget.do_init();
  8225 </xsl:text>
  8393 </xsl:text>
  8226           <xsl:text>        if(typeof(init) == "function"){
  8394           <xsl:text>    });
  8227 </xsl:text>
  8395 </xsl:text>
  8228           <xsl:text>            try {
  8396           <xsl:text>};
  8229 </xsl:text>
  8397 </xsl:text>
  8230           <xsl:text>                init.call(widget);
  8398           <xsl:text>
  8231 </xsl:text>
  8399 </xsl:text>
  8232           <xsl:text>            } catch(err) {
  8400           <xsl:text>// Open WebSocket to relative "/ws" address
  8233 </xsl:text>
  8401 </xsl:text>
  8234           <xsl:text>                console.log(err);
  8402           <xsl:text>var has_watchdog = window.location.hash == "#watchdog";
       
  8403 </xsl:text>
       
  8404           <xsl:text>
       
  8405 </xsl:text>
       
  8406           <xsl:text>var ws_url = 
       
  8407 </xsl:text>
       
  8408           <xsl:text>    window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')
       
  8409 </xsl:text>
       
  8410           <xsl:text>    + '?mode=' + (has_watchdog ? "watchdog" : "multiclient");
       
  8411 </xsl:text>
       
  8412           <xsl:text>
       
  8413 </xsl:text>
       
  8414           <xsl:text>var ws = new WebSocket(ws_url);
       
  8415 </xsl:text>
       
  8416           <xsl:text>ws.binaryType = 'arraybuffer';
       
  8417 </xsl:text>
       
  8418           <xsl:text>
       
  8419 </xsl:text>
       
  8420           <xsl:text>const dvgetters = {
       
  8421 </xsl:text>
       
  8422           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  8423 </xsl:text>
       
  8424           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8425 </xsl:text>
       
  8426           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8427 </xsl:text>
       
  8428           <xsl:text>    REAL: (dv,offset) =&gt; [dv.getFloat32(offset, true), 4],
       
  8429 </xsl:text>
       
  8430           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  8431 </xsl:text>
       
  8432           <xsl:text>        const size = dv.getInt8(offset);
       
  8433 </xsl:text>
       
  8434           <xsl:text>        return [
       
  8435 </xsl:text>
       
  8436           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  8437 </xsl:text>
       
  8438           <xsl:text>                dv.buffer, /* original buffer */
       
  8439 </xsl:text>
       
  8440           <xsl:text>                offset + 1, /* string starts after size*/
       
  8441 </xsl:text>
       
  8442           <xsl:text>                size /* size of string */
       
  8443 </xsl:text>
       
  8444           <xsl:text>            )), size + 1]; /* total increment */
       
  8445 </xsl:text>
       
  8446           <xsl:text>    }
       
  8447 </xsl:text>
       
  8448           <xsl:text>};
       
  8449 </xsl:text>
       
  8450           <xsl:text>
       
  8451 </xsl:text>
       
  8452           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
       
  8453 </xsl:text>
       
  8454           <xsl:text>function apply_updates() {
       
  8455 </xsl:text>
       
  8456           <xsl:text>    updates.forEach((value, index) =&gt; {
       
  8457 </xsl:text>
       
  8458           <xsl:text>        dispatch_value(index, value);
       
  8459 </xsl:text>
       
  8460           <xsl:text>    });
       
  8461 </xsl:text>
       
  8462           <xsl:text>    updates.clear();
       
  8463 </xsl:text>
       
  8464           <xsl:text>}
       
  8465 </xsl:text>
       
  8466           <xsl:text>
       
  8467 </xsl:text>
       
  8468           <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
  8469 </xsl:text>
       
  8470           <xsl:text>var requestAnimationFrameID = null;
       
  8471 </xsl:text>
       
  8472           <xsl:text>function animate() {
       
  8473 </xsl:text>
       
  8474           <xsl:text>    // Do the page swith if any one pending
       
  8475 </xsl:text>
       
  8476           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  8477 </xsl:text>
       
  8478           <xsl:text>        switch_visible_page(current_subscribed_page);
       
  8479 </xsl:text>
       
  8480           <xsl:text>    }
       
  8481 </xsl:text>
       
  8482           <xsl:text>
       
  8483 </xsl:text>
       
  8484           <xsl:text>    while(widget = need_cache_apply.pop()){
       
  8485 </xsl:text>
       
  8486           <xsl:text>        widget.apply_cache();
       
  8487 </xsl:text>
       
  8488           <xsl:text>    }
       
  8489 </xsl:text>
       
  8490           <xsl:text>
       
  8491 </xsl:text>
       
  8492           <xsl:text>    if(jumps_need_update) update_jumps();
       
  8493 </xsl:text>
       
  8494           <xsl:text>
       
  8495 </xsl:text>
       
  8496           <xsl:text>    apply_updates();
       
  8497 </xsl:text>
       
  8498           <xsl:text>
       
  8499 </xsl:text>
       
  8500           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
       
  8501 </xsl:text>
       
  8502           <xsl:text>    pending_widget_animates = [];
       
  8503 </xsl:text>
       
  8504           <xsl:text>
       
  8505 </xsl:text>
       
  8506           <xsl:text>    requestAnimationFrameID = null;
       
  8507 </xsl:text>
       
  8508           <xsl:text>}
       
  8509 </xsl:text>
       
  8510           <xsl:text>
       
  8511 </xsl:text>
       
  8512           <xsl:text>function requestHMIAnimation() {
       
  8513 </xsl:text>
       
  8514           <xsl:text>    if(requestAnimationFrameID == null){
       
  8515 </xsl:text>
       
  8516           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  8517 </xsl:text>
       
  8518           <xsl:text>    }
       
  8519 </xsl:text>
       
  8520           <xsl:text>}
       
  8521 </xsl:text>
       
  8522           <xsl:text>
       
  8523 </xsl:text>
       
  8524           <xsl:text>// Message reception handler
       
  8525 </xsl:text>
       
  8526           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  8527 </xsl:text>
       
  8528           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  8529 </xsl:text>
       
  8530           <xsl:text>ws.onmessage = function (evt) {
       
  8531 </xsl:text>
       
  8532           <xsl:text>
       
  8533 </xsl:text>
       
  8534           <xsl:text>    let data = evt.data;
       
  8535 </xsl:text>
       
  8536           <xsl:text>    let dv = new DataView(data);
       
  8537 </xsl:text>
       
  8538           <xsl:text>    let i = 0;
       
  8539 </xsl:text>
       
  8540           <xsl:text>    try {
       
  8541 </xsl:text>
       
  8542           <xsl:text>        for(let hash_int of hmi_hash) {
       
  8543 </xsl:text>
       
  8544           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  8545 </xsl:text>
       
  8546           <xsl:text>                throw new Error("Hash doesn't match");
       
  8547 </xsl:text>
       
  8548           <xsl:text>            };
       
  8549 </xsl:text>
       
  8550           <xsl:text>            i++;
       
  8551 </xsl:text>
       
  8552           <xsl:text>        };
       
  8553 </xsl:text>
       
  8554           <xsl:text>
       
  8555 </xsl:text>
       
  8556           <xsl:text>        while(i &lt; data.byteLength){
       
  8557 </xsl:text>
       
  8558           <xsl:text>            let index = dv.getUint32(i, true);
       
  8559 </xsl:text>
       
  8560           <xsl:text>            i += 4;
       
  8561 </xsl:text>
       
  8562           <xsl:text>            let iectype = hmitree_types[index];
       
  8563 </xsl:text>
       
  8564           <xsl:text>            if(iectype != undefined){
       
  8565 </xsl:text>
       
  8566           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  8567 </xsl:text>
       
  8568           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  8569 </xsl:text>
       
  8570           <xsl:text>                updates.set(index, value);
       
  8571 </xsl:text>
       
  8572           <xsl:text>                i += bytesize;
       
  8573 </xsl:text>
       
  8574           <xsl:text>            } else {
       
  8575 </xsl:text>
       
  8576           <xsl:text>                throw new Error("Unknown index "+index);
  8235 </xsl:text>
  8577 </xsl:text>
  8236           <xsl:text>            }
  8578           <xsl:text>            }
  8237 </xsl:text>
  8579 </xsl:text>
       
  8580           <xsl:text>        };
       
  8581 </xsl:text>
       
  8582           <xsl:text>        // register for rendering on next frame, since there are updates
       
  8583 </xsl:text>
       
  8584           <xsl:text>        requestHMIAnimation();
       
  8585 </xsl:text>
       
  8586           <xsl:text>    } catch(err) {
       
  8587 </xsl:text>
       
  8588           <xsl:text>        // 1003 is for "Unsupported Data"
       
  8589 </xsl:text>
       
  8590           <xsl:text>        // ws.close(1003, err.message);
       
  8591 </xsl:text>
       
  8592           <xsl:text>
       
  8593 </xsl:text>
       
  8594           <xsl:text>        // TODO : remove debug alert ?
       
  8595 </xsl:text>
       
  8596           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  8597 </xsl:text>
       
  8598           <xsl:text>
       
  8599 </xsl:text>
       
  8600           <xsl:text>        // force reload ignoring cache
       
  8601 </xsl:text>
       
  8602           <xsl:text>        location.reload(true);
       
  8603 </xsl:text>
       
  8604           <xsl:text>    }
       
  8605 </xsl:text>
       
  8606           <xsl:text>};
       
  8607 </xsl:text>
       
  8608           <xsl:text>
       
  8609 </xsl:text>
       
  8610           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
       
  8611 </xsl:text>
       
  8612           <xsl:text>
       
  8613 </xsl:text>
       
  8614           <xsl:text>function send_blob(data) {
       
  8615 </xsl:text>
       
  8616           <xsl:text>    if(data.length &gt; 0) {
       
  8617 </xsl:text>
       
  8618           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
       
  8619 </xsl:text>
       
  8620           <xsl:text>    };
       
  8621 </xsl:text>
       
  8622           <xsl:text>};
       
  8623 </xsl:text>
       
  8624           <xsl:text>
       
  8625 </xsl:text>
       
  8626           <xsl:text>const typedarray_types = {
       
  8627 </xsl:text>
       
  8628           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  8629 </xsl:text>
       
  8630           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  8631 </xsl:text>
       
  8632           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  8633 </xsl:text>
       
  8634           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
  8635 </xsl:text>
       
  8636           <xsl:text>    STRING: (str) =&gt; {
       
  8637 </xsl:text>
       
  8638           <xsl:text>        // beremiz default string max size is 128
       
  8639 </xsl:text>
       
  8640           <xsl:text>        str = str.slice(0,128);
       
  8641 </xsl:text>
       
  8642           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  8643 </xsl:text>
       
  8644           <xsl:text>        binary[0] = str.length;
       
  8645 </xsl:text>
       
  8646           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
  8647 </xsl:text>
       
  8648           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  8649 </xsl:text>
  8238           <xsl:text>        }
  8650           <xsl:text>        }
  8239 </xsl:text>
  8651 </xsl:text>
  8240           <xsl:text>        if(widget.forced_frequency !== undefined)
  8652           <xsl:text>        return binary;
  8241 </xsl:text>
  8653 </xsl:text>
  8242           <xsl:text>            widget.frequency = widget.forced_frequency;
  8654           <xsl:text>    }
       
  8655 </xsl:text>
       
  8656           <xsl:text>    /* TODO */
       
  8657 </xsl:text>
       
  8658           <xsl:text>};
       
  8659 </xsl:text>
       
  8660           <xsl:text>
       
  8661 </xsl:text>
       
  8662           <xsl:text>function send_reset() {
       
  8663 </xsl:text>
       
  8664           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  8665 </xsl:text>
       
  8666           <xsl:text>};
       
  8667 </xsl:text>
       
  8668           <xsl:text>
       
  8669 </xsl:text>
       
  8670           <xsl:text>var subscriptions = [];
       
  8671 </xsl:text>
       
  8672           <xsl:text>
       
  8673 </xsl:text>
       
  8674           <xsl:text>function subscribers(index) {
       
  8675 </xsl:text>
       
  8676           <xsl:text>    let entry = subscriptions[index];
       
  8677 </xsl:text>
       
  8678           <xsl:text>    let res;
       
  8679 </xsl:text>
       
  8680           <xsl:text>    if(entry == undefined){
       
  8681 </xsl:text>
       
  8682           <xsl:text>        res = new Set();
       
  8683 </xsl:text>
       
  8684           <xsl:text>        subscriptions[index] = [res,0];
       
  8685 </xsl:text>
       
  8686           <xsl:text>    }else{
       
  8687 </xsl:text>
       
  8688           <xsl:text>        [res, _ign] = entry;
       
  8689 </xsl:text>
       
  8690           <xsl:text>    }
       
  8691 </xsl:text>
       
  8692           <xsl:text>    return res
       
  8693 </xsl:text>
       
  8694           <xsl:text>}
       
  8695 </xsl:text>
       
  8696           <xsl:text>
       
  8697 </xsl:text>
       
  8698           <xsl:text>function get_subscription_period(index) {
       
  8699 </xsl:text>
       
  8700           <xsl:text>    let entry = subscriptions[index];
       
  8701 </xsl:text>
       
  8702           <xsl:text>    if(entry == undefined)
       
  8703 </xsl:text>
       
  8704           <xsl:text>        return 0;
       
  8705 </xsl:text>
       
  8706           <xsl:text>    let [_ign, period] = entry;
       
  8707 </xsl:text>
       
  8708           <xsl:text>    return period;
       
  8709 </xsl:text>
       
  8710           <xsl:text>}
       
  8711 </xsl:text>
       
  8712           <xsl:text>
       
  8713 </xsl:text>
       
  8714           <xsl:text>function set_subscription_period(index, period) {
       
  8715 </xsl:text>
       
  8716           <xsl:text>    let entry = subscriptions[index];
       
  8717 </xsl:text>
       
  8718           <xsl:text>    if(entry == undefined){
       
  8719 </xsl:text>
       
  8720           <xsl:text>        subscriptions[index] = [new Set(), period];
       
  8721 </xsl:text>
       
  8722           <xsl:text>    } else {
       
  8723 </xsl:text>
       
  8724           <xsl:text>        entry[1] = period;
       
  8725 </xsl:text>
       
  8726           <xsl:text>    }
       
  8727 </xsl:text>
       
  8728           <xsl:text>}
       
  8729 </xsl:text>
       
  8730           <xsl:text>
       
  8731 </xsl:text>
       
  8732           <xsl:text>if(has_watchdog){
       
  8733 </xsl:text>
       
  8734           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  8735 </xsl:text>
       
  8736           <xsl:text>    // Since dispatch directly calls change_hmi_value,
       
  8737 </xsl:text>
       
  8738           <xsl:text>    // PLC will periodically send variable at given frequency
       
  8739 </xsl:text>
       
  8740           <xsl:text>    subscribers(heartbeat_index).add({
       
  8741 </xsl:text>
       
  8742           <xsl:text>        /* type: "Watchdog", */
       
  8743 </xsl:text>
       
  8744           <xsl:text>        frequency: 1,
       
  8745 </xsl:text>
       
  8746           <xsl:text>        indexes: [heartbeat_index],
       
  8747 </xsl:text>
       
  8748           <xsl:text>        new_hmi_value: function(index, value, oldval) {
       
  8749 </xsl:text>
       
  8750           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
       
  8751 </xsl:text>
       
  8752           <xsl:text>        }
  8243 </xsl:text>
  8753 </xsl:text>
  8244           <xsl:text>    });
  8754           <xsl:text>    });
  8245 </xsl:text>
  8755 </xsl:text>
  8246           <xsl:text>};
  8756           <xsl:text>}
  8247 </xsl:text>
  8757 </xsl:text>
  8248           <xsl:text>
  8758           <xsl:text>
  8249 </xsl:text>
  8759 </xsl:text>
  8250           <xsl:text>// Open WebSocket to relative "/ws" address
  8760           <xsl:text>// subscribe to per instance current page hmi variable
  8251 </xsl:text>
  8761 </xsl:text>
  8252           <xsl:text>var has_watchdog = window.location.hash == "#watchdog";
  8762           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
  8253 </xsl:text>
  8763 </xsl:text>
  8254           <xsl:text>
  8764           <xsl:text>subscribers(current_page_var_index).add({
  8255 </xsl:text>
  8765 </xsl:text>
  8256           <xsl:text>var ws_url = 
  8766           <xsl:text>    frequency: 1,
  8257 </xsl:text>
  8767 </xsl:text>
  8258           <xsl:text>    window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')
  8768           <xsl:text>    indexes: [current_page_var_index],
  8259 </xsl:text>
  8769 </xsl:text>
  8260           <xsl:text>    + '?mode=' + (has_watchdog ? "watchdog" : "multiclient");
  8770           <xsl:text>    new_hmi_value: function(index, value, oldval) {
  8261 </xsl:text>
  8771 </xsl:text>
  8262           <xsl:text>
  8772           <xsl:text>        if(value.startsWith("!"))
  8263 </xsl:text>
  8773 </xsl:text>
  8264           <xsl:text>var ws = new WebSocket(ws_url);
  8774           <xsl:text>            switch_page(value.slice(1));
  8265 </xsl:text>
       
  8266           <xsl:text>ws.binaryType = 'arraybuffer';
       
  8267 </xsl:text>
       
  8268           <xsl:text>
       
  8269 </xsl:text>
       
  8270           <xsl:text>const dvgetters = {
       
  8271 </xsl:text>
       
  8272           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  8273 </xsl:text>
       
  8274           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8275 </xsl:text>
       
  8276           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8277 </xsl:text>
       
  8278           <xsl:text>    REAL: (dv,offset) =&gt; [dv.getFloat32(offset, true), 4],
       
  8279 </xsl:text>
       
  8280           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  8281 </xsl:text>
       
  8282           <xsl:text>        const size = dv.getInt8(offset);
       
  8283 </xsl:text>
       
  8284           <xsl:text>        return [
       
  8285 </xsl:text>
       
  8286           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  8287 </xsl:text>
       
  8288           <xsl:text>                dv.buffer, /* original buffer */
       
  8289 </xsl:text>
       
  8290           <xsl:text>                offset + 1, /* string starts after size*/
       
  8291 </xsl:text>
       
  8292           <xsl:text>                size /* size of string */
       
  8293 </xsl:text>
       
  8294           <xsl:text>            )), size + 1]; /* total increment */
       
  8295 </xsl:text>
  8775 </xsl:text>
  8296           <xsl:text>    }
  8776           <xsl:text>    }
  8297 </xsl:text>
  8777 </xsl:text>
  8298           <xsl:text>};
  8778           <xsl:text>});
  8299 </xsl:text>
  8779 </xsl:text>
  8300           <xsl:text>
  8780           <xsl:text>
  8301 </xsl:text>
  8781 </xsl:text>
  8302           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
  8782           <xsl:text>function svg_text_to_multiline(elt) {
  8303 </xsl:text>
  8783 </xsl:text>
  8304           <xsl:text>function apply_updates() {
  8784           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
  8305 </xsl:text>
       
  8306           <xsl:text>    updates.forEach((value, index) =&gt; {
       
  8307 </xsl:text>
       
  8308           <xsl:text>        dispatch_value(index, value);
       
  8309 </xsl:text>
       
  8310           <xsl:text>    });
       
  8311 </xsl:text>
       
  8312           <xsl:text>    updates.clear();
       
  8313 </xsl:text>
  8785 </xsl:text>
  8314           <xsl:text>}
  8786           <xsl:text>}
  8315 </xsl:text>
  8787 </xsl:text>
  8316           <xsl:text>
  8788           <xsl:text>
  8317 </xsl:text>
  8789 </xsl:text>
  8318           <xsl:text>// Called on requestAnimationFrame, modifies DOM
  8790           <xsl:text>function multiline_to_svg_text(elt, str) {
  8319 </xsl:text>
  8791 </xsl:text>
  8320           <xsl:text>var requestAnimationFrameID = null;
  8792           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
  8321 </xsl:text>
  8793 </xsl:text>
  8322           <xsl:text>function animate() {
  8794           <xsl:text>}
  8323 </xsl:text>
  8795 </xsl:text>
  8324           <xsl:text>    // Do the page swith if any one pending
  8796           <xsl:text>
  8325 </xsl:text>
  8797 </xsl:text>
  8326           <xsl:text>    if(current_subscribed_page != current_visible_page){
  8798           <xsl:text>function switch_langnum(langnum) {
  8327 </xsl:text>
  8799 </xsl:text>
  8328           <xsl:text>        switch_visible_page(current_subscribed_page);
  8800           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
  8801 </xsl:text>
       
  8802           <xsl:text>
       
  8803 </xsl:text>
       
  8804           <xsl:text>    for (let translation of translations) {
       
  8805 </xsl:text>
       
  8806           <xsl:text>        let [objs, msgs] = translation;
       
  8807 </xsl:text>
       
  8808           <xsl:text>        let msg = msgs[langnum];
       
  8809 </xsl:text>
       
  8810           <xsl:text>        for (let obj of objs) {
       
  8811 </xsl:text>
       
  8812           <xsl:text>            multiline_to_svg_text(obj, msg);
       
  8813 </xsl:text>
       
  8814           <xsl:text>            obj.setAttribute("lang",langnum);
       
  8815 </xsl:text>
       
  8816           <xsl:text>        }
  8329 </xsl:text>
  8817 </xsl:text>
  8330           <xsl:text>    }
  8818           <xsl:text>    }
  8331 </xsl:text>
  8819 </xsl:text>
  8332           <xsl:text>
  8820           <xsl:text>    return langnum;
  8333 </xsl:text>
  8821 </xsl:text>
  8334           <xsl:text>    while(widget = need_cache_apply.pop()){
  8822           <xsl:text>}
  8335 </xsl:text>
  8823 </xsl:text>
  8336           <xsl:text>        widget.apply_cache();
  8824           <xsl:text>
       
  8825 </xsl:text>
       
  8826           <xsl:text>// backup original texts
       
  8827 </xsl:text>
       
  8828           <xsl:text>for (let translation of translations) {
       
  8829 </xsl:text>
       
  8830           <xsl:text>    let [objs, msgs] = translation;
       
  8831 </xsl:text>
       
  8832           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
  8833 </xsl:text>
       
  8834           <xsl:text>}
       
  8835 </xsl:text>
       
  8836           <xsl:text>
       
  8837 </xsl:text>
       
  8838           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
  8839 </xsl:text>
       
  8840           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
  8841 </xsl:text>
       
  8842           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
  8843 </xsl:text>
       
  8844           <xsl:text>subscribers(lang_local_index).add({
       
  8845 </xsl:text>
       
  8846           <xsl:text>    indexes: [lang_local_index],
       
  8847 </xsl:text>
       
  8848           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  8849 </xsl:text>
       
  8850           <xsl:text>        let current_lang =  switch_langnum(value);
       
  8851 </xsl:text>
       
  8852           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
  8853 </xsl:text>
       
  8854           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
  8855 </xsl:text>
       
  8856           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
  8857 </xsl:text>
       
  8858           <xsl:text>        switch_page();
  8337 </xsl:text>
  8859 </xsl:text>
  8338           <xsl:text>    }
  8860           <xsl:text>    }
  8339 </xsl:text>
  8861 </xsl:text>
  8340           <xsl:text>
  8862           <xsl:text>});
  8341 </xsl:text>
  8863 </xsl:text>
  8342           <xsl:text>    if(jumps_need_update) update_jumps();
  8864           <xsl:text>
  8343 </xsl:text>
  8865 </xsl:text>
  8344           <xsl:text>
  8866           <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language
  8345 </xsl:text>
  8867 </xsl:text>
  8346           <xsl:text>    apply_updates();
  8868           <xsl:text>function get_current_lang_code(){
  8347 </xsl:text>
  8869 </xsl:text>
  8348           <xsl:text>
  8870           <xsl:text>    return cache[langcode_local_index];
  8349 </xsl:text>
       
  8350           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
       
  8351 </xsl:text>
       
  8352           <xsl:text>    pending_widget_animates = [];
       
  8353 </xsl:text>
       
  8354           <xsl:text>
       
  8355 </xsl:text>
       
  8356           <xsl:text>    requestAnimationFrameID = null;
       
  8357 </xsl:text>
  8871 </xsl:text>
  8358           <xsl:text>}
  8872           <xsl:text>}
  8359 </xsl:text>
       
  8360           <xsl:text>
       
  8361 </xsl:text>
       
  8362           <xsl:text>function requestHMIAnimation() {
       
  8363 </xsl:text>
       
  8364           <xsl:text>    if(requestAnimationFrameID == null){
       
  8365 </xsl:text>
       
  8366           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  8367 </xsl:text>
       
  8368           <xsl:text>    }
       
  8369 </xsl:text>
       
  8370           <xsl:text>}
       
  8371 </xsl:text>
       
  8372           <xsl:text>
       
  8373 </xsl:text>
       
  8374           <xsl:text>// Message reception handler
       
  8375 </xsl:text>
       
  8376           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  8377 </xsl:text>
       
  8378           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  8379 </xsl:text>
       
  8380           <xsl:text>ws.onmessage = function (evt) {
       
  8381 </xsl:text>
       
  8382           <xsl:text>
       
  8383 </xsl:text>
       
  8384           <xsl:text>    let data = evt.data;
       
  8385 </xsl:text>
       
  8386           <xsl:text>    let dv = new DataView(data);
       
  8387 </xsl:text>
       
  8388           <xsl:text>    let i = 0;
       
  8389 </xsl:text>
       
  8390           <xsl:text>    try {
       
  8391 </xsl:text>
       
  8392           <xsl:text>        for(let hash_int of hmi_hash) {
       
  8393 </xsl:text>
       
  8394           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  8395 </xsl:text>
       
  8396           <xsl:text>                throw new Error("Hash doesn't match");
       
  8397 </xsl:text>
       
  8398           <xsl:text>            };
       
  8399 </xsl:text>
       
  8400           <xsl:text>            i++;
       
  8401 </xsl:text>
       
  8402           <xsl:text>        };
       
  8403 </xsl:text>
       
  8404           <xsl:text>
       
  8405 </xsl:text>
       
  8406           <xsl:text>        while(i &lt; data.byteLength){
       
  8407 </xsl:text>
       
  8408           <xsl:text>            let index = dv.getUint32(i, true);
       
  8409 </xsl:text>
       
  8410           <xsl:text>            i += 4;
       
  8411 </xsl:text>
       
  8412           <xsl:text>            let iectype = hmitree_types[index];
       
  8413 </xsl:text>
       
  8414           <xsl:text>            if(iectype != undefined){
       
  8415 </xsl:text>
       
  8416           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  8417 </xsl:text>
       
  8418           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  8419 </xsl:text>
       
  8420           <xsl:text>                updates.set(index, value);
       
  8421 </xsl:text>
       
  8422           <xsl:text>                i += bytesize;
       
  8423 </xsl:text>
       
  8424           <xsl:text>            } else {
       
  8425 </xsl:text>
       
  8426           <xsl:text>                throw new Error("Unknown index "+index);
       
  8427 </xsl:text>
       
  8428           <xsl:text>            }
       
  8429 </xsl:text>
       
  8430           <xsl:text>        };
       
  8431 </xsl:text>
       
  8432           <xsl:text>        // register for rendering on next frame, since there are updates
       
  8433 </xsl:text>
       
  8434           <xsl:text>        requestHMIAnimation();
       
  8435 </xsl:text>
       
  8436           <xsl:text>    } catch(err) {
       
  8437 </xsl:text>
       
  8438           <xsl:text>        // 1003 is for "Unsupported Data"
       
  8439 </xsl:text>
       
  8440           <xsl:text>        // ws.close(1003, err.message);
       
  8441 </xsl:text>
       
  8442           <xsl:text>
       
  8443 </xsl:text>
       
  8444           <xsl:text>        // TODO : remove debug alert ?
       
  8445 </xsl:text>
       
  8446           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  8447 </xsl:text>
       
  8448           <xsl:text>
       
  8449 </xsl:text>
       
  8450           <xsl:text>        // force reload ignoring cache
       
  8451 </xsl:text>
       
  8452           <xsl:text>        location.reload(true);
       
  8453 </xsl:text>
       
  8454           <xsl:text>    }
       
  8455 </xsl:text>
       
  8456           <xsl:text>};
       
  8457 </xsl:text>
       
  8458           <xsl:text>
       
  8459 </xsl:text>
       
  8460           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
       
  8461 </xsl:text>
       
  8462           <xsl:text>
       
  8463 </xsl:text>
       
  8464           <xsl:text>function send_blob(data) {
       
  8465 </xsl:text>
       
  8466           <xsl:text>    if(data.length &gt; 0) {
       
  8467 </xsl:text>
       
  8468           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
       
  8469 </xsl:text>
       
  8470           <xsl:text>    };
       
  8471 </xsl:text>
       
  8472           <xsl:text>};
       
  8473 </xsl:text>
       
  8474           <xsl:text>
       
  8475 </xsl:text>
       
  8476           <xsl:text>const typedarray_types = {
       
  8477 </xsl:text>
       
  8478           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  8479 </xsl:text>
       
  8480           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  8481 </xsl:text>
       
  8482           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  8483 </xsl:text>
       
  8484           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
  8485 </xsl:text>
       
  8486           <xsl:text>    STRING: (str) =&gt; {
       
  8487 </xsl:text>
       
  8488           <xsl:text>        // beremiz default string max size is 128
       
  8489 </xsl:text>
       
  8490           <xsl:text>        str = str.slice(0,128);
       
  8491 </xsl:text>
       
  8492           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  8493 </xsl:text>
       
  8494           <xsl:text>        binary[0] = str.length;
       
  8495 </xsl:text>
       
  8496           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
  8497 </xsl:text>
       
  8498           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  8499 </xsl:text>
       
  8500           <xsl:text>        }
       
  8501 </xsl:text>
       
  8502           <xsl:text>        return binary;
       
  8503 </xsl:text>
       
  8504           <xsl:text>    }
       
  8505 </xsl:text>
       
  8506           <xsl:text>    /* TODO */
       
  8507 </xsl:text>
       
  8508           <xsl:text>};
       
  8509 </xsl:text>
       
  8510           <xsl:text>
       
  8511 </xsl:text>
       
  8512           <xsl:text>function send_reset() {
       
  8513 </xsl:text>
       
  8514           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  8515 </xsl:text>
       
  8516           <xsl:text>};
       
  8517 </xsl:text>
       
  8518           <xsl:text>
       
  8519 </xsl:text>
       
  8520           <xsl:text>var subscriptions = [];
       
  8521 </xsl:text>
       
  8522           <xsl:text>
       
  8523 </xsl:text>
       
  8524           <xsl:text>function subscribers(index) {
       
  8525 </xsl:text>
       
  8526           <xsl:text>    let entry = subscriptions[index];
       
  8527 </xsl:text>
       
  8528           <xsl:text>    let res;
       
  8529 </xsl:text>
       
  8530           <xsl:text>    if(entry == undefined){
       
  8531 </xsl:text>
       
  8532           <xsl:text>        res = new Set();
       
  8533 </xsl:text>
       
  8534           <xsl:text>        subscriptions[index] = [res,0];
       
  8535 </xsl:text>
       
  8536           <xsl:text>    }else{
       
  8537 </xsl:text>
       
  8538           <xsl:text>        [res, _ign] = entry;
       
  8539 </xsl:text>
       
  8540           <xsl:text>    }
       
  8541 </xsl:text>
       
  8542           <xsl:text>    return res
       
  8543 </xsl:text>
       
  8544           <xsl:text>}
       
  8545 </xsl:text>
       
  8546           <xsl:text>
       
  8547 </xsl:text>
       
  8548           <xsl:text>function get_subscription_period(index) {
       
  8549 </xsl:text>
       
  8550           <xsl:text>    let entry = subscriptions[index];
       
  8551 </xsl:text>
       
  8552           <xsl:text>    if(entry == undefined)
       
  8553 </xsl:text>
       
  8554           <xsl:text>        return 0;
       
  8555 </xsl:text>
       
  8556           <xsl:text>    let [_ign, period] = entry;
       
  8557 </xsl:text>
       
  8558           <xsl:text>    return period;
       
  8559 </xsl:text>
       
  8560           <xsl:text>}
       
  8561 </xsl:text>
       
  8562           <xsl:text>
       
  8563 </xsl:text>
       
  8564           <xsl:text>function set_subscription_period(index, period) {
       
  8565 </xsl:text>
       
  8566           <xsl:text>    let entry = subscriptions[index];
       
  8567 </xsl:text>
       
  8568           <xsl:text>    if(entry == undefined){
       
  8569 </xsl:text>
       
  8570           <xsl:text>        subscriptions[index] = [new Set(), period];
       
  8571 </xsl:text>
       
  8572           <xsl:text>    } else {
       
  8573 </xsl:text>
       
  8574           <xsl:text>        entry[1] = period;
       
  8575 </xsl:text>
       
  8576           <xsl:text>    }
       
  8577 </xsl:text>
       
  8578           <xsl:text>}
       
  8579 </xsl:text>
       
  8580           <xsl:text>
       
  8581 </xsl:text>
       
  8582           <xsl:text>if(has_watchdog){
       
  8583 </xsl:text>
       
  8584           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  8585 </xsl:text>
       
  8586           <xsl:text>    // Since dispatch directly calls change_hmi_value,
       
  8587 </xsl:text>
       
  8588           <xsl:text>    // PLC will periodically send variable at given frequency
       
  8589 </xsl:text>
       
  8590           <xsl:text>    subscribers(heartbeat_index).add({
       
  8591 </xsl:text>
       
  8592           <xsl:text>        /* type: "Watchdog", */
       
  8593 </xsl:text>
       
  8594           <xsl:text>        frequency: 1,
       
  8595 </xsl:text>
       
  8596           <xsl:text>        indexes: [heartbeat_index],
       
  8597 </xsl:text>
       
  8598           <xsl:text>        new_hmi_value: function(index, value, oldval) {
       
  8599 </xsl:text>
       
  8600           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
       
  8601 </xsl:text>
       
  8602           <xsl:text>        }
       
  8603 </xsl:text>
       
  8604           <xsl:text>    });
       
  8605 </xsl:text>
       
  8606           <xsl:text>}
       
  8607 </xsl:text>
       
  8608           <xsl:text>
       
  8609 </xsl:text>
       
  8610           <xsl:text>// subscribe to per instance current page hmi variable
       
  8611 </xsl:text>
       
  8612           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
       
  8613 </xsl:text>
       
  8614           <xsl:text>subscribers(current_page_var_index).add({
       
  8615 </xsl:text>
       
  8616           <xsl:text>    frequency: 1,
       
  8617 </xsl:text>
       
  8618           <xsl:text>    indexes: [current_page_var_index],
       
  8619 </xsl:text>
       
  8620           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  8621 </xsl:text>
       
  8622           <xsl:text>        if(value.startsWith("!"))
       
  8623 </xsl:text>
       
  8624           <xsl:text>            switch_page(value.slice(1));
       
  8625 </xsl:text>
       
  8626           <xsl:text>    }
       
  8627 </xsl:text>
       
  8628           <xsl:text>});
       
  8629 </xsl:text>
       
  8630           <xsl:text>
       
  8631 </xsl:text>
       
  8632           <xsl:text>function svg_text_to_multiline(elt) {
       
  8633 </xsl:text>
       
  8634           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
       
  8635 </xsl:text>
       
  8636           <xsl:text>}
       
  8637 </xsl:text>
       
  8638           <xsl:text>
       
  8639 </xsl:text>
       
  8640           <xsl:text>function multiline_to_svg_text(elt, str) {
       
  8641 </xsl:text>
       
  8642           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
       
  8643 </xsl:text>
       
  8644           <xsl:text>}
       
  8645 </xsl:text>
       
  8646           <xsl:text>
       
  8647 </xsl:text>
       
  8648           <xsl:text>function switch_langnum(langnum) {
       
  8649 </xsl:text>
       
  8650           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
  8651 </xsl:text>
       
  8652           <xsl:text>
       
  8653 </xsl:text>
       
  8654           <xsl:text>    for (let translation of translations) {
       
  8655 </xsl:text>
       
  8656           <xsl:text>        let [objs, msgs] = translation;
       
  8657 </xsl:text>
       
  8658           <xsl:text>        let msg = msgs[langnum];
       
  8659 </xsl:text>
       
  8660           <xsl:text>        for (let obj of objs) {
       
  8661 </xsl:text>
       
  8662           <xsl:text>            multiline_to_svg_text(obj, msg);
       
  8663 </xsl:text>
       
  8664           <xsl:text>            obj.setAttribute("lang",langnum);
       
  8665 </xsl:text>
       
  8666           <xsl:text>        }
       
  8667 </xsl:text>
       
  8668           <xsl:text>    }
       
  8669 </xsl:text>
       
  8670           <xsl:text>    return langnum;
       
  8671 </xsl:text>
       
  8672           <xsl:text>}
       
  8673 </xsl:text>
       
  8674           <xsl:text>
       
  8675 </xsl:text>
       
  8676           <xsl:text>// backup original texts
       
  8677 </xsl:text>
       
  8678           <xsl:text>for (let translation of translations) {
       
  8679 </xsl:text>
       
  8680           <xsl:text>    let [objs, msgs] = translation;
       
  8681 </xsl:text>
       
  8682           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
  8683 </xsl:text>
       
  8684           <xsl:text>}
       
  8685 </xsl:text>
       
  8686           <xsl:text>
       
  8687 </xsl:text>
       
  8688           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
  8689 </xsl:text>
       
  8690           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
  8691 </xsl:text>
       
  8692           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
  8693 </xsl:text>
       
  8694           <xsl:text>subscribers(lang_local_index).add({
       
  8695 </xsl:text>
       
  8696           <xsl:text>    indexes: [lang_local_index],
       
  8697 </xsl:text>
       
  8698           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  8699 </xsl:text>
       
  8700           <xsl:text>        let current_lang =  switch_langnum(value);
       
  8701 </xsl:text>
       
  8702           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
  8703 </xsl:text>
       
  8704           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
  8705 </xsl:text>
       
  8706           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
  8707 </xsl:text>
       
  8708           <xsl:text>        switch_page();
       
  8709 </xsl:text>
       
  8710           <xsl:text>    }
       
  8711 </xsl:text>
       
  8712           <xsl:text>});
       
  8713 </xsl:text>
  8873 </xsl:text>
  8714           <xsl:text>
  8874           <xsl:text>
  8715 </xsl:text>
  8875 </xsl:text>
  8716           <xsl:text>function setup_lang(){
  8876           <xsl:text>function setup_lang(){
  8717 </xsl:text>
  8877 </xsl:text>