svghmi/gen_index_xhtml.xslt
changeset 3558 f5850ce25caf
parent 3557 59158e360b8c
child 3582 7dcd0de97e6f
equal deleted inserted replaced
3552:ffe4e46a3163 3558:f5850ce25caf
  2057     <xsl:value-of select="@type"/>
  2057     <xsl:value-of select="@type"/>
  2058     <xsl:text> widget didn't provide any */
  2058     <xsl:text> widget didn't provide any */
  2059 </xsl:text>
  2059 </xsl:text>
  2060     <xsl:text>}
  2060     <xsl:text>}
  2061 </xsl:text>
  2061 </xsl:text>
       
  2062     <xsl:message terminate="no">
       
  2063       <xsl:value-of select="@type"/>
       
  2064       <xsl:text> widget is used in SVG but widget type is not declared</xsl:text>
       
  2065     </xsl:message>
  2062   </xsl:template>
  2066   </xsl:template>
  2063   <xsl:variable name="included_ids" select="$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id"/>
  2067   <xsl:variable name="included_ids" select="$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id"/>
  2064   <xsl:variable name="hmi_widgets" select="$hmi_elements[@id = $included_ids]"/>
  2068   <xsl:variable name="hmi_widgets" select="$hmi_elements[@id = $included_ids]"/>
  2065   <xsl:variable name="result_widgets" select="$result_svg_ns//*[@id = $hmi_widgets/@id]"/>
  2069   <xsl:variable name="result_widgets" select="$result_svg_ns//*[@id = $hmi_widgets/@id]"/>
  2066   <declarations:hmi-elements/>
  2070   <declarations:hmi-elements/>
  2075 </xsl:text>
  2079 </xsl:text>
  2076     <xsl:text>var hmi_widgets = {
  2080     <xsl:text>var hmi_widgets = {
  2077 </xsl:text>
  2081 </xsl:text>
  2078     <xsl:apply-templates mode="hmi_widgets" select="$hmi_widgets"/>
  2082     <xsl:apply-templates mode="hmi_widgets" select="$hmi_widgets"/>
  2079     <xsl:text>}
  2083     <xsl:text>}
       
  2084 </xsl:text>
       
  2085     <xsl:text>
  2080 </xsl:text>
  2086 </xsl:text>
  2081     <xsl:text>
  2087     <xsl:text>
  2082 </xsl:text>
  2088 </xsl:text>
  2083   </xsl:template>
  2089   </xsl:template>
  2084   <xsl:template name="defs_by_labels">
  2090   <xsl:template name="defs_by_labels">
  4878         <xsl:with-param name="mandatory" select="'no'"/>
  4884         <xsl:with-param name="mandatory" select="'no'"/>
  4879       </xsl:call-template>
  4885       </xsl:call-template>
  4880     </xsl:variable>
  4886     </xsl:variable>
  4881     <xsl:variable name="have_edit" select="string-length($edit_elt)&gt;0"/>
  4887     <xsl:variable name="have_edit" select="string-length($edit_elt)&gt;0"/>
  4882     <xsl:value-of select="$edit_elt"/>
  4888     <xsl:value-of select="$edit_elt"/>
       
  4889     <xsl:variable name="action_elements" select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]"/>
  4883     <xsl:if test="$have_value">
  4890     <xsl:if test="$have_value">
  4884       <xsl:text>    frequency: 5,
  4891       <xsl:text>    frequency: 5,
  4885 </xsl:text>
  4892 </xsl:text>
  4886     </xsl:if>
  4893     </xsl:if>
  4887     <xsl:text>    dispatch: function(value) {
  4894     <xsl:text>    dispatch: function(value) {
  4918       <xsl:text>        this.value_elt.textContent = String(this.display);
  4925       <xsl:text>        this.value_elt.textContent = String(this.display);
  4919 </xsl:text>
  4926 </xsl:text>
  4920       <xsl:text>    },
  4927       <xsl:text>    },
  4921 </xsl:text>
  4928 </xsl:text>
  4922     </xsl:if>
  4929     </xsl:if>
       
  4930     <xsl:for-each select="$action_elements">
       
  4931       <xsl:text>    action_elt_</xsl:text>
       
  4932       <xsl:value-of select="position()"/>
       
  4933       <xsl:text>: id("</xsl:text>
       
  4934       <xsl:value-of select="@id"/>
       
  4935       <xsl:text>"),
       
  4936 </xsl:text>
       
  4937     </xsl:for-each>
  4923     <xsl:text>    init: function() {
  4938     <xsl:text>    init: function() {
  4924 </xsl:text>
  4939 </xsl:text>
  4925     <xsl:if test="$have_edit">
  4940     <xsl:if test="$have_edit">
  4926       <xsl:text>        this.edit_elt.onclick = () =&gt; edit_value("</xsl:text>
  4941       <xsl:text>        this.edit_elt.onclick = () =&gt; edit_value("</xsl:text>
  4927       <xsl:value-of select="path/@value"/>
  4942       <xsl:value-of select="path/@value"/>
  4934 </xsl:text>
  4949 </xsl:text>
  4935       </xsl:if>
  4950       </xsl:if>
  4936       <xsl:text>        this.animate();
  4951       <xsl:text>        this.animate();
  4937 </xsl:text>
  4952 </xsl:text>
  4938     </xsl:if>
  4953     </xsl:if>
  4939     <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
  4954     <xsl:for-each select="$action_elements">
  4940       <xsl:text>        id("</xsl:text>
  4955       <xsl:text>        this.action_elt_</xsl:text>
  4941       <xsl:value-of select="@id"/>
  4956       <xsl:value-of select="position()"/>
  4942       <xsl:text>").onclick = () =&gt; this.on_op_click("</xsl:text>
  4957       <xsl:text>.onclick = () =&gt; this.on_op_click("</xsl:text>
  4943       <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
  4958       <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
  4944       <xsl:text>");
  4959       <xsl:text>");
  4945 </xsl:text>
  4960 </xsl:text>
  4946     </xsl:for-each>
  4961     </xsl:for-each>
  4947     <xsl:if test="$have_value">
  4962     <xsl:if test="$have_value">
  5694 </xsl:text>
  5709 </xsl:text>
  5695     <xsl:text>
  5710     <xsl:text>
  5696 </xsl:text>
  5711 </xsl:text>
  5697     <xsl:text>.fade-out-page {
  5712     <xsl:text>.fade-out-page {
  5698 </xsl:text>
  5713 </xsl:text>
  5699     <xsl:text>    animation: fadeOut 0.6s both;
  5714     <xsl:text>    animation: cubic-bezier(0, 0.8, 0.6, 1) fadeOut 0.6s both;
  5700 </xsl:text>
  5715 </xsl:text>
  5701     <xsl:text>}
  5716     <xsl:text>}
  5702 </xsl:text>
  5717 </xsl:text>
  5703     <xsl:text>
  5718     <xsl:text>
  5704 </xsl:text>
  5719 </xsl:text>
  6103 </xsl:text>
  6118 </xsl:text>
  6104     </xsl:for-each>
  6119     </xsl:for-each>
  6105     <xsl:text>    },
  6120     <xsl:text>    },
  6106 </xsl:text>
  6121 </xsl:text>
  6107   </xsl:template>
  6122   </xsl:template>
       
  6123   <xsl:template match="widget[@type='List']" mode="widget_class">
       
  6124     <xsl:text>class </xsl:text>
       
  6125     <xsl:text>ListWidget</xsl:text>
       
  6126     <xsl:text> extends Widget{
       
  6127 </xsl:text>
       
  6128     <xsl:text>}
       
  6129 </xsl:text>
       
  6130   </xsl:template>
  6108   <xsl:template match="widget[@type='ListSwitch']" mode="widget_desc">
  6131   <xsl:template match="widget[@type='ListSwitch']" mode="widget_desc">
  6109     <type>
  6132     <type>
  6110       <xsl:value-of select="@type"/>
  6133       <xsl:value-of select="@type"/>
  6111     </type>
  6134     </type>
  6112     <longdesc>
  6135     <longdesc>
  7865 </xsl:text>
  7888 </xsl:text>
  7866     </xsl:for-each>
  7889     </xsl:for-each>
  7867     <xsl:text>    ].reverse(),
  7890     <xsl:text>    ].reverse(),
  7868 </xsl:text>
  7891 </xsl:text>
  7869   </xsl:template>
  7892   </xsl:template>
       
  7893   <xsl:template match="widget[@type='TextList']" mode="widget_class">
       
  7894     <xsl:text>class </xsl:text>
       
  7895     <xsl:text>TextListWidget</xsl:text>
       
  7896     <xsl:text> extends Widget{
       
  7897 </xsl:text>
       
  7898     <xsl:text>}
       
  7899 </xsl:text>
       
  7900   </xsl:template>
  7870   <xsl:template match="widget[@type='TextStyleList']" mode="widget_desc">
  7901   <xsl:template match="widget[@type='TextStyleList']" mode="widget_desc">
  7871     <type>
  7902     <type>
  7872       <xsl:value-of select="@type"/>
  7903       <xsl:value-of select="@type"/>
  7873     </type>
  7904     </type>
  7874     <longdesc>
  7905     <longdesc>
  7904       <xsl:value-of select="$style"/>
  7935       <xsl:value-of select="$style"/>
  7905       <xsl:text>",
  7936       <xsl:text>",
  7906 </xsl:text>
  7937 </xsl:text>
  7907     </xsl:for-each>
  7938     </xsl:for-each>
  7908     <xsl:text>    },
  7939     <xsl:text>    },
       
  7940 </xsl:text>
       
  7941   </xsl:template>
       
  7942   <xsl:template match="widget[@type='TextStyleList']" mode="widget_class">
       
  7943     <xsl:text>class </xsl:text>
       
  7944     <xsl:text>TextStyleListWidget</xsl:text>
       
  7945     <xsl:text> extends Widget{
       
  7946 </xsl:text>
       
  7947     <xsl:text>}
  7909 </xsl:text>
  7948 </xsl:text>
  7910   </xsl:template>
  7949   </xsl:template>
  7911   <xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
  7950   <xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
  7912     <type>
  7951     <type>
  7913       <xsl:value-of select="@type"/>
  7952       <xsl:value-of select="@type"/>
 10497 </xsl:text>
 10536 </xsl:text>
 10498           <xsl:text>var requestAnimationFrameID = null;
 10537           <xsl:text>var requestAnimationFrameID = null;
 10499 </xsl:text>
 10538 </xsl:text>
 10500           <xsl:text>function animate() {
 10539           <xsl:text>function animate() {
 10501 </xsl:text>
 10540 </xsl:text>
 10502           <xsl:text>    // Do the page swith if any one pending
 10541           <xsl:text>    let rearm = true;
 10503 </xsl:text>
 10542 </xsl:text>
 10504           <xsl:text>    if(current_subscribed_page != current_visible_page){
 10543           <xsl:text>    do{
 10505 </xsl:text>
 10544 </xsl:text>
 10506           <xsl:text>        switch_visible_page(current_subscribed_page);
 10545           <xsl:text>        if(page_fading == "pending" || page_fading == "forced"){
       
 10546 </xsl:text>
       
 10547           <xsl:text>            if(page_fading == "pending")
       
 10548 </xsl:text>
       
 10549           <xsl:text>                svg_root.classList.add("fade-out-page");
       
 10550 </xsl:text>
       
 10551           <xsl:text>            page_fading = "in_progress";
       
 10552 </xsl:text>
       
 10553           <xsl:text>            if(page_fading_args.length)
       
 10554 </xsl:text>
       
 10555           <xsl:text>                setTimeout(function(){
       
 10556 </xsl:text>
       
 10557           <xsl:text>                    switch_page(...page_fading_args);
       
 10558 </xsl:text>
       
 10559           <xsl:text>                },1);
       
 10560 </xsl:text>
       
 10561           <xsl:text>            break;
       
 10562 </xsl:text>
       
 10563           <xsl:text>        }
       
 10564 </xsl:text>
       
 10565           <xsl:text>
       
 10566 </xsl:text>
       
 10567           <xsl:text>        // Do the page swith if pending
       
 10568 </xsl:text>
       
 10569           <xsl:text>        if(page_switch_in_progress){
       
 10570 </xsl:text>
       
 10571           <xsl:text>            if(current_subscribed_page != current_visible_page){
       
 10572 </xsl:text>
       
 10573           <xsl:text>                switch_visible_page(current_subscribed_page);
       
 10574 </xsl:text>
       
 10575           <xsl:text>            }
       
 10576 </xsl:text>
       
 10577           <xsl:text>
       
 10578 </xsl:text>
       
 10579           <xsl:text>            page_switch_in_progress = false;
       
 10580 </xsl:text>
       
 10581           <xsl:text>
       
 10582 </xsl:text>
       
 10583           <xsl:text>            if(page_fading == "in_progress"){
       
 10584 </xsl:text>
       
 10585           <xsl:text>                svg_root.classList.remove("fade-out-page");
       
 10586 </xsl:text>
       
 10587           <xsl:text>                page_fading = "off";
       
 10588 </xsl:text>
       
 10589           <xsl:text>            }
       
 10590 </xsl:text>
       
 10591           <xsl:text>        }
       
 10592 </xsl:text>
       
 10593           <xsl:text>
       
 10594 </xsl:text>
       
 10595           <xsl:text>        while(widget = need_cache_apply.pop()){
       
 10596 </xsl:text>
       
 10597           <xsl:text>            widget.apply_cache();
       
 10598 </xsl:text>
       
 10599           <xsl:text>        }
       
 10600 </xsl:text>
       
 10601           <xsl:text>
       
 10602 </xsl:text>
       
 10603           <xsl:text>        if(jumps_need_update) update_jumps();
       
 10604 </xsl:text>
       
 10605           <xsl:text>
       
 10606 </xsl:text>
       
 10607           <xsl:text>        apply_updates();
       
 10608 </xsl:text>
       
 10609           <xsl:text>
       
 10610 </xsl:text>
       
 10611           <xsl:text>        pending_widget_animates.forEach(widget =&gt; widget._animate());
       
 10612 </xsl:text>
       
 10613           <xsl:text>        pending_widget_animates = [];
       
 10614 </xsl:text>
       
 10615           <xsl:text>        rearm = false;
       
 10616 </xsl:text>
       
 10617           <xsl:text>    } while(0);
       
 10618 </xsl:text>
       
 10619           <xsl:text>
       
 10620 </xsl:text>
       
 10621           <xsl:text>    requestAnimationFrameID = null;
       
 10622 </xsl:text>
       
 10623           <xsl:text>
       
 10624 </xsl:text>
       
 10625           <xsl:text>    if(rearm) requestHMIAnimation();
       
 10626 </xsl:text>
       
 10627           <xsl:text>}
       
 10628 </xsl:text>
       
 10629           <xsl:text>
       
 10630 </xsl:text>
       
 10631           <xsl:text>function requestHMIAnimation() {
       
 10632 </xsl:text>
       
 10633           <xsl:text>    if(requestAnimationFrameID == null){
       
 10634 </xsl:text>
       
 10635           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
 10507 </xsl:text>
 10636 </xsl:text>
 10508           <xsl:text>    }
 10637           <xsl:text>    }
 10509 </xsl:text>
 10638 </xsl:text>
 10510           <xsl:text>
 10639           <xsl:text>}
 10511 </xsl:text>
 10640 </xsl:text>
 10512           <xsl:text>    while(widget = need_cache_apply.pop()){
 10641           <xsl:text>
 10513 </xsl:text>
 10642 </xsl:text>
 10514           <xsl:text>        widget.apply_cache();
 10643           <xsl:text>// Message reception handler
       
 10644 </xsl:text>
       
 10645           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
 10646 </xsl:text>
       
 10647           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
 10648 </xsl:text>
       
 10649           <xsl:text>ws.onmessage = function (evt) {
       
 10650 </xsl:text>
       
 10651           <xsl:text>
       
 10652 </xsl:text>
       
 10653           <xsl:text>    let data = evt.data;
       
 10654 </xsl:text>
       
 10655           <xsl:text>    let dv = new DataView(data);
       
 10656 </xsl:text>
       
 10657           <xsl:text>    let i = 0;
       
 10658 </xsl:text>
       
 10659           <xsl:text>    try {
       
 10660 </xsl:text>
       
 10661           <xsl:text>        for(let hash_int of hmi_hash) {
       
 10662 </xsl:text>
       
 10663           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
 10664 </xsl:text>
       
 10665           <xsl:text>                throw new Error("Hash doesn't match");
       
 10666 </xsl:text>
       
 10667           <xsl:text>            };
       
 10668 </xsl:text>
       
 10669           <xsl:text>            i++;
       
 10670 </xsl:text>
       
 10671           <xsl:text>        };
       
 10672 </xsl:text>
       
 10673           <xsl:text>
       
 10674 </xsl:text>
       
 10675           <xsl:text>        while(i &lt; data.byteLength){
       
 10676 </xsl:text>
       
 10677           <xsl:text>            let index = dv.getUint32(i, true);
       
 10678 </xsl:text>
       
 10679           <xsl:text>            i += 4;
       
 10680 </xsl:text>
       
 10681           <xsl:text>            let iectype = hmitree_types[index];
       
 10682 </xsl:text>
       
 10683           <xsl:text>            if(iectype != undefined){
       
 10684 </xsl:text>
       
 10685           <xsl:text>                let dvgetter = dvgetters[iectype];
       
 10686 </xsl:text>
       
 10687           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
 10688 </xsl:text>
       
 10689           <xsl:text>                updates.set(index, value);
       
 10690 </xsl:text>
       
 10691           <xsl:text>                i += bytesize;
       
 10692 </xsl:text>
       
 10693           <xsl:text>            } else {
       
 10694 </xsl:text>
       
 10695           <xsl:text>                throw new Error("Unknown index "+index);
       
 10696 </xsl:text>
       
 10697           <xsl:text>            }
       
 10698 </xsl:text>
       
 10699           <xsl:text>        };
       
 10700 </xsl:text>
       
 10701           <xsl:text>        // register for rendering on next frame, since there are updates
       
 10702 </xsl:text>
       
 10703           <xsl:text>        requestHMIAnimation();
       
 10704 </xsl:text>
       
 10705           <xsl:text>    } catch(err) {
       
 10706 </xsl:text>
       
 10707           <xsl:text>        // 1003 is for "Unsupported Data"
       
 10708 </xsl:text>
       
 10709           <xsl:text>        // ws.close(1003, err.message);
       
 10710 </xsl:text>
       
 10711           <xsl:text>
       
 10712 </xsl:text>
       
 10713           <xsl:text>        // TODO : remove debug alert ?
       
 10714 </xsl:text>
       
 10715           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
 10716 </xsl:text>
       
 10717           <xsl:text>
       
 10718 </xsl:text>
       
 10719           <xsl:text>        // force reload ignoring cache
       
 10720 </xsl:text>
       
 10721           <xsl:text>        location.reload(true);
 10515 </xsl:text>
 10722 </xsl:text>
 10516           <xsl:text>    }
 10723           <xsl:text>    }
 10517 </xsl:text>
 10724 </xsl:text>
 10518           <xsl:text>
 10725           <xsl:text>};
 10519 </xsl:text>
 10726 </xsl:text>
 10520           <xsl:text>    if(jumps_need_update) update_jumps();
 10727           <xsl:text>
 10521 </xsl:text>
 10728 </xsl:text>
 10522           <xsl:text>
 10729           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
 10523 </xsl:text>
 10730 </xsl:text>
 10524           <xsl:text>    apply_updates();
 10731           <xsl:text>
 10525 </xsl:text>
 10732 </xsl:text>
 10526           <xsl:text>
 10733           <xsl:text>function send_blob(data) {
 10527 </xsl:text>
 10734 </xsl:text>
 10528           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
 10735           <xsl:text>    if(data.length &gt; 0) {
 10529 </xsl:text>
 10736 </xsl:text>
 10530           <xsl:text>    pending_widget_animates = [];
 10737           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
 10531 </xsl:text>
 10738 </xsl:text>
 10532           <xsl:text>
 10739           <xsl:text>    };
 10533 </xsl:text>
 10740 </xsl:text>
 10534           <xsl:text>    requestAnimationFrameID = null;
 10741           <xsl:text>};
       
 10742 </xsl:text>
       
 10743           <xsl:text>
       
 10744 </xsl:text>
       
 10745           <xsl:text>const typedarray_types = {
       
 10746 </xsl:text>
       
 10747           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
 10748 </xsl:text>
       
 10749           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
 10750 </xsl:text>
       
 10751           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
 10752 </xsl:text>
       
 10753           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
 10754 </xsl:text>
       
 10755           <xsl:text>    STRING: (str) =&gt; {
       
 10756 </xsl:text>
       
 10757           <xsl:text>        // beremiz default string max size is 128
       
 10758 </xsl:text>
       
 10759           <xsl:text>        str = str.slice(0,128);
       
 10760 </xsl:text>
       
 10761           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
 10762 </xsl:text>
       
 10763           <xsl:text>        binary[0] = str.length;
       
 10764 </xsl:text>
       
 10765           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
 10766 </xsl:text>
       
 10767           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
 10768 </xsl:text>
       
 10769           <xsl:text>        }
       
 10770 </xsl:text>
       
 10771           <xsl:text>        return binary;
       
 10772 </xsl:text>
       
 10773           <xsl:text>    }
       
 10774 </xsl:text>
       
 10775           <xsl:text>    /* TODO */
       
 10776 </xsl:text>
       
 10777           <xsl:text>};
       
 10778 </xsl:text>
       
 10779           <xsl:text>
       
 10780 </xsl:text>
       
 10781           <xsl:text>function send_reset() {
       
 10782 </xsl:text>
       
 10783           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
 10784 </xsl:text>
       
 10785           <xsl:text>};
       
 10786 </xsl:text>
       
 10787           <xsl:text>
       
 10788 </xsl:text>
       
 10789           <xsl:text>var subscriptions = [];
       
 10790 </xsl:text>
       
 10791           <xsl:text>
       
 10792 </xsl:text>
       
 10793           <xsl:text>function subscribers(index) {
       
 10794 </xsl:text>
       
 10795           <xsl:text>    let entry = subscriptions[index];
       
 10796 </xsl:text>
       
 10797           <xsl:text>    let res;
       
 10798 </xsl:text>
       
 10799           <xsl:text>    if(entry == undefined){
       
 10800 </xsl:text>
       
 10801           <xsl:text>        res = new Set();
       
 10802 </xsl:text>
       
 10803           <xsl:text>        subscriptions[index] = [res,0];
       
 10804 </xsl:text>
       
 10805           <xsl:text>    }else{
       
 10806 </xsl:text>
       
 10807           <xsl:text>        [res, _ign] = entry;
       
 10808 </xsl:text>
       
 10809           <xsl:text>    }
       
 10810 </xsl:text>
       
 10811           <xsl:text>    return res
 10535 </xsl:text>
 10812 </xsl:text>
 10536           <xsl:text>}
 10813           <xsl:text>}
 10537 </xsl:text>
 10814 </xsl:text>
 10538           <xsl:text>
 10815           <xsl:text>
 10539 </xsl:text>
 10816 </xsl:text>
 10540           <xsl:text>function requestHMIAnimation() {
 10817           <xsl:text>function get_subscription_period(index) {
 10541 </xsl:text>
 10818 </xsl:text>
 10542           <xsl:text>    if(requestAnimationFrameID == null){
 10819           <xsl:text>    let entry = subscriptions[index];
 10543 </xsl:text>
 10820 </xsl:text>
 10544           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
 10821           <xsl:text>    if(entry == undefined)
       
 10822 </xsl:text>
       
 10823           <xsl:text>        return 0;
       
 10824 </xsl:text>
       
 10825           <xsl:text>    let [_ign, period] = entry;
       
 10826 </xsl:text>
       
 10827           <xsl:text>    return period;
       
 10828 </xsl:text>
       
 10829           <xsl:text>}
       
 10830 </xsl:text>
       
 10831           <xsl:text>
       
 10832 </xsl:text>
       
 10833           <xsl:text>function set_subscription_period(index, period) {
       
 10834 </xsl:text>
       
 10835           <xsl:text>    let entry = subscriptions[index];
       
 10836 </xsl:text>
       
 10837           <xsl:text>    if(entry == undefined){
       
 10838 </xsl:text>
       
 10839           <xsl:text>        subscriptions[index] = [new Set(), period];
       
 10840 </xsl:text>
       
 10841           <xsl:text>    } else {
       
 10842 </xsl:text>
       
 10843           <xsl:text>        entry[1] = period;
 10545 </xsl:text>
 10844 </xsl:text>
 10546           <xsl:text>    }
 10845           <xsl:text>    }
 10547 </xsl:text>
 10846 </xsl:text>
 10548           <xsl:text>}
 10847           <xsl:text>}
 10549 </xsl:text>
 10848 </xsl:text>
 10550           <xsl:text>
 10849           <xsl:text>
 10551 </xsl:text>
 10850 </xsl:text>
 10552           <xsl:text>// Message reception handler
 10851           <xsl:text>if(has_watchdog){
 10553 </xsl:text>
 10852 </xsl:text>
 10554           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
 10853           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
 10555 </xsl:text>
 10854 </xsl:text>
 10556           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
 10855           <xsl:text>    // Since dispatch directly calls change_hmi_value,
 10557 </xsl:text>
 10856 </xsl:text>
 10558           <xsl:text>ws.onmessage = function (evt) {
 10857           <xsl:text>    // PLC will periodically send variable at given frequency
 10559 </xsl:text>
 10858 </xsl:text>
 10560           <xsl:text>
 10859           <xsl:text>    subscribers(heartbeat_index).add({
 10561 </xsl:text>
 10860 </xsl:text>
 10562           <xsl:text>    let data = evt.data;
 10861           <xsl:text>        /* type: "Watchdog", */
 10563 </xsl:text>
 10862 </xsl:text>
 10564           <xsl:text>    let dv = new DataView(data);
 10863           <xsl:text>        frequency: 1,
 10565 </xsl:text>
 10864 </xsl:text>
 10566           <xsl:text>    let i = 0;
 10865           <xsl:text>        indexes: [heartbeat_index],
 10567 </xsl:text>
 10866 </xsl:text>
 10568           <xsl:text>    try {
 10867           <xsl:text>        new_hmi_value: function(index, value, oldval) {
 10569 </xsl:text>
 10868 </xsl:text>
 10570           <xsl:text>        for(let hash_int of hmi_hash) {
 10869           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
 10571 </xsl:text>
 10870 </xsl:text>
 10572           <xsl:text>            if(hash_int != dv.getUint8(i)){
 10871           <xsl:text>        }
 10573 </xsl:text>
 10872 </xsl:text>
 10574           <xsl:text>                throw new Error("Hash doesn't match");
 10873           <xsl:text>    });
 10575 </xsl:text>
 10874 </xsl:text>
 10576           <xsl:text>            };
 10875           <xsl:text>}
 10577 </xsl:text>
 10876 </xsl:text>
 10578           <xsl:text>            i++;
 10877           <xsl:text>
 10579 </xsl:text>
 10878 </xsl:text>
 10580           <xsl:text>        };
 10879           <xsl:text>
 10581 </xsl:text>
 10880 </xsl:text>
 10582           <xsl:text>
 10881           <xsl:text>var page_fading = "off";
 10583 </xsl:text>
 10882 </xsl:text>
 10584           <xsl:text>        while(i &lt; data.byteLength){
 10883           <xsl:text>var page_fading_args = "off";
 10585 </xsl:text>
 10884 </xsl:text>
 10586           <xsl:text>            let index = dv.getUint32(i, true);
 10885           <xsl:text>function fading_page_switch(...args){
 10587 </xsl:text>
 10886 </xsl:text>
 10588           <xsl:text>            i += 4;
 10887           <xsl:text>    if(page_fading == "in_progress")
 10589 </xsl:text>
 10888 </xsl:text>
 10590           <xsl:text>            let iectype = hmitree_types[index];
 10889           <xsl:text>        page_fading = "forced";
 10591 </xsl:text>
 10890 </xsl:text>
 10592           <xsl:text>            if(iectype != undefined){
 10891           <xsl:text>    else
 10593 </xsl:text>
 10892 </xsl:text>
 10594           <xsl:text>                let dvgetter = dvgetters[iectype];
 10893           <xsl:text>        page_fading = "pending";
 10595 </xsl:text>
 10894 </xsl:text>
 10596           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
 10895           <xsl:text>    page_fading_args = args;
 10597 </xsl:text>
 10896 </xsl:text>
 10598           <xsl:text>                updates.set(index, value);
 10897           <xsl:text>
 10599 </xsl:text>
 10898 </xsl:text>
 10600           <xsl:text>                i += bytesize;
 10899           <xsl:text>    requestHMIAnimation();
 10601 </xsl:text>
 10900 </xsl:text>
 10602           <xsl:text>            } else {
 10901           <xsl:text>
 10603 </xsl:text>
 10902 </xsl:text>
 10604           <xsl:text>                throw new Error("Unknown index "+index);
 10903           <xsl:text>}
       
 10904 </xsl:text>
       
 10905           <xsl:text>document.body.style.backgroundColor = "black";
       
 10906 </xsl:text>
       
 10907           <xsl:text>
       
 10908 </xsl:text>
       
 10909           <xsl:text>// subscribe to per instance current page hmi variable
       
 10910 </xsl:text>
       
 10911           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
       
 10912 </xsl:text>
       
 10913           <xsl:text>subscribers(current_page_var_index).add({
       
 10914 </xsl:text>
       
 10915           <xsl:text>    frequency: 1,
       
 10916 </xsl:text>
       
 10917           <xsl:text>    indexes: [current_page_var_index],
       
 10918 </xsl:text>
       
 10919           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
 10920 </xsl:text>
       
 10921           <xsl:text>        if(value.startsWith("!"))
       
 10922 </xsl:text>
       
 10923           <xsl:text>            fading_page_switch(value.slice(1));
       
 10924 </xsl:text>
       
 10925           <xsl:text>    }
       
 10926 </xsl:text>
       
 10927           <xsl:text>});
       
 10928 </xsl:text>
       
 10929           <xsl:text>
       
 10930 </xsl:text>
       
 10931           <xsl:text>function svg_text_to_multiline(elt) {
       
 10932 </xsl:text>
       
 10933           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
       
 10934 </xsl:text>
       
 10935           <xsl:text>}
       
 10936 </xsl:text>
       
 10937           <xsl:text>
       
 10938 </xsl:text>
       
 10939           <xsl:text>function multiline_to_svg_text(elt, str, blank) {
       
 10940 </xsl:text>
       
 10941           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = blank?"":line;});
       
 10942 </xsl:text>
       
 10943           <xsl:text>}
       
 10944 </xsl:text>
       
 10945           <xsl:text>
       
 10946 </xsl:text>
       
 10947           <xsl:text>function switch_langnum(langnum) {
       
 10948 </xsl:text>
       
 10949           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
 10950 </xsl:text>
       
 10951           <xsl:text>
       
 10952 </xsl:text>
       
 10953           <xsl:text>    for (let translation of translations) {
       
 10954 </xsl:text>
       
 10955           <xsl:text>        let [objs, msgs] = translation;
       
 10956 </xsl:text>
       
 10957           <xsl:text>        let msg = msgs[langnum];
       
 10958 </xsl:text>
       
 10959           <xsl:text>        for (let obj of objs) {
       
 10960 </xsl:text>
       
 10961           <xsl:text>            multiline_to_svg_text(obj, msg);
       
 10962 </xsl:text>
       
 10963           <xsl:text>            obj.setAttribute("lang",langnum);
       
 10964 </xsl:text>
       
 10965           <xsl:text>        }
       
 10966 </xsl:text>
       
 10967           <xsl:text>    }
       
 10968 </xsl:text>
       
 10969           <xsl:text>    return langnum;
       
 10970 </xsl:text>
       
 10971           <xsl:text>}
       
 10972 </xsl:text>
       
 10973           <xsl:text>
       
 10974 </xsl:text>
       
 10975           <xsl:text>// backup original texts
       
 10976 </xsl:text>
       
 10977           <xsl:text>for (let translation of translations) {
       
 10978 </xsl:text>
       
 10979           <xsl:text>    let [objs, msgs] = translation;
       
 10980 </xsl:text>
       
 10981           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
 10982 </xsl:text>
       
 10983           <xsl:text>}
       
 10984 </xsl:text>
       
 10985           <xsl:text>
       
 10986 </xsl:text>
       
 10987           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
 10988 </xsl:text>
       
 10989           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
 10990 </xsl:text>
       
 10991           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
 10992 </xsl:text>
       
 10993           <xsl:text>subscribers(lang_local_index).add({
       
 10994 </xsl:text>
       
 10995           <xsl:text>    indexes: [lang_local_index],
       
 10996 </xsl:text>
       
 10997           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
 10998 </xsl:text>
       
 10999           <xsl:text>        let current_lang =  switch_langnum(value);
       
 11000 </xsl:text>
       
 11001           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
 11002 </xsl:text>
       
 11003           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
 11004 </xsl:text>
       
 11005           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
 11006 </xsl:text>
       
 11007           <xsl:text>        switch_page();
       
 11008 </xsl:text>
       
 11009           <xsl:text>    }
       
 11010 </xsl:text>
       
 11011           <xsl:text>});
       
 11012 </xsl:text>
       
 11013           <xsl:text>
       
 11014 </xsl:text>
       
 11015           <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language
       
 11016 </xsl:text>
       
 11017           <xsl:text>function get_current_lang_code(){
       
 11018 </xsl:text>
       
 11019           <xsl:text>    return cache[langcode_local_index];
       
 11020 </xsl:text>
       
 11021           <xsl:text>}
       
 11022 </xsl:text>
       
 11023           <xsl:text>
       
 11024 </xsl:text>
       
 11025           <xsl:text>function setup_lang(){
       
 11026 </xsl:text>
       
 11027           <xsl:text>    let current_lang = cache[lang_local_index];
       
 11028 </xsl:text>
       
 11029           <xsl:text>    let new_lang = switch_langnum(current_lang);
       
 11030 </xsl:text>
       
 11031           <xsl:text>    if(current_lang != new_lang){
       
 11032 </xsl:text>
       
 11033           <xsl:text>        apply_hmi_value(lang_local_index, new_lang);
       
 11034 </xsl:text>
       
 11035           <xsl:text>    }
       
 11036 </xsl:text>
       
 11037           <xsl:text>}
       
 11038 </xsl:text>
       
 11039           <xsl:text>
       
 11040 </xsl:text>
       
 11041           <xsl:text>setup_lang();
       
 11042 </xsl:text>
       
 11043           <xsl:text>
       
 11044 </xsl:text>
       
 11045           <xsl:text>function update_subscriptions() {
       
 11046 </xsl:text>
       
 11047           <xsl:text>    let delta = [];
       
 11048 </xsl:text>
       
 11049           <xsl:text>    for(let index in subscriptions){
       
 11050 </xsl:text>
       
 11051           <xsl:text>        let widgets = subscribers(index);
       
 11052 </xsl:text>
       
 11053           <xsl:text>
       
 11054 </xsl:text>
       
 11055           <xsl:text>        // periods are in ms
       
 11056 </xsl:text>
       
 11057           <xsl:text>        let previous_period = get_subscription_period(index);
       
 11058 </xsl:text>
       
 11059           <xsl:text>
       
 11060 </xsl:text>
       
 11061           <xsl:text>        // subscribing with a zero period is unsubscribing
       
 11062 </xsl:text>
       
 11063           <xsl:text>        let new_period = 0;
       
 11064 </xsl:text>
       
 11065           <xsl:text>        if(widgets.size &gt; 0) {
       
 11066 </xsl:text>
       
 11067           <xsl:text>            let maxfreq = 0;
       
 11068 </xsl:text>
       
 11069           <xsl:text>            for(let widget of widgets){
       
 11070 </xsl:text>
       
 11071           <xsl:text>                let wf = widget.frequency;
       
 11072 </xsl:text>
       
 11073           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
 11074 </xsl:text>
       
 11075           <xsl:text>                    maxfreq = wf;
 10605 </xsl:text>
 11076 </xsl:text>
 10606           <xsl:text>            }
 11077           <xsl:text>            }
 10607 </xsl:text>
 11078 </xsl:text>
 10608           <xsl:text>        };
 11079           <xsl:text>
 10609 </xsl:text>
 11080 </xsl:text>
 10610           <xsl:text>        // register for rendering on next frame, since there are updates
 11081           <xsl:text>            if(maxfreq != 0)
       
 11082 </xsl:text>
       
 11083           <xsl:text>                new_period = 1000/maxfreq;
       
 11084 </xsl:text>
       
 11085           <xsl:text>        }
       
 11086 </xsl:text>
       
 11087           <xsl:text>
       
 11088 </xsl:text>
       
 11089           <xsl:text>        if(previous_period != new_period) {
       
 11090 </xsl:text>
       
 11091           <xsl:text>            set_subscription_period(index, new_period);
       
 11092 </xsl:text>
       
 11093           <xsl:text>            if(index &lt;= last_remote_index){
       
 11094 </xsl:text>
       
 11095           <xsl:text>                delta.push(
       
 11096 </xsl:text>
       
 11097           <xsl:text>                    new Uint8Array([2]), /* subscribe = 2 */
       
 11098 </xsl:text>
       
 11099           <xsl:text>                    new Uint32Array([index]),
       
 11100 </xsl:text>
       
 11101           <xsl:text>                    new Uint16Array([new_period]));
       
 11102 </xsl:text>
       
 11103           <xsl:text>            }
       
 11104 </xsl:text>
       
 11105           <xsl:text>        }
       
 11106 </xsl:text>
       
 11107           <xsl:text>    }
       
 11108 </xsl:text>
       
 11109           <xsl:text>    send_blob(delta);
       
 11110 </xsl:text>
       
 11111           <xsl:text>};
       
 11112 </xsl:text>
       
 11113           <xsl:text>
       
 11114 </xsl:text>
       
 11115           <xsl:text>function send_hmi_value(index, value) {
       
 11116 </xsl:text>
       
 11117           <xsl:text>    if(index &gt; last_remote_index){
       
 11118 </xsl:text>
       
 11119           <xsl:text>        updates.set(index, value);
       
 11120 </xsl:text>
       
 11121           <xsl:text>
       
 11122 </xsl:text>
       
 11123           <xsl:text>        if(persistent_indexes.has(index)){
       
 11124 </xsl:text>
       
 11125           <xsl:text>            let varname = persistent_indexes.get(index);
       
 11126 </xsl:text>
       
 11127           <xsl:text>            document.cookie = varname+"="+value+"; max-age=3153600000";
       
 11128 </xsl:text>
       
 11129           <xsl:text>        }
       
 11130 </xsl:text>
       
 11131           <xsl:text>
 10611 </xsl:text>
 11132 </xsl:text>
 10612           <xsl:text>        requestHMIAnimation();
 11133           <xsl:text>        requestHMIAnimation();
 10613 </xsl:text>
 11134 </xsl:text>
 10614           <xsl:text>    } catch(err) {
 11135           <xsl:text>        return;
 10615 </xsl:text>
       
 10616           <xsl:text>        // 1003 is for "Unsupported Data"
       
 10617 </xsl:text>
       
 10618           <xsl:text>        // ws.close(1003, err.message);
       
 10619 </xsl:text>
       
 10620           <xsl:text>
       
 10621 </xsl:text>
       
 10622           <xsl:text>        // TODO : remove debug alert ?
       
 10623 </xsl:text>
       
 10624           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
 10625 </xsl:text>
       
 10626           <xsl:text>
       
 10627 </xsl:text>
       
 10628           <xsl:text>        // force reload ignoring cache
       
 10629 </xsl:text>
       
 10630           <xsl:text>        location.reload(true);
       
 10631 </xsl:text>
 11136 </xsl:text>
 10632           <xsl:text>    }
 11137           <xsl:text>    }
 10633 </xsl:text>
 11138 </xsl:text>
       
 11139           <xsl:text>
       
 11140 </xsl:text>
       
 11141           <xsl:text>    let iectype = hmitree_types[index];
       
 11142 </xsl:text>
       
 11143           <xsl:text>    let tobinary = typedarray_types[iectype];
       
 11144 </xsl:text>
       
 11145           <xsl:text>    send_blob([
       
 11146 </xsl:text>
       
 11147           <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
       
 11148 </xsl:text>
       
 11149           <xsl:text>        new Uint32Array([index]),
       
 11150 </xsl:text>
       
 11151           <xsl:text>        tobinary(value)]);
       
 11152 </xsl:text>
       
 11153           <xsl:text>
       
 11154 </xsl:text>
       
 11155           <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
 11156 </xsl:text>
       
 11157           <xsl:text>    // cache[index] = value;
       
 11158 </xsl:text>
 10634           <xsl:text>};
 11159           <xsl:text>};
 10635 </xsl:text>
 11160 </xsl:text>
 10636           <xsl:text>
 11161           <xsl:text>
 10637 </xsl:text>
 11162 </xsl:text>
 10638           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
 11163           <xsl:text>function apply_hmi_value(index, new_val) {
 10639 </xsl:text>
 11164 </xsl:text>
 10640           <xsl:text>
 11165           <xsl:text>    // Similarly to previous comment, taking decision to update based 
 10641 </xsl:text>
 11166 </xsl:text>
 10642           <xsl:text>function send_blob(data) {
 11167           <xsl:text>    // on cache content is bad and can lead to inconsistency
 10643 </xsl:text>
 11168 </xsl:text>
 10644           <xsl:text>    if(data.length &gt; 0) {
 11169           <xsl:text>    /*let old_val = cache[index];*/
 10645 </xsl:text>
 11170 </xsl:text>
 10646           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
 11171           <xsl:text>    if(new_val != undefined /*&amp;&amp; old_val != new_val*/)
 10647 </xsl:text>
 11172 </xsl:text>
 10648           <xsl:text>    };
 11173           <xsl:text>        send_hmi_value(index, new_val);
       
 11174 </xsl:text>
       
 11175           <xsl:text>    return new_val;
       
 11176 </xsl:text>
       
 11177           <xsl:text>}
       
 11178 </xsl:text>
       
 11179           <xsl:text>
       
 11180 </xsl:text>
       
 11181           <xsl:text>const quotes = {"'":null, '"':null};
       
 11182 </xsl:text>
       
 11183           <xsl:text>
       
 11184 </xsl:text>
       
 11185           <xsl:text>function eval_operation_string(old_val, opstr) {
       
 11186 </xsl:text>
       
 11187           <xsl:text>    let op = opstr[0];
       
 11188 </xsl:text>
       
 11189           <xsl:text>    let given_val;
       
 11190 </xsl:text>
       
 11191           <xsl:text>    if(opstr.length &lt; 2) 
       
 11192 </xsl:text>
       
 11193           <xsl:text>        return undefined;
       
 11194 </xsl:text>
       
 11195           <xsl:text>    if(opstr[1] in quotes){
       
 11196 </xsl:text>
       
 11197           <xsl:text>        if(opstr.length &lt; 3) 
       
 11198 </xsl:text>
       
 11199           <xsl:text>            return undefined;
       
 11200 </xsl:text>
       
 11201           <xsl:text>        if(opstr[opstr.length-1] == opstr[1]){
       
 11202 </xsl:text>
       
 11203           <xsl:text>            given_val = opstr.slice(2,opstr.length-1);
       
 11204 </xsl:text>
       
 11205           <xsl:text>        }
       
 11206 </xsl:text>
       
 11207           <xsl:text>    } else {
       
 11208 </xsl:text>
       
 11209           <xsl:text>        given_val = Number(opstr.slice(1));
       
 11210 </xsl:text>
       
 11211           <xsl:text>    }
       
 11212 </xsl:text>
       
 11213           <xsl:text>    let new_val;
       
 11214 </xsl:text>
       
 11215           <xsl:text>    switch(op){
       
 11216 </xsl:text>
       
 11217           <xsl:text>      case "=":
       
 11218 </xsl:text>
       
 11219           <xsl:text>        new_val = given_val;
       
 11220 </xsl:text>
       
 11221           <xsl:text>        break;
       
 11222 </xsl:text>
       
 11223           <xsl:text>      case "+":
       
 11224 </xsl:text>
       
 11225           <xsl:text>        new_val = old_val + given_val;
       
 11226 </xsl:text>
       
 11227           <xsl:text>        break;
       
 11228 </xsl:text>
       
 11229           <xsl:text>      case "-":
       
 11230 </xsl:text>
       
 11231           <xsl:text>        new_val = old_val - given_val;
       
 11232 </xsl:text>
       
 11233           <xsl:text>        break;
       
 11234 </xsl:text>
       
 11235           <xsl:text>      case "*":
       
 11236 </xsl:text>
       
 11237           <xsl:text>        new_val = old_val * given_val;
       
 11238 </xsl:text>
       
 11239           <xsl:text>        break;
       
 11240 </xsl:text>
       
 11241           <xsl:text>      case "/":
       
 11242 </xsl:text>
       
 11243           <xsl:text>        new_val = old_val / given_val;
       
 11244 </xsl:text>
       
 11245           <xsl:text>        break;
       
 11246 </xsl:text>
       
 11247           <xsl:text>    }
       
 11248 </xsl:text>
       
 11249           <xsl:text>    return new_val;
       
 11250 </xsl:text>
       
 11251           <xsl:text>}
       
 11252 </xsl:text>
       
 11253           <xsl:text>
       
 11254 </xsl:text>
       
 11255           <xsl:text>var current_visible_page;
       
 11256 </xsl:text>
       
 11257           <xsl:text>var current_subscribed_page;
       
 11258 </xsl:text>
       
 11259           <xsl:text>var current_page_index;
       
 11260 </xsl:text>
       
 11261           <xsl:text>var page_node_local_index = hmi_local_index("page_node");
       
 11262 </xsl:text>
       
 11263           <xsl:text>var page_switch_in_progress = false;
       
 11264 </xsl:text>
       
 11265           <xsl:text>
       
 11266 </xsl:text>
       
 11267           <xsl:text>function toggleFullscreen() {
       
 11268 </xsl:text>
       
 11269           <xsl:text>  let elem = document.documentElement;
       
 11270 </xsl:text>
       
 11271           <xsl:text>
       
 11272 </xsl:text>
       
 11273           <xsl:text>  if (!document.fullscreenElement) {
       
 11274 </xsl:text>
       
 11275           <xsl:text>    elem.requestFullscreen().catch(err =&gt; {
       
 11276 </xsl:text>
       
 11277           <xsl:text>      console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")");
       
 11278 </xsl:text>
       
 11279           <xsl:text>    });
       
 11280 </xsl:text>
       
 11281           <xsl:text>  } else {
       
 11282 </xsl:text>
       
 11283           <xsl:text>    document.exitFullscreen();
       
 11284 </xsl:text>
       
 11285           <xsl:text>  }
       
 11286 </xsl:text>
       
 11287           <xsl:text>}
       
 11288 </xsl:text>
       
 11289           <xsl:text>
       
 11290 </xsl:text>
       
 11291           <xsl:text>function prepare_svg() {
       
 11292 </xsl:text>
       
 11293           <xsl:text>    // prevents context menu from appearing on right click and long touch
       
 11294 </xsl:text>
       
 11295           <xsl:text>    document.body.addEventListener('contextmenu', e =&gt; {
       
 11296 </xsl:text>
       
 11297           <xsl:text>        toggleFullscreen();
       
 11298 </xsl:text>
       
 11299           <xsl:text>        e.preventDefault();
       
 11300 </xsl:text>
       
 11301           <xsl:text>    });
       
 11302 </xsl:text>
       
 11303           <xsl:text>
       
 11304 </xsl:text>
       
 11305           <xsl:text>    for(let eltid in detachable_elements){
       
 11306 </xsl:text>
       
 11307           <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
 11308 </xsl:text>
       
 11309           <xsl:text>        parent.removeChild(element);
       
 11310 </xsl:text>
       
 11311           <xsl:text>    }
 10649 </xsl:text>
 11312 </xsl:text>
 10650           <xsl:text>};
 11313           <xsl:text>};
 10651 </xsl:text>
 11314 </xsl:text>
 10652           <xsl:text>
 11315           <xsl:text>
 10653 </xsl:text>
 11316 </xsl:text>
 10654           <xsl:text>const typedarray_types = {
 11317           <xsl:text>function switch_page(page_name, page_index) {
 10655 </xsl:text>
 11318 </xsl:text>
 10656           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
 11319           <xsl:text>    if(page_switch_in_progress){
 10657 </xsl:text>
 11320 </xsl:text>
 10658           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
 11321           <xsl:text>        /* page switch already going */
 10659 </xsl:text>
 11322 </xsl:text>
 10660           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
 11323           <xsl:text>        /* TODO LOG ERROR */
 10661 </xsl:text>
 11324 </xsl:text>
 10662           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
 11325           <xsl:text>        return false;
 10663 </xsl:text>
 11326 </xsl:text>
 10664           <xsl:text>    STRING: (str) =&gt; {
 11327           <xsl:text>    }
 10665 </xsl:text>
 11328 </xsl:text>
 10666           <xsl:text>        // beremiz default string max size is 128
 11329           <xsl:text>    page_switch_in_progress = true;
 10667 </xsl:text>
 11330 </xsl:text>
 10668           <xsl:text>        str = str.slice(0,128);
 11331           <xsl:text>
 10669 </xsl:text>
 11332 </xsl:text>
 10670           <xsl:text>        binary = new Uint8Array(str.length + 1);
 11333           <xsl:text>    if(page_name == undefined)
 10671 </xsl:text>
 11334 </xsl:text>
 10672           <xsl:text>        binary[0] = str.length;
 11335           <xsl:text>        page_name = current_subscribed_page;
 10673 </xsl:text>
 11336 </xsl:text>
 10674           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
 11337           <xsl:text>    else if(page_index == undefined){
 10675 </xsl:text>
 11338 </xsl:text>
 10676           <xsl:text>            binary[i+1] = str.charCodeAt(i);
 11339           <xsl:text>        [page_name, page_index] = page_name.split('@')
       
 11340 </xsl:text>
       
 11341           <xsl:text>    }
       
 11342 </xsl:text>
       
 11343           <xsl:text>
       
 11344 </xsl:text>
       
 11345           <xsl:text>    let old_desc = page_desc[current_subscribed_page];
       
 11346 </xsl:text>
       
 11347           <xsl:text>    let new_desc = page_desc[page_name];
       
 11348 </xsl:text>
       
 11349           <xsl:text>
       
 11350 </xsl:text>
       
 11351           <xsl:text>    if(new_desc == undefined){
       
 11352 </xsl:text>
       
 11353           <xsl:text>        /* TODO LOG ERROR */
       
 11354 </xsl:text>
       
 11355           <xsl:text>        return false;
       
 11356 </xsl:text>
       
 11357           <xsl:text>    }
       
 11358 </xsl:text>
       
 11359           <xsl:text>
       
 11360 </xsl:text>
       
 11361           <xsl:text>    if(page_index == undefined)
       
 11362 </xsl:text>
       
 11363           <xsl:text>        page_index = new_desc.page_index;
       
 11364 </xsl:text>
       
 11365           <xsl:text>    else if(typeof(page_index) == "string") {
       
 11366 </xsl:text>
       
 11367           <xsl:text>        let hmitree_node = hmitree_nodes[page_index];
       
 11368 </xsl:text>
       
 11369           <xsl:text>        if(hmitree_node !== undefined){
       
 11370 </xsl:text>
       
 11371           <xsl:text>            let [int_index, hmiclass] = hmitree_node;
       
 11372 </xsl:text>
       
 11373           <xsl:text>            if(hmiclass == new_desc.page_class)
       
 11374 </xsl:text>
       
 11375           <xsl:text>                page_index = int_index;
       
 11376 </xsl:text>
       
 11377           <xsl:text>            else
       
 11378 </xsl:text>
       
 11379           <xsl:text>                page_index = new_desc.page_index;
       
 11380 </xsl:text>
       
 11381           <xsl:text>        } else {
       
 11382 </xsl:text>
       
 11383           <xsl:text>            page_index = new_desc.page_index;
 10677 </xsl:text>
 11384 </xsl:text>
 10678           <xsl:text>        }
 11385           <xsl:text>        }
 10679 </xsl:text>
 11386 </xsl:text>
 10680           <xsl:text>        return binary;
       
 10681 </xsl:text>
       
 10682           <xsl:text>    }
 11387           <xsl:text>    }
 10683 </xsl:text>
 11388 </xsl:text>
 10684           <xsl:text>    /* TODO */
 11389           <xsl:text>
       
 11390 </xsl:text>
       
 11391           <xsl:text>    if(old_desc){
       
 11392 </xsl:text>
       
 11393           <xsl:text>        old_desc.widgets.map(([widget,relativeness])=&gt;widget.unsub());
       
 11394 </xsl:text>
       
 11395           <xsl:text>    }
       
 11396 </xsl:text>
       
 11397           <xsl:text>    const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
       
 11398 </xsl:text>
       
 11399           <xsl:text>
       
 11400 </xsl:text>
       
 11401           <xsl:text>    const container_id = page_name + (page_index != undefined ? page_index : "");
       
 11402 </xsl:text>
       
 11403           <xsl:text>
       
 11404 </xsl:text>
       
 11405           <xsl:text>    new_desc.widgets.map(([widget,relativeness])=&gt;widget.sub(new_offset,relativeness,container_id));
       
 11406 </xsl:text>
       
 11407           <xsl:text>
       
 11408 </xsl:text>
       
 11409           <xsl:text>    update_subscriptions();
       
 11410 </xsl:text>
       
 11411           <xsl:text>
       
 11412 </xsl:text>
       
 11413           <xsl:text>    current_subscribed_page = page_name;
       
 11414 </xsl:text>
       
 11415           <xsl:text>    current_page_index = page_index;
       
 11416 </xsl:text>
       
 11417           <xsl:text>    let page_node;
       
 11418 </xsl:text>
       
 11419           <xsl:text>    if(page_index != undefined){
       
 11420 </xsl:text>
       
 11421           <xsl:text>        page_node = hmitree_paths[page_index];
       
 11422 </xsl:text>
       
 11423           <xsl:text>    }else{
       
 11424 </xsl:text>
       
 11425           <xsl:text>        page_node = "";
       
 11426 </xsl:text>
       
 11427           <xsl:text>    }
       
 11428 </xsl:text>
       
 11429           <xsl:text>    apply_hmi_value(page_node_local_index, page_node);
       
 11430 </xsl:text>
       
 11431           <xsl:text>
       
 11432 </xsl:text>
       
 11433           <xsl:text>    jumps_need_update = true;
       
 11434 </xsl:text>
       
 11435           <xsl:text>
       
 11436 </xsl:text>
       
 11437           <xsl:text>    requestHMIAnimation();
       
 11438 </xsl:text>
       
 11439           <xsl:text>    jump_history.push([page_name, page_index]);
       
 11440 </xsl:text>
       
 11441           <xsl:text>    if(jump_history.length &gt; 42)
       
 11442 </xsl:text>
       
 11443           <xsl:text>        jump_history.shift();
       
 11444 </xsl:text>
       
 11445           <xsl:text>
       
 11446 </xsl:text>
       
 11447           <xsl:text>    apply_hmi_value(current_page_var_index, page_index == undefined
       
 11448 </xsl:text>
       
 11449           <xsl:text>        ? page_name
       
 11450 </xsl:text>
       
 11451           <xsl:text>        : page_name + "@" + hmitree_paths[page_index]);
       
 11452 </xsl:text>
       
 11453           <xsl:text>
       
 11454 </xsl:text>
       
 11455           <xsl:text>    return true;
 10685 </xsl:text>
 11456 </xsl:text>
 10686           <xsl:text>};
 11457           <xsl:text>};
 10687 </xsl:text>
 11458 </xsl:text>
 10688           <xsl:text>
 11459           <xsl:text>
 10689 </xsl:text>
 11460 </xsl:text>
 10690           <xsl:text>function send_reset() {
 11461           <xsl:text>function switch_visible_page(page_name) {
 10691 </xsl:text>
 11462 </xsl:text>
 10692           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
 11463           <xsl:text>
       
 11464 </xsl:text>
       
 11465           <xsl:text>    let old_desc = page_desc[current_visible_page];
       
 11466 </xsl:text>
       
 11467           <xsl:text>    let new_desc = page_desc[page_name];
       
 11468 </xsl:text>
       
 11469           <xsl:text>
       
 11470 </xsl:text>
       
 11471           <xsl:text>    if(old_desc){
       
 11472 </xsl:text>
       
 11473           <xsl:text>        for(let eltid in old_desc.required_detachables){
       
 11474 </xsl:text>
       
 11475           <xsl:text>            if(!(eltid in new_desc.required_detachables)){
       
 11476 </xsl:text>
       
 11477           <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
       
 11478 </xsl:text>
       
 11479           <xsl:text>                parent.removeChild(element);
       
 11480 </xsl:text>
       
 11481           <xsl:text>            }
       
 11482 </xsl:text>
       
 11483           <xsl:text>        }
       
 11484 </xsl:text>
       
 11485           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
 11486 </xsl:text>
       
 11487           <xsl:text>            if(!(eltid in old_desc.required_detachables)){
       
 11488 </xsl:text>
       
 11489           <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
       
 11490 </xsl:text>
       
 11491           <xsl:text>                parent.appendChild(element);
       
 11492 </xsl:text>
       
 11493           <xsl:text>            }
       
 11494 </xsl:text>
       
 11495           <xsl:text>        }
       
 11496 </xsl:text>
       
 11497           <xsl:text>    }else{
       
 11498 </xsl:text>
       
 11499           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
 11500 </xsl:text>
       
 11501           <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
       
 11502 </xsl:text>
       
 11503           <xsl:text>            parent.appendChild(element);
       
 11504 </xsl:text>
       
 11505           <xsl:text>        }
       
 11506 </xsl:text>
       
 11507           <xsl:text>    }
       
 11508 </xsl:text>
       
 11509           <xsl:text>
       
 11510 </xsl:text>
       
 11511           <xsl:text>    svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
       
 11512 </xsl:text>
       
 11513           <xsl:text>    current_visible_page = page_name;
 10693 </xsl:text>
 11514 </xsl:text>
 10694           <xsl:text>};
 11515           <xsl:text>};
 10695 </xsl:text>
 11516 </xsl:text>
 10696           <xsl:text>
 11517           <xsl:text>
 10697 </xsl:text>
 11518 </xsl:text>
 10698           <xsl:text>var subscriptions = [];
 11519           <xsl:text>// Once connection established
 10699 </xsl:text>
 11520 </xsl:text>
 10700           <xsl:text>
 11521           <xsl:text>ws.onopen = function (evt) {
 10701 </xsl:text>
 11522 </xsl:text>
 10702           <xsl:text>function subscribers(index) {
 11523           <xsl:text>    init_widgets();
 10703 </xsl:text>
 11524 </xsl:text>
 10704           <xsl:text>    let entry = subscriptions[index];
 11525           <xsl:text>    send_reset();
 10705 </xsl:text>
 11526 </xsl:text>
 10706           <xsl:text>    let res;
 11527           <xsl:text>    // show main page
 10707 </xsl:text>
 11528 </xsl:text>
 10708           <xsl:text>    if(entry == undefined){
 11529           <xsl:text>    prepare_svg();
 10709 </xsl:text>
 11530 </xsl:text>
 10710           <xsl:text>        res = new Set();
 11531           <xsl:text>    switch_page(default_page);
 10711 </xsl:text>
 11532 </xsl:text>
 10712           <xsl:text>        subscriptions[index] = [res,0];
 11533           <xsl:text>};
 10713 </xsl:text>
 11534 </xsl:text>
 10714           <xsl:text>    }else{
 11535           <xsl:text>
 10715 </xsl:text>
 11536 </xsl:text>
 10716           <xsl:text>        [res, _ign] = entry;
 11537           <xsl:text>ws.onclose = function (evt) {
       
 11538 </xsl:text>
       
 11539           <xsl:text>    // TODO : add visible notification while waiting for reload
       
 11540 </xsl:text>
       
 11541           <xsl:text>    console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s.");
       
 11542 </xsl:text>
       
 11543           <xsl:text>    // TODO : re-enable auto reload when not in debug
       
 11544 </xsl:text>
       
 11545           <xsl:text>    //window.setTimeout(() =&gt; location.reload(true), 10000);
       
 11546 </xsl:text>
       
 11547           <xsl:text>    alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+".");
       
 11548 </xsl:text>
       
 11549           <xsl:text>
       
 11550 </xsl:text>
       
 11551           <xsl:text>};
       
 11552 </xsl:text>
       
 11553           <xsl:text>
       
 11554 </xsl:text>
       
 11555           <xsl:text>const xmlns = "http://www.w3.org/2000/svg";
       
 11556 </xsl:text>
       
 11557           <xsl:text>var edit_callback;
       
 11558 </xsl:text>
       
 11559           <xsl:text>const localtypes = {"PAGE_LOCAL":null, "HMI_LOCAL":null}
       
 11560 </xsl:text>
       
 11561           <xsl:text>function edit_value(path, valuetype, callback, initial) {
       
 11562 </xsl:text>
       
 11563           <xsl:text>    if(valuetype in localtypes){
       
 11564 </xsl:text>
       
 11565           <xsl:text>        valuetype = (typeof initial) == "number" ? "HMI_REAL" : "HMI_STRING";
 10717 </xsl:text>
 11566 </xsl:text>
 10718           <xsl:text>    }
 11567           <xsl:text>    }
 10719 </xsl:text>
 11568 </xsl:text>
 10720           <xsl:text>    return res
 11569           <xsl:text>    let [keypadid, xcoord, ycoord] = keypads[valuetype];
 10721 </xsl:text>
 11570 </xsl:text>
 10722           <xsl:text>}
 11571           <xsl:text>    edit_callback = callback;
 10723 </xsl:text>
 11572 </xsl:text>
 10724           <xsl:text>
 11573           <xsl:text>    let widget = hmi_widgets[keypadid];
 10725 </xsl:text>
 11574 </xsl:text>
 10726           <xsl:text>function get_subscription_period(index) {
 11575           <xsl:text>    widget.start_edit(path, valuetype, callback, initial);
 10727 </xsl:text>
       
 10728           <xsl:text>    let entry = subscriptions[index];
       
 10729 </xsl:text>
       
 10730           <xsl:text>    if(entry == undefined)
       
 10731 </xsl:text>
       
 10732           <xsl:text>        return 0;
       
 10733 </xsl:text>
       
 10734           <xsl:text>    let [_ign, period] = entry;
       
 10735 </xsl:text>
       
 10736           <xsl:text>    return period;
       
 10737 </xsl:text>
       
 10738           <xsl:text>}
       
 10739 </xsl:text>
       
 10740           <xsl:text>
       
 10741 </xsl:text>
       
 10742           <xsl:text>function set_subscription_period(index, period) {
       
 10743 </xsl:text>
       
 10744           <xsl:text>    let entry = subscriptions[index];
       
 10745 </xsl:text>
       
 10746           <xsl:text>    if(entry == undefined){
       
 10747 </xsl:text>
       
 10748           <xsl:text>        subscriptions[index] = [new Set(), period];
       
 10749 </xsl:text>
       
 10750           <xsl:text>    } else {
       
 10751 </xsl:text>
       
 10752           <xsl:text>        entry[1] = period;
       
 10753 </xsl:text>
       
 10754           <xsl:text>    }
       
 10755 </xsl:text>
       
 10756           <xsl:text>}
       
 10757 </xsl:text>
       
 10758           <xsl:text>
       
 10759 </xsl:text>
       
 10760           <xsl:text>if(has_watchdog){
       
 10761 </xsl:text>
       
 10762           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
 10763 </xsl:text>
       
 10764           <xsl:text>    // Since dispatch directly calls change_hmi_value,
       
 10765 </xsl:text>
       
 10766           <xsl:text>    // PLC will periodically send variable at given frequency
       
 10767 </xsl:text>
       
 10768           <xsl:text>    subscribers(heartbeat_index).add({
       
 10769 </xsl:text>
       
 10770           <xsl:text>        /* type: "Watchdog", */
       
 10771 </xsl:text>
       
 10772           <xsl:text>        frequency: 1,
       
 10773 </xsl:text>
       
 10774           <xsl:text>        indexes: [heartbeat_index],
       
 10775 </xsl:text>
       
 10776           <xsl:text>        new_hmi_value: function(index, value, oldval) {
       
 10777 </xsl:text>
       
 10778           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
       
 10779 </xsl:text>
       
 10780           <xsl:text>        }
       
 10781 </xsl:text>
       
 10782           <xsl:text>    });
       
 10783 </xsl:text>
       
 10784           <xsl:text>}
       
 10785 </xsl:text>
       
 10786           <xsl:text>
       
 10787 </xsl:text>
       
 10788           <xsl:text>
       
 10789 </xsl:text>
       
 10790           <xsl:text>var page_fading_in_progress = false;
       
 10791 </xsl:text>
       
 10792           <xsl:text>function fading_page_switch(...args){
       
 10793 </xsl:text>
       
 10794           <xsl:text>    svg_root.classList.add("fade-out-page");
       
 10795 </xsl:text>
       
 10796           <xsl:text>    page_fading_in_progress = true;
       
 10797 </xsl:text>
       
 10798           <xsl:text>
       
 10799 </xsl:text>
       
 10800           <xsl:text>    setTimeout(function(){
       
 10801 </xsl:text>
       
 10802           <xsl:text>        switch_page(...args);
       
 10803 </xsl:text>
       
 10804           <xsl:text>    },1);
       
 10805 </xsl:text>
       
 10806           <xsl:text>}
       
 10807 </xsl:text>
       
 10808           <xsl:text>document.body.style.backgroundColor = "black";
       
 10809 </xsl:text>
       
 10810           <xsl:text>
       
 10811 </xsl:text>
       
 10812           <xsl:text>// subscribe to per instance current page hmi variable
       
 10813 </xsl:text>
       
 10814           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
       
 10815 </xsl:text>
       
 10816           <xsl:text>subscribers(current_page_var_index).add({
       
 10817 </xsl:text>
       
 10818           <xsl:text>    frequency: 1,
       
 10819 </xsl:text>
       
 10820           <xsl:text>    indexes: [current_page_var_index],
       
 10821 </xsl:text>
       
 10822           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
 10823 </xsl:text>
       
 10824           <xsl:text>        if(value.startsWith("!"))
       
 10825 </xsl:text>
       
 10826           <xsl:text>            fading_page_switch(value.slice(1));
       
 10827 </xsl:text>
       
 10828           <xsl:text>    }
       
 10829 </xsl:text>
       
 10830           <xsl:text>});
       
 10831 </xsl:text>
       
 10832           <xsl:text>
       
 10833 </xsl:text>
       
 10834           <xsl:text>function svg_text_to_multiline(elt) {
       
 10835 </xsl:text>
       
 10836           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
       
 10837 </xsl:text>
       
 10838           <xsl:text>}
       
 10839 </xsl:text>
       
 10840           <xsl:text>
       
 10841 </xsl:text>
       
 10842           <xsl:text>function multiline_to_svg_text(elt, str, blank) {
       
 10843 </xsl:text>
       
 10844           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = blank?"":line;});
       
 10845 </xsl:text>
       
 10846           <xsl:text>}
       
 10847 </xsl:text>
       
 10848           <xsl:text>
       
 10849 </xsl:text>
       
 10850           <xsl:text>function switch_langnum(langnum) {
       
 10851 </xsl:text>
       
 10852           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
 10853 </xsl:text>
       
 10854           <xsl:text>
       
 10855 </xsl:text>
       
 10856           <xsl:text>    for (let translation of translations) {
       
 10857 </xsl:text>
       
 10858           <xsl:text>        let [objs, msgs] = translation;
       
 10859 </xsl:text>
       
 10860           <xsl:text>        let msg = msgs[langnum];
       
 10861 </xsl:text>
       
 10862           <xsl:text>        for (let obj of objs) {
       
 10863 </xsl:text>
       
 10864           <xsl:text>            multiline_to_svg_text(obj, msg);
       
 10865 </xsl:text>
       
 10866           <xsl:text>            obj.setAttribute("lang",langnum);
       
 10867 </xsl:text>
       
 10868           <xsl:text>        }
       
 10869 </xsl:text>
       
 10870           <xsl:text>    }
       
 10871 </xsl:text>
       
 10872           <xsl:text>    return langnum;
       
 10873 </xsl:text>
       
 10874           <xsl:text>}
       
 10875 </xsl:text>
       
 10876           <xsl:text>
       
 10877 </xsl:text>
       
 10878           <xsl:text>// backup original texts
       
 10879 </xsl:text>
       
 10880           <xsl:text>for (let translation of translations) {
       
 10881 </xsl:text>
       
 10882           <xsl:text>    let [objs, msgs] = translation;
       
 10883 </xsl:text>
       
 10884           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
 10885 </xsl:text>
       
 10886           <xsl:text>}
       
 10887 </xsl:text>
       
 10888           <xsl:text>
       
 10889 </xsl:text>
       
 10890           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
 10891 </xsl:text>
       
 10892           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
 10893 </xsl:text>
       
 10894           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
 10895 </xsl:text>
       
 10896           <xsl:text>subscribers(lang_local_index).add({
       
 10897 </xsl:text>
       
 10898           <xsl:text>    indexes: [lang_local_index],
       
 10899 </xsl:text>
       
 10900           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
 10901 </xsl:text>
       
 10902           <xsl:text>        let current_lang =  switch_langnum(value);
       
 10903 </xsl:text>
       
 10904           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
 10905 </xsl:text>
       
 10906           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
 10907 </xsl:text>
       
 10908           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
 10909 </xsl:text>
       
 10910           <xsl:text>        switch_page();
       
 10911 </xsl:text>
       
 10912           <xsl:text>    }
       
 10913 </xsl:text>
       
 10914           <xsl:text>});
       
 10915 </xsl:text>
       
 10916           <xsl:text>
       
 10917 </xsl:text>
       
 10918           <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language
       
 10919 </xsl:text>
       
 10920           <xsl:text>function get_current_lang_code(){
       
 10921 </xsl:text>
       
 10922           <xsl:text>    return cache[langcode_local_index];
       
 10923 </xsl:text>
       
 10924           <xsl:text>}
       
 10925 </xsl:text>
       
 10926           <xsl:text>
       
 10927 </xsl:text>
       
 10928           <xsl:text>function setup_lang(){
       
 10929 </xsl:text>
       
 10930           <xsl:text>    let current_lang = cache[lang_local_index];
       
 10931 </xsl:text>
       
 10932           <xsl:text>    let new_lang = switch_langnum(current_lang);
       
 10933 </xsl:text>
       
 10934           <xsl:text>    if(current_lang != new_lang){
       
 10935 </xsl:text>
       
 10936           <xsl:text>        apply_hmi_value(lang_local_index, new_lang);
       
 10937 </xsl:text>
       
 10938           <xsl:text>    }
       
 10939 </xsl:text>
       
 10940           <xsl:text>}
       
 10941 </xsl:text>
       
 10942           <xsl:text>
       
 10943 </xsl:text>
       
 10944           <xsl:text>setup_lang();
       
 10945 </xsl:text>
       
 10946           <xsl:text>
       
 10947 </xsl:text>
       
 10948           <xsl:text>function update_subscriptions() {
       
 10949 </xsl:text>
       
 10950           <xsl:text>    let delta = [];
       
 10951 </xsl:text>
       
 10952           <xsl:text>    for(let index in subscriptions){
       
 10953 </xsl:text>
       
 10954           <xsl:text>        let widgets = subscribers(index);
       
 10955 </xsl:text>
       
 10956           <xsl:text>
       
 10957 </xsl:text>
       
 10958           <xsl:text>        // periods are in ms
       
 10959 </xsl:text>
       
 10960           <xsl:text>        let previous_period = get_subscription_period(index);
       
 10961 </xsl:text>
       
 10962           <xsl:text>
       
 10963 </xsl:text>
       
 10964           <xsl:text>        // subscribing with a zero period is unsubscribing
       
 10965 </xsl:text>
       
 10966           <xsl:text>        let new_period = 0;
       
 10967 </xsl:text>
       
 10968           <xsl:text>        if(widgets.size &gt; 0) {
       
 10969 </xsl:text>
       
 10970           <xsl:text>            let maxfreq = 0;
       
 10971 </xsl:text>
       
 10972           <xsl:text>            for(let widget of widgets){
       
 10973 </xsl:text>
       
 10974           <xsl:text>                let wf = widget.frequency;
       
 10975 </xsl:text>
       
 10976           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
 10977 </xsl:text>
       
 10978           <xsl:text>                    maxfreq = wf;
       
 10979 </xsl:text>
       
 10980           <xsl:text>            }
       
 10981 </xsl:text>
       
 10982           <xsl:text>
       
 10983 </xsl:text>
       
 10984           <xsl:text>            if(maxfreq != 0)
       
 10985 </xsl:text>
       
 10986           <xsl:text>                new_period = 1000/maxfreq;
       
 10987 </xsl:text>
       
 10988           <xsl:text>        }
       
 10989 </xsl:text>
       
 10990           <xsl:text>
       
 10991 </xsl:text>
       
 10992           <xsl:text>        if(previous_period != new_period) {
       
 10993 </xsl:text>
       
 10994           <xsl:text>            set_subscription_period(index, new_period);
       
 10995 </xsl:text>
       
 10996           <xsl:text>            if(index &lt;= last_remote_index){
       
 10997 </xsl:text>
       
 10998           <xsl:text>                delta.push(
       
 10999 </xsl:text>
       
 11000           <xsl:text>                    new Uint8Array([2]), /* subscribe = 2 */
       
 11001 </xsl:text>
       
 11002           <xsl:text>                    new Uint32Array([index]),
       
 11003 </xsl:text>
       
 11004           <xsl:text>                    new Uint16Array([new_period]));
       
 11005 </xsl:text>
       
 11006           <xsl:text>            }
       
 11007 </xsl:text>
       
 11008           <xsl:text>        }
       
 11009 </xsl:text>
       
 11010           <xsl:text>    }
       
 11011 </xsl:text>
       
 11012           <xsl:text>    send_blob(delta);
       
 11013 </xsl:text>
 11576 </xsl:text>
 11014           <xsl:text>};
 11577           <xsl:text>};
 11015 </xsl:text>
 11578 </xsl:text>
 11016           <xsl:text>
 11579           <xsl:text>
 11017 </xsl:text>
 11580 </xsl:text>
 11018           <xsl:text>function send_hmi_value(index, value) {
 11581           <xsl:text>var current_modal; /* TODO stack ?*/
 11019 </xsl:text>
 11582 </xsl:text>
 11020           <xsl:text>    if(index &gt; last_remote_index){
 11583           <xsl:text>
 11021 </xsl:text>
 11584 </xsl:text>
 11022           <xsl:text>        updates.set(index, value);
 11585           <xsl:text>function show_modal() {
 11023 </xsl:text>
 11586 </xsl:text>
 11024           <xsl:text>
 11587           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
 11025 </xsl:text>
 11588 </xsl:text>
 11026           <xsl:text>        if(persistent_indexes.has(index)){
 11589           <xsl:text>
 11027 </xsl:text>
 11590 </xsl:text>
 11028           <xsl:text>            let varname = persistent_indexes.get(index);
 11591           <xsl:text>    tmpgrp = document.createElementNS(xmlns,"g");
 11029 </xsl:text>
 11592 </xsl:text>
 11030           <xsl:text>            document.cookie = varname+"="+value+"; max-age=3153600000";
 11593           <xsl:text>    tmpgrpattr = document.createAttribute("transform");
 11031 </xsl:text>
 11594 </xsl:text>
 11032           <xsl:text>        }
 11595           <xsl:text>    let [xcoord,ycoord] = this.coordinates;
 11033 </xsl:text>
 11596 </xsl:text>
 11034           <xsl:text>
 11597           <xsl:text>    let [xdest,ydest] = page_desc[current_visible_page].bbox;
 11035 </xsl:text>
 11598 </xsl:text>
 11036           <xsl:text>        requestHMIAnimation();
 11599           <xsl:text>    tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")";
 11037 </xsl:text>
 11600 </xsl:text>
 11038           <xsl:text>        return;
 11601           <xsl:text>
 11039 </xsl:text>
 11602 </xsl:text>
 11040           <xsl:text>    }
 11603           <xsl:text>    tmpgrp.setAttributeNode(tmpgrpattr);
 11041 </xsl:text>
 11604 </xsl:text>
 11042           <xsl:text>
 11605           <xsl:text>
 11043 </xsl:text>
 11606 </xsl:text>
 11044           <xsl:text>    let iectype = hmitree_types[index];
 11607           <xsl:text>    tmpgrp.appendChild(element);
 11045 </xsl:text>
 11608 </xsl:text>
 11046           <xsl:text>    let tobinary = typedarray_types[iectype];
 11609           <xsl:text>    parent.appendChild(tmpgrp);
 11047 </xsl:text>
 11610 </xsl:text>
 11048           <xsl:text>    send_blob([
 11611           <xsl:text>
 11049 </xsl:text>
 11612 </xsl:text>
 11050           <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
 11613           <xsl:text>    current_modal = [this.element.id, tmpgrp];
 11051 </xsl:text>
       
 11052           <xsl:text>        new Uint32Array([index]),
       
 11053 </xsl:text>
       
 11054           <xsl:text>        tobinary(value)]);
       
 11055 </xsl:text>
       
 11056           <xsl:text>
       
 11057 </xsl:text>
       
 11058           <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
 11059 </xsl:text>
       
 11060           <xsl:text>    // cache[index] = value;
       
 11061 </xsl:text>
 11614 </xsl:text>
 11062           <xsl:text>};
 11615           <xsl:text>};
 11063 </xsl:text>
 11616 </xsl:text>
 11064           <xsl:text>
 11617           <xsl:text>
 11065 </xsl:text>
 11618 </xsl:text>
 11066           <xsl:text>function apply_hmi_value(index, new_val) {
 11619           <xsl:text>function end_modal() {
 11067 </xsl:text>
 11620 </xsl:text>
 11068           <xsl:text>    // Similarly to previous comment, taking decision to update based 
 11621           <xsl:text>    let [eltid, tmpgrp] = current_modal;
 11069 </xsl:text>
 11622 </xsl:text>
 11070           <xsl:text>    // on cache content is bad and can lead to inconsistency
 11623           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
 11071 </xsl:text>
 11624 </xsl:text>
 11072           <xsl:text>    /*let old_val = cache[index];*/
 11625           <xsl:text>
 11073 </xsl:text>
 11626 </xsl:text>
 11074           <xsl:text>    if(new_val != undefined /*&amp;&amp; old_val != new_val*/)
 11627           <xsl:text>    parent.removeChild(tmpgrp);
 11075 </xsl:text>
 11628 </xsl:text>
 11076           <xsl:text>        send_hmi_value(index, new_val);
 11629           <xsl:text>
 11077 </xsl:text>
 11630 </xsl:text>
 11078           <xsl:text>    return new_val;
 11631           <xsl:text>    current_modal = undefined;
 11079 </xsl:text>
       
 11080           <xsl:text>}
       
 11081 </xsl:text>
       
 11082           <xsl:text>
       
 11083 </xsl:text>
       
 11084           <xsl:text>const quotes = {"'":null, '"':null};
       
 11085 </xsl:text>
       
 11086           <xsl:text>
       
 11087 </xsl:text>
       
 11088           <xsl:text>function eval_operation_string(old_val, opstr) {
       
 11089 </xsl:text>
       
 11090           <xsl:text>    let op = opstr[0];
       
 11091 </xsl:text>
       
 11092           <xsl:text>    let given_val;
       
 11093 </xsl:text>
       
 11094           <xsl:text>    if(opstr.length &lt; 2) 
       
 11095 </xsl:text>
       
 11096           <xsl:text>        return undefined;
       
 11097 </xsl:text>
       
 11098           <xsl:text>    if(opstr[1] in quotes){
       
 11099 </xsl:text>
       
 11100           <xsl:text>        if(opstr.length &lt; 3) 
       
 11101 </xsl:text>
       
 11102           <xsl:text>            return undefined;
       
 11103 </xsl:text>
       
 11104           <xsl:text>        if(opstr[opstr.length-1] == opstr[1]){
       
 11105 </xsl:text>
       
 11106           <xsl:text>            given_val = opstr.slice(2,opstr.length-1);
       
 11107 </xsl:text>
       
 11108           <xsl:text>        }
       
 11109 </xsl:text>
       
 11110           <xsl:text>    } else {
       
 11111 </xsl:text>
       
 11112           <xsl:text>        given_val = Number(opstr.slice(1));
       
 11113 </xsl:text>
       
 11114           <xsl:text>    }
       
 11115 </xsl:text>
       
 11116           <xsl:text>    let new_val;
       
 11117 </xsl:text>
       
 11118           <xsl:text>    switch(op){
       
 11119 </xsl:text>
       
 11120           <xsl:text>      case "=":
       
 11121 </xsl:text>
       
 11122           <xsl:text>        new_val = given_val;
       
 11123 </xsl:text>
       
 11124           <xsl:text>        break;
       
 11125 </xsl:text>
       
 11126           <xsl:text>      case "+":
       
 11127 </xsl:text>
       
 11128           <xsl:text>        new_val = old_val + given_val;
       
 11129 </xsl:text>
       
 11130           <xsl:text>        break;
       
 11131 </xsl:text>
       
 11132           <xsl:text>      case "-":
       
 11133 </xsl:text>
       
 11134           <xsl:text>        new_val = old_val - given_val;
       
 11135 </xsl:text>
       
 11136           <xsl:text>        break;
       
 11137 </xsl:text>
       
 11138           <xsl:text>      case "*":
       
 11139 </xsl:text>
       
 11140           <xsl:text>        new_val = old_val * given_val;
       
 11141 </xsl:text>
       
 11142           <xsl:text>        break;
       
 11143 </xsl:text>
       
 11144           <xsl:text>      case "/":
       
 11145 </xsl:text>
       
 11146           <xsl:text>        new_val = old_val / given_val;
       
 11147 </xsl:text>
       
 11148           <xsl:text>        break;
       
 11149 </xsl:text>
       
 11150           <xsl:text>    }
       
 11151 </xsl:text>
       
 11152           <xsl:text>    return new_val;
       
 11153 </xsl:text>
       
 11154           <xsl:text>}
       
 11155 </xsl:text>
       
 11156           <xsl:text>
       
 11157 </xsl:text>
       
 11158           <xsl:text>var current_visible_page;
       
 11159 </xsl:text>
       
 11160           <xsl:text>var current_subscribed_page;
       
 11161 </xsl:text>
       
 11162           <xsl:text>var current_page_index;
       
 11163 </xsl:text>
       
 11164           <xsl:text>var page_node_local_index = hmi_local_index("page_node");
       
 11165 </xsl:text>
       
 11166           <xsl:text>
       
 11167 </xsl:text>
       
 11168           <xsl:text>function toggleFullscreen() {
       
 11169 </xsl:text>
       
 11170           <xsl:text>  let elem = document.documentElement;
       
 11171 </xsl:text>
       
 11172           <xsl:text>
       
 11173 </xsl:text>
       
 11174           <xsl:text>  if (!document.fullscreenElement) {
       
 11175 </xsl:text>
       
 11176           <xsl:text>    elem.requestFullscreen().catch(err =&gt; {
       
 11177 </xsl:text>
       
 11178           <xsl:text>      console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")");
       
 11179 </xsl:text>
       
 11180           <xsl:text>    });
       
 11181 </xsl:text>
       
 11182           <xsl:text>  } else {
       
 11183 </xsl:text>
       
 11184           <xsl:text>    document.exitFullscreen();
       
 11185 </xsl:text>
       
 11186           <xsl:text>  }
       
 11187 </xsl:text>
       
 11188           <xsl:text>}
       
 11189 </xsl:text>
       
 11190           <xsl:text>
       
 11191 </xsl:text>
       
 11192           <xsl:text>function prepare_svg() {
       
 11193 </xsl:text>
       
 11194           <xsl:text>    // prevents context menu from appearing on right click and long touch
       
 11195 </xsl:text>
       
 11196           <xsl:text>    document.body.addEventListener('contextmenu', e =&gt; {
       
 11197 </xsl:text>
       
 11198           <xsl:text>        toggleFullscreen();
       
 11199 </xsl:text>
       
 11200           <xsl:text>        e.preventDefault();
       
 11201 </xsl:text>
       
 11202           <xsl:text>    });
       
 11203 </xsl:text>
       
 11204           <xsl:text>
       
 11205 </xsl:text>
       
 11206           <xsl:text>    for(let eltid in detachable_elements){
       
 11207 </xsl:text>
       
 11208           <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
 11209 </xsl:text>
       
 11210           <xsl:text>        parent.removeChild(element);
       
 11211 </xsl:text>
       
 11212           <xsl:text>    }
       
 11213 </xsl:text>
 11632 </xsl:text>
 11214           <xsl:text>};
 11633           <xsl:text>};
 11215 </xsl:text>
 11634 </xsl:text>
 11216           <xsl:text>
 11635           <xsl:text>
 11217 </xsl:text>
 11636 </xsl:text>
 11218           <xsl:text>function switch_page(page_name, page_index) {
 11637           <xsl:text>
 11219 </xsl:text>
 11638 //
 11220           <xsl:text>    if(current_subscribed_page != current_visible_page){
 11639 //
 11221 </xsl:text>
 11640 // Declarations from SVG scripts (inkscape document properties) 
 11222           <xsl:text>        /* page switch already going */
 11641 //
 11223 </xsl:text>
 11642 //
 11224           <xsl:text>        /* TODO LOG ERROR */
 11643 </xsl:text>
 11225 </xsl:text>
 11644           <xsl:for-each select="/svg:svg/svg:script">
 11226           <xsl:text>        return false;
 11645             <xsl:text>
 11227 </xsl:text>
 11646 </xsl:text>
 11228           <xsl:text>    }
 11647             <xsl:text>/* </xsl:text>
 11229 </xsl:text>
 11648             <xsl:value-of select="@id"/>
 11230           <xsl:text>
 11649             <xsl:text> */
 11231 </xsl:text>
 11650 </xsl:text>
 11232           <xsl:text>    if(page_name == undefined)
 11651             <xsl:value-of select="text()"/>
 11233 </xsl:text>
 11652             <xsl:text>
 11234           <xsl:text>        page_name = current_subscribed_page;
 11653 </xsl:text>
 11235 </xsl:text>
 11654           </xsl:for-each>
 11236           <xsl:text>    else if(page_index == undefined){
       
 11237 </xsl:text>
       
 11238           <xsl:text>        [page_name, page_index] = page_name.split('@')
       
 11239 </xsl:text>
       
 11240           <xsl:text>    }
       
 11241 </xsl:text>
       
 11242           <xsl:text>
       
 11243 </xsl:text>
       
 11244           <xsl:text>    let old_desc = page_desc[current_subscribed_page];
       
 11245 </xsl:text>
       
 11246           <xsl:text>    let new_desc = page_desc[page_name];
       
 11247 </xsl:text>
       
 11248           <xsl:text>
       
 11249 </xsl:text>
       
 11250           <xsl:text>    if(new_desc == undefined){
       
 11251 </xsl:text>
       
 11252           <xsl:text>        /* TODO LOG ERROR */
       
 11253 </xsl:text>
       
 11254           <xsl:text>        return false;
       
 11255 </xsl:text>
       
 11256           <xsl:text>    }
       
 11257 </xsl:text>
       
 11258           <xsl:text>
       
 11259 </xsl:text>
       
 11260           <xsl:text>    if(page_index == undefined)
       
 11261 </xsl:text>
       
 11262           <xsl:text>        page_index = new_desc.page_index;
       
 11263 </xsl:text>
       
 11264           <xsl:text>    else if(typeof(page_index) == "string") {
       
 11265 </xsl:text>
       
 11266           <xsl:text>        let hmitree_node = hmitree_nodes[page_index];
       
 11267 </xsl:text>
       
 11268           <xsl:text>        if(hmitree_node !== undefined){
       
 11269 </xsl:text>
       
 11270           <xsl:text>            let [int_index, hmiclass] = hmitree_node;
       
 11271 </xsl:text>
       
 11272           <xsl:text>            if(hmiclass == new_desc.page_class)
       
 11273 </xsl:text>
       
 11274           <xsl:text>                page_index = int_index;
       
 11275 </xsl:text>
       
 11276           <xsl:text>            else
       
 11277 </xsl:text>
       
 11278           <xsl:text>                page_index = new_desc.page_index;
       
 11279 </xsl:text>
       
 11280           <xsl:text>        } else {
       
 11281 </xsl:text>
       
 11282           <xsl:text>            page_index = new_desc.page_index;
       
 11283 </xsl:text>
       
 11284           <xsl:text>        }
       
 11285 </xsl:text>
       
 11286           <xsl:text>    }
       
 11287 </xsl:text>
       
 11288           <xsl:text>
       
 11289 </xsl:text>
       
 11290           <xsl:text>    if(old_desc){
       
 11291 </xsl:text>
       
 11292           <xsl:text>        old_desc.widgets.map(([widget,relativeness])=&gt;widget.unsub());
       
 11293 </xsl:text>
       
 11294           <xsl:text>    }
       
 11295 </xsl:text>
       
 11296           <xsl:text>    const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
       
 11297 </xsl:text>
       
 11298           <xsl:text>
       
 11299 </xsl:text>
       
 11300           <xsl:text>    const container_id = page_name + (page_index != undefined ? page_index : "");
       
 11301 </xsl:text>
       
 11302           <xsl:text>
       
 11303 </xsl:text>
       
 11304           <xsl:text>    new_desc.widgets.map(([widget,relativeness])=&gt;widget.sub(new_offset,relativeness,container_id));
       
 11305 </xsl:text>
       
 11306           <xsl:text>
       
 11307 </xsl:text>
       
 11308           <xsl:text>    update_subscriptions();
       
 11309 </xsl:text>
       
 11310           <xsl:text>
       
 11311 </xsl:text>
       
 11312           <xsl:text>    current_subscribed_page = page_name;
       
 11313 </xsl:text>
       
 11314           <xsl:text>    current_page_index = page_index;
       
 11315 </xsl:text>
       
 11316           <xsl:text>    let page_node;
       
 11317 </xsl:text>
       
 11318           <xsl:text>    if(page_index != undefined){
       
 11319 </xsl:text>
       
 11320           <xsl:text>        page_node = hmitree_paths[page_index];
       
 11321 </xsl:text>
       
 11322           <xsl:text>    }else{
       
 11323 </xsl:text>
       
 11324           <xsl:text>        page_node = "";
       
 11325 </xsl:text>
       
 11326           <xsl:text>    }
       
 11327 </xsl:text>
       
 11328           <xsl:text>    apply_hmi_value(page_node_local_index, page_node);
       
 11329 </xsl:text>
       
 11330           <xsl:text>
       
 11331 </xsl:text>
       
 11332           <xsl:text>    jumps_need_update = true;
       
 11333 </xsl:text>
       
 11334           <xsl:text>
       
 11335 </xsl:text>
       
 11336           <xsl:text>    requestHMIAnimation();
       
 11337 </xsl:text>
       
 11338           <xsl:text>    jump_history.push([page_name, page_index]);
       
 11339 </xsl:text>
       
 11340           <xsl:text>    if(jump_history.length &gt; 42)
       
 11341 </xsl:text>
       
 11342           <xsl:text>        jump_history.shift();
       
 11343 </xsl:text>
       
 11344           <xsl:text>
       
 11345 </xsl:text>
       
 11346           <xsl:text>    apply_hmi_value(current_page_var_index, page_index == undefined
       
 11347 </xsl:text>
       
 11348           <xsl:text>        ? page_name
       
 11349 </xsl:text>
       
 11350           <xsl:text>        : page_name + "@" + hmitree_paths[page_index]);
       
 11351 </xsl:text>
       
 11352           <xsl:text>
       
 11353 </xsl:text>
       
 11354           <xsl:text>    return true;
       
 11355 </xsl:text>
       
 11356           <xsl:text>};
       
 11357 </xsl:text>
       
 11358           <xsl:text>
       
 11359 </xsl:text>
       
 11360           <xsl:text>function switch_visible_page(page_name) {
       
 11361 </xsl:text>
       
 11362           <xsl:text>
       
 11363 </xsl:text>
       
 11364           <xsl:text>    let old_desc = page_desc[current_visible_page];
       
 11365 </xsl:text>
       
 11366           <xsl:text>    let new_desc = page_desc[page_name];
       
 11367 </xsl:text>
       
 11368           <xsl:text>
       
 11369 </xsl:text>
       
 11370           <xsl:text>    if(old_desc){
       
 11371 </xsl:text>
       
 11372           <xsl:text>        for(let eltid in old_desc.required_detachables){
       
 11373 </xsl:text>
       
 11374           <xsl:text>            if(!(eltid in new_desc.required_detachables)){
       
 11375 </xsl:text>
       
 11376           <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
       
 11377 </xsl:text>
       
 11378           <xsl:text>                parent.removeChild(element);
       
 11379 </xsl:text>
       
 11380           <xsl:text>            }
       
 11381 </xsl:text>
       
 11382           <xsl:text>        }
       
 11383 </xsl:text>
       
 11384           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
 11385 </xsl:text>
       
 11386           <xsl:text>            if(!(eltid in old_desc.required_detachables)){
       
 11387 </xsl:text>
       
 11388           <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
       
 11389 </xsl:text>
       
 11390           <xsl:text>                parent.appendChild(element);
       
 11391 </xsl:text>
       
 11392           <xsl:text>            }
       
 11393 </xsl:text>
       
 11394           <xsl:text>        }
       
 11395 </xsl:text>
       
 11396           <xsl:text>    }else{
       
 11397 </xsl:text>
       
 11398           <xsl:text>        for(let eltid in new_desc.required_detachables){
       
 11399 </xsl:text>
       
 11400           <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
       
 11401 </xsl:text>
       
 11402           <xsl:text>            parent.appendChild(element);
       
 11403 </xsl:text>
       
 11404           <xsl:text>        }
       
 11405 </xsl:text>
       
 11406           <xsl:text>    }
       
 11407 </xsl:text>
       
 11408           <xsl:text>
       
 11409 </xsl:text>
       
 11410           <xsl:text>    svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
       
 11411 </xsl:text>
       
 11412           <xsl:text>    if(page_fading_in_progress)
       
 11413 </xsl:text>
       
 11414           <xsl:text>        svg_root.classList.remove("fade-out-page");
       
 11415 </xsl:text>
       
 11416           <xsl:text>        page_fading_in_progress = false;
       
 11417 </xsl:text>
       
 11418           <xsl:text>    current_visible_page = page_name;
       
 11419 </xsl:text>
       
 11420           <xsl:text>};
       
 11421 </xsl:text>
       
 11422           <xsl:text>
       
 11423 </xsl:text>
       
 11424           <xsl:text>// Once connection established
       
 11425 </xsl:text>
       
 11426           <xsl:text>ws.onopen = function (evt) {
       
 11427 </xsl:text>
       
 11428           <xsl:text>    init_widgets();
       
 11429 </xsl:text>
       
 11430           <xsl:text>    send_reset();
       
 11431 </xsl:text>
       
 11432           <xsl:text>    // show main page
       
 11433 </xsl:text>
       
 11434           <xsl:text>    prepare_svg();
       
 11435 </xsl:text>
       
 11436           <xsl:text>    switch_page(default_page);
       
 11437 </xsl:text>
       
 11438           <xsl:text>};
       
 11439 </xsl:text>
       
 11440           <xsl:text>
       
 11441 </xsl:text>
       
 11442           <xsl:text>ws.onclose = function (evt) {
       
 11443 </xsl:text>
       
 11444           <xsl:text>    // TODO : add visible notification while waiting for reload
       
 11445 </xsl:text>
       
 11446           <xsl:text>    console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s.");
       
 11447 </xsl:text>
       
 11448           <xsl:text>    // TODO : re-enable auto reload when not in debug
       
 11449 </xsl:text>
       
 11450           <xsl:text>    //window.setTimeout(() =&gt; location.reload(true), 10000);
       
 11451 </xsl:text>
       
 11452           <xsl:text>    alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+".");
       
 11453 </xsl:text>
       
 11454           <xsl:text>
       
 11455 </xsl:text>
       
 11456           <xsl:text>};
       
 11457 </xsl:text>
       
 11458           <xsl:text>
       
 11459 </xsl:text>
       
 11460           <xsl:text>const xmlns = "http://www.w3.org/2000/svg";
       
 11461 </xsl:text>
       
 11462           <xsl:text>var edit_callback;
       
 11463 </xsl:text>
       
 11464           <xsl:text>const localtypes = {"PAGE_LOCAL":null, "HMI_LOCAL":null}
       
 11465 </xsl:text>
       
 11466           <xsl:text>function edit_value(path, valuetype, callback, initial) {
       
 11467 </xsl:text>
       
 11468           <xsl:text>    if(valuetype in localtypes){
       
 11469 </xsl:text>
       
 11470           <xsl:text>        valuetype = (typeof initial) == "number" ? "HMI_REAL" : "HMI_STRING";
       
 11471 </xsl:text>
       
 11472           <xsl:text>    }
       
 11473 </xsl:text>
       
 11474           <xsl:text>    let [keypadid, xcoord, ycoord] = keypads[valuetype];
       
 11475 </xsl:text>
       
 11476           <xsl:text>    edit_callback = callback;
       
 11477 </xsl:text>
       
 11478           <xsl:text>    let widget = hmi_widgets[keypadid];
       
 11479 </xsl:text>
       
 11480           <xsl:text>    widget.start_edit(path, valuetype, callback, initial);
       
 11481 </xsl:text>
       
 11482           <xsl:text>};
       
 11483 </xsl:text>
       
 11484           <xsl:text>
       
 11485 </xsl:text>
       
 11486           <xsl:text>var current_modal; /* TODO stack ?*/
       
 11487 </xsl:text>
       
 11488           <xsl:text>
       
 11489 </xsl:text>
       
 11490           <xsl:text>function show_modal() {
       
 11491 </xsl:text>
       
 11492           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
 11493 </xsl:text>
       
 11494           <xsl:text>
       
 11495 </xsl:text>
       
 11496           <xsl:text>    tmpgrp = document.createElementNS(xmlns,"g");
       
 11497 </xsl:text>
       
 11498           <xsl:text>    tmpgrpattr = document.createAttribute("transform");
       
 11499 </xsl:text>
       
 11500           <xsl:text>    let [xcoord,ycoord] = this.coordinates;
       
 11501 </xsl:text>
       
 11502           <xsl:text>    let [xdest,ydest] = page_desc[current_visible_page].bbox;
       
 11503 </xsl:text>
       
 11504           <xsl:text>    tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")";
       
 11505 </xsl:text>
       
 11506           <xsl:text>
       
 11507 </xsl:text>
       
 11508           <xsl:text>    tmpgrp.setAttributeNode(tmpgrpattr);
       
 11509 </xsl:text>
       
 11510           <xsl:text>
       
 11511 </xsl:text>
       
 11512           <xsl:text>    tmpgrp.appendChild(element);
       
 11513 </xsl:text>
       
 11514           <xsl:text>    parent.appendChild(tmpgrp);
       
 11515 </xsl:text>
       
 11516           <xsl:text>
       
 11517 </xsl:text>
       
 11518           <xsl:text>    current_modal = [this.element.id, tmpgrp];
       
 11519 </xsl:text>
       
 11520           <xsl:text>};
       
 11521 </xsl:text>
       
 11522           <xsl:text>
       
 11523 </xsl:text>
       
 11524           <xsl:text>function end_modal() {
       
 11525 </xsl:text>
       
 11526           <xsl:text>    let [eltid, tmpgrp] = current_modal;
       
 11527 </xsl:text>
       
 11528           <xsl:text>    let [element, parent] = detachable_elements[this.element.id];
       
 11529 </xsl:text>
       
 11530           <xsl:text>
       
 11531 </xsl:text>
       
 11532           <xsl:text>    parent.removeChild(tmpgrp);
       
 11533 </xsl:text>
       
 11534           <xsl:text>
       
 11535 </xsl:text>
       
 11536           <xsl:text>    current_modal = undefined;
       
 11537 </xsl:text>
       
 11538           <xsl:text>};
       
 11539 </xsl:text>
       
 11540           <xsl:text>
       
 11541 </xsl:text>
       
 11542         </script>
 11655         </script>
 11543       </body>
 11656       </body>
 11544     </html>
 11657     </html>
 11545   </xsl:template>
 11658   </xsl:template>
 11546 </xsl:stylesheet>
 11659 </xsl:stylesheet>