svghmi/gen_index_xhtml.xslt
branchsvghmi
changeset 3154 a8c81a080588
parent 3151 8e5d383a58cb
child 3155 99ce78ddd353
equal deleted inserted replaced
3153:671283404554 3154:a8c81a080588
     1 <?xml version="1.0"?>
     1 <?xml version="1.0"?>
     2 <xsl:stylesheet xmlns:ns="beremiz" xmlns:definitions="definitions" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:str="http://exslt.org/strings" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:exsl="http://exslt.org/common" xmlns:declarations="declarations" xmlns:debug="debug" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0">
     2 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:debug="debug" xmlns:preamble="preamble" xmlns:declarations="declarations" xmlns:definitions="definitions" xmlns:epilogue="epilogue" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions">
     3   <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
     3   <xsl:output cdata-section-elements="xhtml:script" method="xml"/>
     4   <xsl:variable name="svg" select="/svg:svg"/>
     4   <xsl:variable name="svg" select="/svg:svg"/>
     5   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     5   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     6   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     6   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     7   <xsl:variable name="_categories">
     7   <xsl:variable name="_categories">
     8     <noindex>
     8     <noindex>
  1208 </xsl:text>
  1208 </xsl:text>
  1209     <xsl:text>var persistent_indexes = new Map();
  1209     <xsl:text>var persistent_indexes = new Map();
  1210 </xsl:text>
  1210 </xsl:text>
  1211     <xsl:text>var cache = hmitree_types.map(_ignored =&gt; undefined);
  1211     <xsl:text>var cache = hmitree_types.map(_ignored =&gt; undefined);
  1212 </xsl:text>
  1212 </xsl:text>
  1213     <xsl:text>var updates = {};
  1213     <xsl:text>var updates = new Map();
  1214 </xsl:text>
  1214 </xsl:text>
  1215     <xsl:text>
  1215     <xsl:text>
  1216 </xsl:text>
  1216 </xsl:text>
  1217     <xsl:text>function page_local_index(varname, pagename){
  1217     <xsl:text>function page_local_index(varname, pagename){
  1218 </xsl:text>
  1218 </xsl:text>
  1248 </xsl:text>
  1248 </xsl:text>
  1249     <xsl:text>    if(defaultval != undefined) {
  1249     <xsl:text>    if(defaultval != undefined) {
  1250 </xsl:text>
  1250 </xsl:text>
  1251     <xsl:text>        cache[new_index] = defaultval; 
  1251     <xsl:text>        cache[new_index] = defaultval; 
  1252 </xsl:text>
  1252 </xsl:text>
  1253     <xsl:text>        updates[new_index] = defaultval;
  1253     <xsl:text>        updates.set(new_index, defaultval);
  1254 </xsl:text>
  1254 </xsl:text>
  1255     <xsl:text>        if(persistent_locals.has(varname))
  1255     <xsl:text>        if(persistent_locals.has(varname))
  1256 </xsl:text>
  1256 </xsl:text>
  1257     <xsl:text>            persistent_indexes.set(new_index, varname);
  1257     <xsl:text>            persistent_indexes.set(new_index, varname);
  1258 </xsl:text>
  1258 </xsl:text>
  1669 </xsl:text>
  1669 </xsl:text>
  1670     <xsl:text>
  1670     <xsl:text>
  1671 </xsl:text>
  1671 </xsl:text>
  1672   </xsl:template>
  1672   </xsl:template>
  1673   <xsl:variable name="excluded_types" select="str:split('Page VarInit VarInitPersistent')"/>
  1673   <xsl:variable name="excluded_types" select="str:split('Page VarInit VarInitPersistent')"/>
  1674   <xsl:key use="@type" name="TypesKey" match="widget"/>
  1674   <xsl:key name="TypesKey" match="widget" use="@type"/>
  1675   <declarations:hmi-classes/>
  1675   <declarations:hmi-classes/>
  1676   <xsl:template match="declarations:hmi-classes">
  1676   <xsl:template match="declarations:hmi-classes">
  1677     <xsl:text>
  1677     <xsl:text>
  1678 </xsl:text>
  1678 </xsl:text>
  1679     <xsl:text>/* </xsl:text>
  1679     <xsl:text>/* </xsl:text>
  4455     <xsl:if test="$have_edit">
  4455     <xsl:if test="$have_edit">
  4456       <xsl:text>        this.last_val = value;
  4456       <xsl:text>        this.last_val = value;
  4457 </xsl:text>
  4457 </xsl:text>
  4458     </xsl:if>
  4458     </xsl:if>
  4459     <xsl:if test="$have_value">
  4459     <xsl:if test="$have_value">
  4460       <xsl:text>        this.last_display = value;
  4460       <xsl:choose>
  4461 </xsl:text>
  4461         <xsl:when test="count(arg) = 1">
       
  4462           <xsl:text>        this.last_display = vsprintf("</xsl:text>
       
  4463           <xsl:value-of select="arg[1]/@value"/>
       
  4464           <xsl:text>", value);
       
  4465 </xsl:text>
       
  4466         </xsl:when>
       
  4467         <xsl:otherwise>
       
  4468           <xsl:text>        this.last_display = value;
       
  4469 </xsl:text>
       
  4470         </xsl:otherwise>
       
  4471       </xsl:choose>
  4462       <xsl:text>        this.request_animate();
  4472       <xsl:text>        this.request_animate();
  4463 </xsl:text>
  4473 </xsl:text>
  4464     </xsl:if>
  4474     </xsl:if>
  4465     <xsl:text>    },
  4475     <xsl:text>    },
  4466 </xsl:text>
  4476 </xsl:text>
  4479       <xsl:value-of select="path/@value"/>
  4489       <xsl:value-of select="path/@value"/>
  4480       <xsl:text>", "</xsl:text>
  4490       <xsl:text>", "</xsl:text>
  4481       <xsl:value-of select="path/@type"/>
  4491       <xsl:value-of select="path/@type"/>
  4482       <xsl:text>", this, this.last_val);
  4492       <xsl:text>", this, this.last_val);
  4483 </xsl:text>
  4493 </xsl:text>
       
  4494       <xsl:if test="$have_value">
       
  4495         <xsl:text>        this.value_elt.style.pointerEvents = "none";
       
  4496 </xsl:text>
       
  4497       </xsl:if>
  4484     </xsl:if>
  4498     </xsl:if>
  4485     <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
  4499     <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
  4486       <xsl:text>        id("</xsl:text>
  4500       <xsl:text>        id("</xsl:text>
  4487       <xsl:value-of select="@id"/>
  4501       <xsl:value-of select="@id"/>
  4488       <xsl:text>").onclick = () =&gt; this.on_op_click("</xsl:text>
  4502       <xsl:text>").onclick = () =&gt; this.on_op_click("</xsl:text>
  6750       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  6764       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  6751     </xsl:comment>
  6765     </xsl:comment>
  6752     <xsl:comment>
  6766     <xsl:comment>
  6753       <xsl:apply-templates select="document('')/*/debug:*"/>
  6767       <xsl:apply-templates select="document('')/*/debug:*"/>
  6754     </xsl:comment>
  6768     </xsl:comment>
  6755     <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
  6769     <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  6756       <head/>
  6770       <head/>
  6757       <body style="margin:0;overflow:hidden;user-select:none;touch-action:none;">
  6771       <body style="margin:0;overflow:hidden;user-select:none;touch-action:none;">
  6758         <xsl:copy-of select="$result_svg"/>
  6772         <xsl:copy-of select="$result_svg"/>
  6759         <script>
  6773         <script>
  6760           <xsl:text>
  6774           <xsl:text>
  6893 </xsl:text>
  6907 </xsl:text>
  6894           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
  6908           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
  6895 </xsl:text>
  6909 </xsl:text>
  6896           <xsl:text>function apply_updates() {
  6910           <xsl:text>function apply_updates() {
  6897 </xsl:text>
  6911 </xsl:text>
  6898           <xsl:text>    for(let index in updates){
  6912           <xsl:text>    updates.forEach((value, index) =&gt; {
  6899 </xsl:text>
  6913 </xsl:text>
  6900           <xsl:text>        // serving as a key, index becomes a string
  6914           <xsl:text>        dispatch_value(index, value);
  6901 </xsl:text>
  6915 </xsl:text>
  6902           <xsl:text>        // -&gt; pass Number(index) instead
  6916           <xsl:text>    });
  6903 </xsl:text>
  6917 </xsl:text>
  6904           <xsl:text>        dispatch_value(Number(index), updates[index]);
  6918           <xsl:text>    updates.clear();
  6905 </xsl:text>
  6919 </xsl:text>
  6906           <xsl:text>        delete updates[index];
  6920           <xsl:text>}
       
  6921 </xsl:text>
       
  6922           <xsl:text>
       
  6923 </xsl:text>
       
  6924           <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
  6925 </xsl:text>
       
  6926           <xsl:text>var requestAnimationFrameID = null;
       
  6927 </xsl:text>
       
  6928           <xsl:text>function animate() {
       
  6929 </xsl:text>
       
  6930           <xsl:text>    // Do the page swith if any one pending
       
  6931 </xsl:text>
       
  6932           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  6933 </xsl:text>
       
  6934           <xsl:text>        switch_visible_page(current_subscribed_page);
  6907 </xsl:text>
  6935 </xsl:text>
  6908           <xsl:text>    }
  6936           <xsl:text>    }
  6909 </xsl:text>
  6937 </xsl:text>
       
  6938           <xsl:text>
       
  6939 </xsl:text>
       
  6940           <xsl:text>    while(widget = need_cache_apply.pop()){
       
  6941 </xsl:text>
       
  6942           <xsl:text>        widget.apply_cache();
       
  6943 </xsl:text>
       
  6944           <xsl:text>    }
       
  6945 </xsl:text>
       
  6946           <xsl:text>
       
  6947 </xsl:text>
       
  6948           <xsl:text>    if(jumps_need_update) update_jumps();
       
  6949 </xsl:text>
       
  6950           <xsl:text>
       
  6951 </xsl:text>
       
  6952           <xsl:text>    apply_updates();
       
  6953 </xsl:text>
       
  6954           <xsl:text>
       
  6955 </xsl:text>
       
  6956           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
       
  6957 </xsl:text>
       
  6958           <xsl:text>    pending_widget_animates = [];
       
  6959 </xsl:text>
       
  6960           <xsl:text>
       
  6961 </xsl:text>
       
  6962           <xsl:text>    requestAnimationFrameID = null;
       
  6963 </xsl:text>
  6910           <xsl:text>}
  6964           <xsl:text>}
  6911 </xsl:text>
  6965 </xsl:text>
  6912           <xsl:text>
  6966           <xsl:text>
  6913 </xsl:text>
  6967 </xsl:text>
  6914           <xsl:text>// Called on requestAnimationFrame, modifies DOM
  6968           <xsl:text>function requestHMIAnimation() {
  6915 </xsl:text>
  6969 </xsl:text>
  6916           <xsl:text>var requestAnimationFrameID = null;
  6970           <xsl:text>    if(requestAnimationFrameID == null){
  6917 </xsl:text>
  6971 </xsl:text>
  6918           <xsl:text>function animate() {
  6972           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
  6919 </xsl:text>
       
  6920           <xsl:text>    // Do the page swith if any one pending
       
  6921 </xsl:text>
       
  6922           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  6923 </xsl:text>
       
  6924           <xsl:text>        switch_visible_page(current_subscribed_page);
       
  6925 </xsl:text>
  6973 </xsl:text>
  6926           <xsl:text>    }
  6974           <xsl:text>    }
  6927 </xsl:text>
  6975 </xsl:text>
  6928           <xsl:text>
  6976           <xsl:text>}
  6929 </xsl:text>
  6977 </xsl:text>
  6930           <xsl:text>    while(widget = need_cache_apply.pop()){
  6978           <xsl:text>
  6931 </xsl:text>
  6979 </xsl:text>
  6932           <xsl:text>        widget.apply_cache();
  6980           <xsl:text>// Message reception handler
       
  6981 </xsl:text>
       
  6982           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  6983 </xsl:text>
       
  6984           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  6985 </xsl:text>
       
  6986           <xsl:text>ws.onmessage = function (evt) {
       
  6987 </xsl:text>
       
  6988           <xsl:text>
       
  6989 </xsl:text>
       
  6990           <xsl:text>    let data = evt.data;
       
  6991 </xsl:text>
       
  6992           <xsl:text>    let dv = new DataView(data);
       
  6993 </xsl:text>
       
  6994           <xsl:text>    let i = 0;
       
  6995 </xsl:text>
       
  6996           <xsl:text>    try {
       
  6997 </xsl:text>
       
  6998           <xsl:text>        for(let hash_int of hmi_hash) {
       
  6999 </xsl:text>
       
  7000           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  7001 </xsl:text>
       
  7002           <xsl:text>                throw new Error("Hash doesn't match");
       
  7003 </xsl:text>
       
  7004           <xsl:text>            };
       
  7005 </xsl:text>
       
  7006           <xsl:text>            i++;
       
  7007 </xsl:text>
       
  7008           <xsl:text>        };
       
  7009 </xsl:text>
       
  7010           <xsl:text>
       
  7011 </xsl:text>
       
  7012           <xsl:text>        while(i &lt; data.byteLength){
       
  7013 </xsl:text>
       
  7014           <xsl:text>            let index = dv.getUint32(i, true);
       
  7015 </xsl:text>
       
  7016           <xsl:text>            i += 4;
       
  7017 </xsl:text>
       
  7018           <xsl:text>            let iectype = hmitree_types[index];
       
  7019 </xsl:text>
       
  7020           <xsl:text>            if(iectype != undefined){
       
  7021 </xsl:text>
       
  7022           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  7023 </xsl:text>
       
  7024           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  7025 </xsl:text>
       
  7026           <xsl:text>                updates.set(index, value);
       
  7027 </xsl:text>
       
  7028           <xsl:text>                i += bytesize;
       
  7029 </xsl:text>
       
  7030           <xsl:text>            } else {
       
  7031 </xsl:text>
       
  7032           <xsl:text>                throw new Error("Unknown index "+index);
       
  7033 </xsl:text>
       
  7034           <xsl:text>            }
       
  7035 </xsl:text>
       
  7036           <xsl:text>        };
       
  7037 </xsl:text>
       
  7038           <xsl:text>        // register for rendering on next frame, since there are updates
       
  7039 </xsl:text>
       
  7040           <xsl:text>        requestHMIAnimation();
       
  7041 </xsl:text>
       
  7042           <xsl:text>    } catch(err) {
       
  7043 </xsl:text>
       
  7044           <xsl:text>        // 1003 is for "Unsupported Data"
       
  7045 </xsl:text>
       
  7046           <xsl:text>        // ws.close(1003, err.message);
       
  7047 </xsl:text>
       
  7048           <xsl:text>
       
  7049 </xsl:text>
       
  7050           <xsl:text>        // TODO : remove debug alert ?
       
  7051 </xsl:text>
       
  7052           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  7053 </xsl:text>
       
  7054           <xsl:text>
       
  7055 </xsl:text>
       
  7056           <xsl:text>        // force reload ignoring cache
       
  7057 </xsl:text>
       
  7058           <xsl:text>        location.reload(true);
  6933 </xsl:text>
  7059 </xsl:text>
  6934           <xsl:text>    }
  7060           <xsl:text>    }
  6935 </xsl:text>
  7061 </xsl:text>
  6936           <xsl:text>
  7062           <xsl:text>};
  6937 </xsl:text>
  7063 </xsl:text>
  6938           <xsl:text>    if(jumps_need_update) update_jumps();
  7064           <xsl:text>
  6939 </xsl:text>
  7065 </xsl:text>
  6940           <xsl:text>
  7066           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
  6941 </xsl:text>
  7067 </xsl:text>
  6942           <xsl:text>    apply_updates();
  7068           <xsl:text>
  6943 </xsl:text>
  7069 </xsl:text>
  6944           <xsl:text>
  7070           <xsl:text>function send_blob(data) {
  6945 </xsl:text>
  7071 </xsl:text>
  6946           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
  7072           <xsl:text>    if(data.length &gt; 0) {
  6947 </xsl:text>
  7073 </xsl:text>
  6948           <xsl:text>    pending_widget_animates = [];
  7074           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
  6949 </xsl:text>
  7075 </xsl:text>
  6950           <xsl:text>
  7076           <xsl:text>    };
  6951 </xsl:text>
  7077 </xsl:text>
  6952           <xsl:text>    requestAnimationFrameID = null;
  7078           <xsl:text>};
       
  7079 </xsl:text>
       
  7080           <xsl:text>
       
  7081 </xsl:text>
       
  7082           <xsl:text>const typedarray_types = {
       
  7083 </xsl:text>
       
  7084           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  7085 </xsl:text>
       
  7086           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  7087 </xsl:text>
       
  7088           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  7089 </xsl:text>
       
  7090           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
  7091 </xsl:text>
       
  7092           <xsl:text>    STRING: (str) =&gt; {
       
  7093 </xsl:text>
       
  7094           <xsl:text>        // beremiz default string max size is 128
       
  7095 </xsl:text>
       
  7096           <xsl:text>        str = str.slice(0,128);
       
  7097 </xsl:text>
       
  7098           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  7099 </xsl:text>
       
  7100           <xsl:text>        binary[0] = str.length;
       
  7101 </xsl:text>
       
  7102           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
  7103 </xsl:text>
       
  7104           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  7105 </xsl:text>
       
  7106           <xsl:text>        }
       
  7107 </xsl:text>
       
  7108           <xsl:text>        return binary;
       
  7109 </xsl:text>
       
  7110           <xsl:text>    }
       
  7111 </xsl:text>
       
  7112           <xsl:text>    /* TODO */
       
  7113 </xsl:text>
       
  7114           <xsl:text>};
       
  7115 </xsl:text>
       
  7116           <xsl:text>
       
  7117 </xsl:text>
       
  7118           <xsl:text>function send_reset() {
       
  7119 </xsl:text>
       
  7120           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  7121 </xsl:text>
       
  7122           <xsl:text>};
       
  7123 </xsl:text>
       
  7124           <xsl:text>
       
  7125 </xsl:text>
       
  7126           <xsl:text>var subscriptions = [];
       
  7127 </xsl:text>
       
  7128           <xsl:text>
       
  7129 </xsl:text>
       
  7130           <xsl:text>function subscribers(index) {
       
  7131 </xsl:text>
       
  7132           <xsl:text>    let entry = subscriptions[index];
       
  7133 </xsl:text>
       
  7134           <xsl:text>    let res;
       
  7135 </xsl:text>
       
  7136           <xsl:text>    if(entry == undefined){
       
  7137 </xsl:text>
       
  7138           <xsl:text>        res = new Set();
       
  7139 </xsl:text>
       
  7140           <xsl:text>        subscriptions[index] = [res,0];
       
  7141 </xsl:text>
       
  7142           <xsl:text>    }else{
       
  7143 </xsl:text>
       
  7144           <xsl:text>        [res, _ign] = entry;
       
  7145 </xsl:text>
       
  7146           <xsl:text>    }
       
  7147 </xsl:text>
       
  7148           <xsl:text>    return res
  6953 </xsl:text>
  7149 </xsl:text>
  6954           <xsl:text>}
  7150           <xsl:text>}
  6955 </xsl:text>
  7151 </xsl:text>
  6956           <xsl:text>
  7152           <xsl:text>
  6957 </xsl:text>
  7153 </xsl:text>
  6958           <xsl:text>function requestHMIAnimation() {
  7154           <xsl:text>function get_subscription_period(index) {
  6959 </xsl:text>
  7155 </xsl:text>
  6960           <xsl:text>    if(requestAnimationFrameID == null){
  7156           <xsl:text>    let entry = subscriptions[index];
  6961 </xsl:text>
  7157 </xsl:text>
  6962           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
  7158           <xsl:text>    if(entry == undefined)
       
  7159 </xsl:text>
       
  7160           <xsl:text>        return 0;
       
  7161 </xsl:text>
       
  7162           <xsl:text>    let [_ign, period] = entry;
       
  7163 </xsl:text>
       
  7164           <xsl:text>    return period;
       
  7165 </xsl:text>
       
  7166           <xsl:text>}
       
  7167 </xsl:text>
       
  7168           <xsl:text>
       
  7169 </xsl:text>
       
  7170           <xsl:text>function set_subscription_period(index, period) {
       
  7171 </xsl:text>
       
  7172           <xsl:text>    let entry = subscriptions[index];
       
  7173 </xsl:text>
       
  7174           <xsl:text>    if(entry == undefined){
       
  7175 </xsl:text>
       
  7176           <xsl:text>        subscriptions[index] = [new Set(), period];
       
  7177 </xsl:text>
       
  7178           <xsl:text>    } else {
       
  7179 </xsl:text>
       
  7180           <xsl:text>        entry[1] = period;
  6963 </xsl:text>
  7181 </xsl:text>
  6964           <xsl:text>    }
  7182           <xsl:text>    }
  6965 </xsl:text>
  7183 </xsl:text>
  6966           <xsl:text>}
  7184           <xsl:text>}
  6967 </xsl:text>
  7185 </xsl:text>
  6968           <xsl:text>
  7186           <xsl:text>
  6969 </xsl:text>
  7187 </xsl:text>
  6970           <xsl:text>// Message reception handler
  7188           <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
  6971 </xsl:text>
  7189 </xsl:text>
  6972           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
  7190           <xsl:text>// Since dispatch directly calls change_hmi_value,
  6973 </xsl:text>
  7191 </xsl:text>
  6974           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
  7192           <xsl:text>// PLC will periodically send variable at given frequency
  6975 </xsl:text>
  7193 </xsl:text>
  6976           <xsl:text>ws.onmessage = function (evt) {
  7194           <xsl:text>subscribers(heartbeat_index).add({
  6977 </xsl:text>
  7195 </xsl:text>
  6978           <xsl:text>
  7196           <xsl:text>    /* type: "Watchdog", */
  6979 </xsl:text>
  7197 </xsl:text>
  6980           <xsl:text>    let data = evt.data;
  7198           <xsl:text>    frequency: 1,
  6981 </xsl:text>
  7199 </xsl:text>
  6982           <xsl:text>    let dv = new DataView(data);
  7200           <xsl:text>    indexes: [heartbeat_index],
  6983 </xsl:text>
  7201 </xsl:text>
  6984           <xsl:text>    let i = 0;
  7202           <xsl:text>    new_hmi_value: function(index, value, oldval) {
  6985 </xsl:text>
  7203 </xsl:text>
  6986           <xsl:text>    try {
  7204           <xsl:text>        apply_hmi_value(heartbeat_index, value+1);
  6987 </xsl:text>
  7205 </xsl:text>
  6988           <xsl:text>        for(let hash_int of hmi_hash) {
  7206           <xsl:text>    }
  6989 </xsl:text>
  7207 </xsl:text>
  6990           <xsl:text>            if(hash_int != dv.getUint8(i)){
  7208           <xsl:text>});
  6991 </xsl:text>
  7209 </xsl:text>
  6992           <xsl:text>                throw new Error("Hash doesn't match");
  7210           <xsl:text>
  6993 </xsl:text>
  7211 </xsl:text>
  6994           <xsl:text>            };
  7212           <xsl:text>function svg_text_to_multiline(elt) {
  6995 </xsl:text>
  7213 </xsl:text>
  6996           <xsl:text>            i++;
  7214           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
  6997 </xsl:text>
  7215 </xsl:text>
  6998           <xsl:text>        };
  7216           <xsl:text>}
  6999 </xsl:text>
  7217 </xsl:text>
  7000           <xsl:text>
  7218           <xsl:text>
  7001 </xsl:text>
  7219 </xsl:text>
  7002           <xsl:text>        while(i &lt; data.byteLength){
  7220           <xsl:text>function multiline_to_svg_text(elt, str) {
  7003 </xsl:text>
  7221 </xsl:text>
  7004           <xsl:text>            let index = dv.getUint32(i, true);
  7222           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
  7005 </xsl:text>
  7223 </xsl:text>
  7006           <xsl:text>            i += 4;
  7224           <xsl:text>}
  7007 </xsl:text>
  7225 </xsl:text>
  7008           <xsl:text>            let iectype = hmitree_types[index];
  7226           <xsl:text>
  7009 </xsl:text>
  7227 </xsl:text>
  7010           <xsl:text>            if(iectype != undefined){
  7228           <xsl:text>function switch_langnum(langnum) {
  7011 </xsl:text>
  7229 </xsl:text>
  7012           <xsl:text>                let dvgetter = dvgetters[iectype];
  7230           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
  7013 </xsl:text>
  7231 </xsl:text>
  7014           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
  7232           <xsl:text>
  7015 </xsl:text>
  7233 </xsl:text>
  7016           <xsl:text>                updates[index] = value;
  7234           <xsl:text>    for (let translation of translations) {
  7017 </xsl:text>
  7235 </xsl:text>
  7018           <xsl:text>                i += bytesize;
  7236           <xsl:text>        let [objs, msgs] = translation;
  7019 </xsl:text>
  7237 </xsl:text>
  7020           <xsl:text>            } else {
  7238           <xsl:text>        let msg = msgs[langnum];
  7021 </xsl:text>
  7239 </xsl:text>
  7022           <xsl:text>                throw new Error("Unknown index "+index);
  7240           <xsl:text>        for (let obj of objs) {
       
  7241 </xsl:text>
       
  7242           <xsl:text>            multiline_to_svg_text(obj, msg);
       
  7243 </xsl:text>
       
  7244           <xsl:text>            obj.setAttribute("lang",langnum);
       
  7245 </xsl:text>
       
  7246           <xsl:text>        }
       
  7247 </xsl:text>
       
  7248           <xsl:text>    }
       
  7249 </xsl:text>
       
  7250           <xsl:text>    return langnum;
       
  7251 </xsl:text>
       
  7252           <xsl:text>}
       
  7253 </xsl:text>
       
  7254           <xsl:text>
       
  7255 </xsl:text>
       
  7256           <xsl:text>// backup original texts
       
  7257 </xsl:text>
       
  7258           <xsl:text>for (let translation of translations) {
       
  7259 </xsl:text>
       
  7260           <xsl:text>    let [objs, msgs] = translation;
       
  7261 </xsl:text>
       
  7262           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
  7263 </xsl:text>
       
  7264           <xsl:text>}
       
  7265 </xsl:text>
       
  7266           <xsl:text>
       
  7267 </xsl:text>
       
  7268           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
  7269 </xsl:text>
       
  7270           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
  7271 </xsl:text>
       
  7272           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
  7273 </xsl:text>
       
  7274           <xsl:text>subscribers(lang_local_index).add({
       
  7275 </xsl:text>
       
  7276           <xsl:text>    indexes: [lang_local_index],
       
  7277 </xsl:text>
       
  7278           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  7279 </xsl:text>
       
  7280           <xsl:text>        let current_lang =  switch_langnum(value);
       
  7281 </xsl:text>
       
  7282           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
  7283 </xsl:text>
       
  7284           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
  7285 </xsl:text>
       
  7286           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
  7287 </xsl:text>
       
  7288           <xsl:text>        switch_page();
       
  7289 </xsl:text>
       
  7290           <xsl:text>    }
       
  7291 </xsl:text>
       
  7292           <xsl:text>});
       
  7293 </xsl:text>
       
  7294           <xsl:text>
       
  7295 </xsl:text>
       
  7296           <xsl:text>function setup_lang(){
       
  7297 </xsl:text>
       
  7298           <xsl:text>    let current_lang = cache[lang_local_index];
       
  7299 </xsl:text>
       
  7300           <xsl:text>    let new_lang = switch_langnum(current_lang);
       
  7301 </xsl:text>
       
  7302           <xsl:text>    if(current_lang != new_lang){
       
  7303 </xsl:text>
       
  7304           <xsl:text>        apply_hmi_value(lang_local_index, new_lang);
       
  7305 </xsl:text>
       
  7306           <xsl:text>    }
       
  7307 </xsl:text>
       
  7308           <xsl:text>}
       
  7309 </xsl:text>
       
  7310           <xsl:text>
       
  7311 </xsl:text>
       
  7312           <xsl:text>setup_lang();
       
  7313 </xsl:text>
       
  7314           <xsl:text>
       
  7315 </xsl:text>
       
  7316           <xsl:text>function update_subscriptions() {
       
  7317 </xsl:text>
       
  7318           <xsl:text>    let delta = [];
       
  7319 </xsl:text>
       
  7320           <xsl:text>    for(let index in subscriptions){
       
  7321 </xsl:text>
       
  7322           <xsl:text>        let widgets = subscribers(index);
       
  7323 </xsl:text>
       
  7324           <xsl:text>
       
  7325 </xsl:text>
       
  7326           <xsl:text>        // periods are in ms
       
  7327 </xsl:text>
       
  7328           <xsl:text>        let previous_period = get_subscription_period(index);
       
  7329 </xsl:text>
       
  7330           <xsl:text>
       
  7331 </xsl:text>
       
  7332           <xsl:text>        // subscribing with a zero period is unsubscribing
       
  7333 </xsl:text>
       
  7334           <xsl:text>        let new_period = 0;
       
  7335 </xsl:text>
       
  7336           <xsl:text>        if(widgets.size &gt; 0) {
       
  7337 </xsl:text>
       
  7338           <xsl:text>            let maxfreq = 0;
       
  7339 </xsl:text>
       
  7340           <xsl:text>            for(let widget of widgets){
       
  7341 </xsl:text>
       
  7342           <xsl:text>                let wf = widget.frequency;
       
  7343 </xsl:text>
       
  7344           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
  7345 </xsl:text>
       
  7346           <xsl:text>                    maxfreq = wf;
  7023 </xsl:text>
  7347 </xsl:text>
  7024           <xsl:text>            }
  7348           <xsl:text>            }
  7025 </xsl:text>
  7349 </xsl:text>
  7026           <xsl:text>        };
  7350           <xsl:text>
  7027 </xsl:text>
  7351 </xsl:text>
  7028           <xsl:text>        // register for rendering on next frame, since there are updates
  7352           <xsl:text>            if(maxfreq != 0)
  7029 </xsl:text>
  7353 </xsl:text>
  7030           <xsl:text>        requestHMIAnimation();
  7354           <xsl:text>                new_period = 1000/maxfreq;
  7031 </xsl:text>
  7355 </xsl:text>
  7032           <xsl:text>    } catch(err) {
  7356           <xsl:text>        }
  7033 </xsl:text>
  7357 </xsl:text>
  7034           <xsl:text>        // 1003 is for "Unsupported Data"
  7358           <xsl:text>
  7035 </xsl:text>
  7359 </xsl:text>
  7036           <xsl:text>        // ws.close(1003, err.message);
  7360           <xsl:text>        if(previous_period != new_period) {
  7037 </xsl:text>
  7361 </xsl:text>
  7038           <xsl:text>
  7362           <xsl:text>            set_subscription_period(index, new_period);
  7039 </xsl:text>
  7363 </xsl:text>
  7040           <xsl:text>        // TODO : remove debug alert ?
  7364           <xsl:text>            if(index &lt;= last_remote_index){
  7041 </xsl:text>
  7365 </xsl:text>
  7042           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
  7366           <xsl:text>                delta.push(
  7043 </xsl:text>
  7367 </xsl:text>
  7044           <xsl:text>
  7368           <xsl:text>                    new Uint8Array([2]), /* subscribe = 2 */
  7045 </xsl:text>
  7369 </xsl:text>
  7046           <xsl:text>        // force reload ignoring cache
  7370           <xsl:text>                    new Uint32Array([index]),
  7047 </xsl:text>
  7371 </xsl:text>
  7048           <xsl:text>        location.reload(true);
  7372           <xsl:text>                    new Uint16Array([new_period]));
       
  7373 </xsl:text>
       
  7374           <xsl:text>            }
       
  7375 </xsl:text>
       
  7376           <xsl:text>        }
  7049 </xsl:text>
  7377 </xsl:text>
  7050           <xsl:text>    }
  7378           <xsl:text>    }
  7051 </xsl:text>
  7379 </xsl:text>
       
  7380           <xsl:text>    send_blob(delta);
       
  7381 </xsl:text>
  7052           <xsl:text>};
  7382           <xsl:text>};
  7053 </xsl:text>
  7383 </xsl:text>
  7054           <xsl:text>
  7384           <xsl:text>
  7055 </xsl:text>
  7385 </xsl:text>
  7056           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
       
  7057 </xsl:text>
       
  7058           <xsl:text>
       
  7059 </xsl:text>
       
  7060           <xsl:text>function send_blob(data) {
       
  7061 </xsl:text>
       
  7062           <xsl:text>    if(data.length &gt; 0) {
       
  7063 </xsl:text>
       
  7064           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
       
  7065 </xsl:text>
       
  7066           <xsl:text>    };
       
  7067 </xsl:text>
       
  7068           <xsl:text>};
       
  7069 </xsl:text>
       
  7070           <xsl:text>
       
  7071 </xsl:text>
       
  7072           <xsl:text>const typedarray_types = {
       
  7073 </xsl:text>
       
  7074           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  7075 </xsl:text>
       
  7076           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  7077 </xsl:text>
       
  7078           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  7079 </xsl:text>
       
  7080           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
  7081 </xsl:text>
       
  7082           <xsl:text>    STRING: (str) =&gt; {
       
  7083 </xsl:text>
       
  7084           <xsl:text>        // beremiz default string max size is 128
       
  7085 </xsl:text>
       
  7086           <xsl:text>        str = str.slice(0,128);
       
  7087 </xsl:text>
       
  7088           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  7089 </xsl:text>
       
  7090           <xsl:text>        binary[0] = str.length;
       
  7091 </xsl:text>
       
  7092           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
  7093 </xsl:text>
       
  7094           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  7095 </xsl:text>
       
  7096           <xsl:text>        }
       
  7097 </xsl:text>
       
  7098           <xsl:text>        return binary;
       
  7099 </xsl:text>
       
  7100           <xsl:text>    }
       
  7101 </xsl:text>
       
  7102           <xsl:text>    /* TODO */
       
  7103 </xsl:text>
       
  7104           <xsl:text>};
       
  7105 </xsl:text>
       
  7106           <xsl:text>
       
  7107 </xsl:text>
       
  7108           <xsl:text>function send_reset() {
       
  7109 </xsl:text>
       
  7110           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  7111 </xsl:text>
       
  7112           <xsl:text>};
       
  7113 </xsl:text>
       
  7114           <xsl:text>
       
  7115 </xsl:text>
       
  7116           <xsl:text>var subscriptions = [];
       
  7117 </xsl:text>
       
  7118           <xsl:text>
       
  7119 </xsl:text>
       
  7120           <xsl:text>function subscribers(index) {
       
  7121 </xsl:text>
       
  7122           <xsl:text>    let entry = subscriptions[index];
       
  7123 </xsl:text>
       
  7124           <xsl:text>    let res;
       
  7125 </xsl:text>
       
  7126           <xsl:text>    if(entry == undefined){
       
  7127 </xsl:text>
       
  7128           <xsl:text>        res = new Set();
       
  7129 </xsl:text>
       
  7130           <xsl:text>        subscriptions[index] = [res,0];
       
  7131 </xsl:text>
       
  7132           <xsl:text>    }else{
       
  7133 </xsl:text>
       
  7134           <xsl:text>        [res, _ign] = entry;
       
  7135 </xsl:text>
       
  7136           <xsl:text>    }
       
  7137 </xsl:text>
       
  7138           <xsl:text>    return res
       
  7139 </xsl:text>
       
  7140           <xsl:text>}
       
  7141 </xsl:text>
       
  7142           <xsl:text>
       
  7143 </xsl:text>
       
  7144           <xsl:text>function get_subscription_period(index) {
       
  7145 </xsl:text>
       
  7146           <xsl:text>    let entry = subscriptions[index];
       
  7147 </xsl:text>
       
  7148           <xsl:text>    if(entry == undefined)
       
  7149 </xsl:text>
       
  7150           <xsl:text>        return 0;
       
  7151 </xsl:text>
       
  7152           <xsl:text>    let [_ign, period] = entry;
       
  7153 </xsl:text>
       
  7154           <xsl:text>    return period;
       
  7155 </xsl:text>
       
  7156           <xsl:text>}
       
  7157 </xsl:text>
       
  7158           <xsl:text>
       
  7159 </xsl:text>
       
  7160           <xsl:text>function set_subscription_period(index, period) {
       
  7161 </xsl:text>
       
  7162           <xsl:text>    let entry = subscriptions[index];
       
  7163 </xsl:text>
       
  7164           <xsl:text>    if(entry == undefined){
       
  7165 </xsl:text>
       
  7166           <xsl:text>        subscriptions[index] = [new Set(), period];
       
  7167 </xsl:text>
       
  7168           <xsl:text>    } else {
       
  7169 </xsl:text>
       
  7170           <xsl:text>        entry[1] = period;
       
  7171 </xsl:text>
       
  7172           <xsl:text>    }
       
  7173 </xsl:text>
       
  7174           <xsl:text>}
       
  7175 </xsl:text>
       
  7176           <xsl:text>
       
  7177 </xsl:text>
       
  7178           <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  7179 </xsl:text>
       
  7180           <xsl:text>// Since dispatch directly calls change_hmi_value,
       
  7181 </xsl:text>
       
  7182           <xsl:text>// PLC will periodically send variable at given frequency
       
  7183 </xsl:text>
       
  7184           <xsl:text>subscribers(heartbeat_index).add({
       
  7185 </xsl:text>
       
  7186           <xsl:text>    /* type: "Watchdog", */
       
  7187 </xsl:text>
       
  7188           <xsl:text>    frequency: 1,
       
  7189 </xsl:text>
       
  7190           <xsl:text>    indexes: [heartbeat_index],
       
  7191 </xsl:text>
       
  7192           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  7193 </xsl:text>
       
  7194           <xsl:text>        apply_hmi_value(heartbeat_index, value+1);
       
  7195 </xsl:text>
       
  7196           <xsl:text>    }
       
  7197 </xsl:text>
       
  7198           <xsl:text>});
       
  7199 </xsl:text>
       
  7200           <xsl:text>
       
  7201 </xsl:text>
       
  7202           <xsl:text>function svg_text_to_multiline(elt) {
       
  7203 </xsl:text>
       
  7204           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
       
  7205 </xsl:text>
       
  7206           <xsl:text>}
       
  7207 </xsl:text>
       
  7208           <xsl:text>
       
  7209 </xsl:text>
       
  7210           <xsl:text>function multiline_to_svg_text(elt, str) {
       
  7211 </xsl:text>
       
  7212           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
       
  7213 </xsl:text>
       
  7214           <xsl:text>}
       
  7215 </xsl:text>
       
  7216           <xsl:text>
       
  7217 </xsl:text>
       
  7218           <xsl:text>function switch_langnum(langnum) {
       
  7219 </xsl:text>
       
  7220           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
  7221 </xsl:text>
       
  7222           <xsl:text>
       
  7223 </xsl:text>
       
  7224           <xsl:text>    for (let translation of translations) {
       
  7225 </xsl:text>
       
  7226           <xsl:text>        let [objs, msgs] = translation;
       
  7227 </xsl:text>
       
  7228           <xsl:text>        let msg = msgs[langnum];
       
  7229 </xsl:text>
       
  7230           <xsl:text>        for (let obj of objs) {
       
  7231 </xsl:text>
       
  7232           <xsl:text>            multiline_to_svg_text(obj, msg);
       
  7233 </xsl:text>
       
  7234           <xsl:text>            obj.setAttribute("lang",langnum);
       
  7235 </xsl:text>
       
  7236           <xsl:text>        }
       
  7237 </xsl:text>
       
  7238           <xsl:text>    }
       
  7239 </xsl:text>
       
  7240           <xsl:text>    return langnum;
       
  7241 </xsl:text>
       
  7242           <xsl:text>}
       
  7243 </xsl:text>
       
  7244           <xsl:text>
       
  7245 </xsl:text>
       
  7246           <xsl:text>// backup original texts
       
  7247 </xsl:text>
       
  7248           <xsl:text>for (let translation of translations) {
       
  7249 </xsl:text>
       
  7250           <xsl:text>    let [objs, msgs] = translation;
       
  7251 </xsl:text>
       
  7252           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
  7253 </xsl:text>
       
  7254           <xsl:text>}
       
  7255 </xsl:text>
       
  7256           <xsl:text>
       
  7257 </xsl:text>
       
  7258           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
  7259 </xsl:text>
       
  7260           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
  7261 </xsl:text>
       
  7262           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
  7263 </xsl:text>
       
  7264           <xsl:text>subscribers(lang_local_index).add({
       
  7265 </xsl:text>
       
  7266           <xsl:text>    indexes: [lang_local_index],
       
  7267 </xsl:text>
       
  7268           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  7269 </xsl:text>
       
  7270           <xsl:text>        let current_lang =  switch_langnum(value);
       
  7271 </xsl:text>
       
  7272           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
  7273 </xsl:text>
       
  7274           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
  7275 </xsl:text>
       
  7276           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
  7277 </xsl:text>
       
  7278           <xsl:text>        switch_page();
       
  7279 </xsl:text>
       
  7280           <xsl:text>    }
       
  7281 </xsl:text>
       
  7282           <xsl:text>});
       
  7283 </xsl:text>
       
  7284           <xsl:text>
       
  7285 </xsl:text>
       
  7286           <xsl:text>function setup_lang(){
       
  7287 </xsl:text>
       
  7288           <xsl:text>    let current_lang = cache[lang_local_index];
       
  7289 </xsl:text>
       
  7290           <xsl:text>    let new_lang = switch_langnum(current_lang);
       
  7291 </xsl:text>
       
  7292           <xsl:text>    if(current_lang != new_lang){
       
  7293 </xsl:text>
       
  7294           <xsl:text>        apply_hmi_value(lang_local_index, new_lang);
       
  7295 </xsl:text>
       
  7296           <xsl:text>    }
       
  7297 </xsl:text>
       
  7298           <xsl:text>}
       
  7299 </xsl:text>
       
  7300           <xsl:text>
       
  7301 </xsl:text>
       
  7302           <xsl:text>setup_lang();
       
  7303 </xsl:text>
       
  7304           <xsl:text>
       
  7305 </xsl:text>
       
  7306           <xsl:text>function update_subscriptions() {
       
  7307 </xsl:text>
       
  7308           <xsl:text>    let delta = [];
       
  7309 </xsl:text>
       
  7310           <xsl:text>    for(let index in subscriptions){
       
  7311 </xsl:text>
       
  7312           <xsl:text>        let widgets = subscribers(index);
       
  7313 </xsl:text>
       
  7314           <xsl:text>
       
  7315 </xsl:text>
       
  7316           <xsl:text>        // periods are in ms
       
  7317 </xsl:text>
       
  7318           <xsl:text>        let previous_period = get_subscription_period(index);
       
  7319 </xsl:text>
       
  7320           <xsl:text>
       
  7321 </xsl:text>
       
  7322           <xsl:text>        // subscribing with a zero period is unsubscribing
       
  7323 </xsl:text>
       
  7324           <xsl:text>        let new_period = 0;
       
  7325 </xsl:text>
       
  7326           <xsl:text>        if(widgets.size &gt; 0) {
       
  7327 </xsl:text>
       
  7328           <xsl:text>            let maxfreq = 0;
       
  7329 </xsl:text>
       
  7330           <xsl:text>            for(let widget of widgets){
       
  7331 </xsl:text>
       
  7332           <xsl:text>                let wf = widget.frequency;
       
  7333 </xsl:text>
       
  7334           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
  7335 </xsl:text>
       
  7336           <xsl:text>                    maxfreq = wf;
       
  7337 </xsl:text>
       
  7338           <xsl:text>            }
       
  7339 </xsl:text>
       
  7340           <xsl:text>
       
  7341 </xsl:text>
       
  7342           <xsl:text>            if(maxfreq != 0)
       
  7343 </xsl:text>
       
  7344           <xsl:text>                new_period = 1000/maxfreq;
       
  7345 </xsl:text>
       
  7346           <xsl:text>        }
       
  7347 </xsl:text>
       
  7348           <xsl:text>
       
  7349 </xsl:text>
       
  7350           <xsl:text>        if(previous_period != new_period) {
       
  7351 </xsl:text>
       
  7352           <xsl:text>            set_subscription_period(index, new_period);
       
  7353 </xsl:text>
       
  7354           <xsl:text>            if(index &lt;= last_remote_index){
       
  7355 </xsl:text>
       
  7356           <xsl:text>                delta.push(
       
  7357 </xsl:text>
       
  7358           <xsl:text>                    new Uint8Array([2]), /* subscribe = 2 */
       
  7359 </xsl:text>
       
  7360           <xsl:text>                    new Uint32Array([index]),
       
  7361 </xsl:text>
       
  7362           <xsl:text>                    new Uint16Array([new_period]));
       
  7363 </xsl:text>
       
  7364           <xsl:text>            }
       
  7365 </xsl:text>
       
  7366           <xsl:text>        }
       
  7367 </xsl:text>
       
  7368           <xsl:text>    }
       
  7369 </xsl:text>
       
  7370           <xsl:text>    send_blob(delta);
       
  7371 </xsl:text>
       
  7372           <xsl:text>};
       
  7373 </xsl:text>
       
  7374           <xsl:text>
       
  7375 </xsl:text>
       
  7376           <xsl:text>function send_hmi_value(index, value) {
  7386           <xsl:text>function send_hmi_value(index, value) {
  7377 </xsl:text>
  7387 </xsl:text>
  7378           <xsl:text>    if(index &gt; last_remote_index){
  7388           <xsl:text>    if(index &gt; last_remote_index){
  7379 </xsl:text>
  7389 </xsl:text>
  7380           <xsl:text>        updates[index] = value;
  7390           <xsl:text>        updates.set(index, value);
  7381 </xsl:text>
  7391 </xsl:text>
  7382           <xsl:text>
  7392           <xsl:text>
  7383 </xsl:text>
  7393 </xsl:text>
  7384           <xsl:text>        if(persistent_indexes.has(index)){
  7394           <xsl:text>        if(persistent_indexes.has(index)){
  7385 </xsl:text>
  7395 </xsl:text>