svghmi/gen_index_xhtml.xslt
branchsvghmi
changeset 3005 ff9ae4f4e3be
parent 3004 705e34c6fe93
child 3008 dabad70db1bf
equal deleted inserted replaced
3004:705e34c6fe93 3005:ff9ae4f4e3be
   495       <xsl:text>    page_index: </xsl:text>
   495       <xsl:text>    page_index: </xsl:text>
   496       <xsl:value-of select="$desc/path/@index"/>
   496       <xsl:value-of select="$desc/path/@index"/>
   497       <xsl:text>,
   497       <xsl:text>,
   498 </xsl:text>
   498 </xsl:text>
   499     </xsl:if>
   499     </xsl:if>
   500     <xsl:text>    relative_widgets: [
   500     <xsl:text>    widgets: [
   501 </xsl:text>
   501 </xsl:text>
   502     <xsl:for-each select="$page_relative_widgets">
   502     <xsl:for-each select="$page_managed_widgets">
   503       <xsl:text>        hmi_widgets["</xsl:text>
   503       <xsl:variable name="widget_paths_relativeness">
       
   504         <xsl:for-each select="func:widget(@id)/path">
       
   505           <xsl:value-of select="func:is_descendant_path(@value, $desc/path/@value)"/>
       
   506           <xsl:if test="position()!=last()">
       
   507             <xsl:text>,</xsl:text>
       
   508           </xsl:if>
       
   509         </xsl:for-each>
       
   510       </xsl:variable>
       
   511       <xsl:text>        [hmi_widgets["</xsl:text>
   504       <xsl:value-of select="@id"/>
   512       <xsl:value-of select="@id"/>
   505       <xsl:text>"]</xsl:text>
   513       <xsl:text>"], [</xsl:text>
   506       <xsl:if test="position()!=last()">
   514       <xsl:value-of select="$widget_paths_relativeness"/>
   507         <xsl:text>,</xsl:text>
   515       <xsl:text>]]</xsl:text>
   508       </xsl:if>
       
   509       <xsl:text>
       
   510 </xsl:text>
       
   511     </xsl:for-each>
       
   512     <xsl:text>    ],
       
   513 </xsl:text>
       
   514     <xsl:text>    absolute_widgets: [
       
   515 </xsl:text>
       
   516     <xsl:for-each select="$page_managed_widgets[not(@id = $page_relative_widgets/@id)]">
       
   517       <xsl:text>        hmi_widgets["</xsl:text>
       
   518       <xsl:value-of select="@id"/>
       
   519       <xsl:text>"]</xsl:text>
       
   520       <xsl:if test="position()!=last()">
   516       <xsl:if test="position()!=last()">
   521         <xsl:text>,</xsl:text>
   517         <xsl:text>,</xsl:text>
   522       </xsl:if>
   518       </xsl:if>
   523       <xsl:text>
   519       <xsl:text>
   524 </xsl:text>
   520 </xsl:text>
   945 </xsl:text>
   941 </xsl:text>
   946     <xsl:text>    unsub(){
   942     <xsl:text>    unsub(){
   947 </xsl:text>
   943 </xsl:text>
   948     <xsl:text>        /* remove subsribers */
   944     <xsl:text>        /* remove subsribers */
   949 </xsl:text>
   945 </xsl:text>
       
   946     <xsl:text>        if(!this.unsubscribable)
       
   947 </xsl:text>
       
   948     <xsl:text>            for(let i = 0; i &lt; this.indexes.length; i++) {
       
   949 </xsl:text>
       
   950     <xsl:text>                let index = this.indexes[i];
       
   951 </xsl:text>
       
   952     <xsl:text>                if(this.relativeness[i])
       
   953 </xsl:text>
       
   954     <xsl:text>                    index += this.offset;
       
   955 </xsl:text>
       
   956     <xsl:text>                subscribers[index].delete(this);
       
   957 </xsl:text>
       
   958     <xsl:text>            }
       
   959 </xsl:text>
       
   960     <xsl:text>        this.offset = 0;
       
   961 </xsl:text>
       
   962     <xsl:text>        this.relativeness = undefined;
       
   963 </xsl:text>
       
   964     <xsl:text>    }
       
   965 </xsl:text>
       
   966     <xsl:text>
       
   967 </xsl:text>
       
   968     <xsl:text>    sub(new_offset=0, relativeness){
       
   969 </xsl:text>
       
   970     <xsl:text>        this.offset = new_offset;
       
   971 </xsl:text>
       
   972     <xsl:text>        this.relativeness = relativeness;
       
   973 </xsl:text>
       
   974     <xsl:text>        /* add this's subsribers */
       
   975 </xsl:text>
       
   976     <xsl:text>        if(!this.unsubscribable)
       
   977 </xsl:text>
       
   978     <xsl:text>            for(let i = 0; i &lt; this.indexes.length; i++) {
       
   979 </xsl:text>
       
   980     <xsl:text>                let index = this.indexes[i];
       
   981 </xsl:text>
       
   982     <xsl:text>                if(relativeness[i])
       
   983 </xsl:text>
       
   984     <xsl:text>                    index += new_offset;
       
   985 </xsl:text>
       
   986     <xsl:text>                subscribers[index].add(this);
       
   987 </xsl:text>
       
   988     <xsl:text>            }
       
   989 </xsl:text>
       
   990     <xsl:text>        need_cache_apply.push(this); 
       
   991 </xsl:text>
       
   992     <xsl:text>    }
       
   993 </xsl:text>
       
   994     <xsl:text>
       
   995 </xsl:text>
       
   996     <xsl:text>    apply_cache() {
       
   997 </xsl:text>
   950     <xsl:text>        if(!this.unsubscribable) for(let index of this.indexes){
   998     <xsl:text>        if(!this.unsubscribable) for(let index of this.indexes){
   951 </xsl:text>
   999 </xsl:text>
   952     <xsl:text>            let idx = index + this.offset;
  1000     <xsl:text>            /* dispatch current cache in newly opened page widgets */
   953 </xsl:text>
  1001 </xsl:text>
   954     <xsl:text>            subscribers[idx].delete(this);
  1002     <xsl:text>            let realindex = index+this.offset;
       
  1003 </xsl:text>
       
  1004     <xsl:text>            let cached_val = cache[realindex];
       
  1005 </xsl:text>
       
  1006     <xsl:text>            if(cached_val != undefined)
       
  1007 </xsl:text>
       
  1008     <xsl:text>                this.new_hmi_value(realindex, cached_val, cached_val);
   955 </xsl:text>
  1009 </xsl:text>
   956     <xsl:text>        }
  1010     <xsl:text>        }
   957 </xsl:text>
  1011 </xsl:text>
   958     <xsl:text>        this.offset = 0;
       
   959 </xsl:text>
       
   960     <xsl:text>    }
  1012     <xsl:text>    }
   961 </xsl:text>
  1013 </xsl:text>
   962     <xsl:text>
  1014     <xsl:text>
   963 </xsl:text>
  1015 </xsl:text>
   964     <xsl:text>    sub(new_offset=0){
  1016     <xsl:text>    get_idx(index) {
   965 </xsl:text>
  1017 </xsl:text>
   966     <xsl:text>        /* set the offset because relative */
  1018     <xsl:text>         let orig = this.indexes[index];
   967 </xsl:text>
  1019 </xsl:text>
   968     <xsl:text>        this.offset = new_offset;
  1020     <xsl:text>         return this.relativeness[index] ? orig + this.offset : orig;
   969 </xsl:text>
  1021 </xsl:text>
   970     <xsl:text>        /* add this's subsribers */
  1022     <xsl:text>    }
   971 </xsl:text>
  1023 </xsl:text>
   972     <xsl:text>        if(!this.unsubscribable) for(let index of this.indexes){
  1024     <xsl:text>    change_hmi_value(index,opstr) {
   973 </xsl:text>
  1025 </xsl:text>
   974     <xsl:text>            subscribers[index + new_offset].add(this);
  1026     <xsl:text>        return change_hmi_value(this.get_idx(index), opstr);
       
  1027 </xsl:text>
       
  1028     <xsl:text>    }
       
  1029 </xsl:text>
       
  1030     <xsl:text>
       
  1031 </xsl:text>
       
  1032     <xsl:text>    apply_hmi_value(index, new_val) {
       
  1033 </xsl:text>
       
  1034     <xsl:text>        return apply_hmi_value(this.get_idx(0), new_val);
       
  1035 </xsl:text>
       
  1036     <xsl:text>    }
       
  1037 </xsl:text>
       
  1038     <xsl:text>
       
  1039 </xsl:text>
       
  1040     <xsl:text>    new_hmi_value(index, value, oldval) {
       
  1041 </xsl:text>
       
  1042     <xsl:text>        try {
       
  1043 </xsl:text>
       
  1044     <xsl:text>            // TODO avoid searching, store index at sub()
       
  1045 </xsl:text>
       
  1046     <xsl:text>            for(let i = 0; i &lt; this.indexes.length; i++) {
       
  1047 </xsl:text>
       
  1048     <xsl:text>                let refindex = this.indexes[i];
       
  1049 </xsl:text>
       
  1050     <xsl:text>                if(this.relativeness[i])
       
  1051 </xsl:text>
       
  1052     <xsl:text>                    refindex += this.offset;
       
  1053 </xsl:text>
       
  1054     <xsl:text>
       
  1055 </xsl:text>
       
  1056     <xsl:text>                if(index == refindex) {
       
  1057 </xsl:text>
       
  1058     <xsl:text>                    let d = this.dispatch;
       
  1059 </xsl:text>
       
  1060     <xsl:text>                    if(typeof(d) == "function"){
       
  1061 </xsl:text>
       
  1062     <xsl:text>                        d.call(this, value, oldval, i);
       
  1063 </xsl:text>
       
  1064     <xsl:text>                    }
       
  1065 </xsl:text>
       
  1066     <xsl:text>                    else if(typeof(d) == "object"){
       
  1067 </xsl:text>
       
  1068     <xsl:text>                        d[i].call(this, value, oldval);
       
  1069 </xsl:text>
       
  1070     <xsl:text>                    }
       
  1071 </xsl:text>
       
  1072     <xsl:text>                    /* else dispatch_0, ..., dispatch_n ? */
       
  1073 </xsl:text>
       
  1074     <xsl:text>                    /*else {
       
  1075 </xsl:text>
       
  1076     <xsl:text>                        throw new Error("Dunno how to dispatch to widget at index = " + index);
       
  1077 </xsl:text>
       
  1078     <xsl:text>                    }*/
       
  1079 </xsl:text>
       
  1080     <xsl:text>                    break;
       
  1081 </xsl:text>
       
  1082     <xsl:text>                }
       
  1083 </xsl:text>
       
  1084     <xsl:text>            }
       
  1085 </xsl:text>
       
  1086     <xsl:text>        } catch(err) {
       
  1087 </xsl:text>
       
  1088     <xsl:text>            console.log(err);
   975 </xsl:text>
  1089 </xsl:text>
   976     <xsl:text>        }
  1090     <xsl:text>        }
   977 </xsl:text>
       
   978     <xsl:text>        need_cache_apply.push(this); 
       
   979 </xsl:text>
       
   980     <xsl:text>    }
       
   981 </xsl:text>
       
   982     <xsl:text>
       
   983 </xsl:text>
       
   984     <xsl:text>    apply_cache() {
       
   985 </xsl:text>
       
   986     <xsl:text>        if(!this.unsubscribable) for(let index of this.indexes){
       
   987 </xsl:text>
       
   988     <xsl:text>            /* dispatch current cache in newly opened page widgets */
       
   989 </xsl:text>
       
   990     <xsl:text>            let realindex = index+this.offset;
       
   991 </xsl:text>
       
   992     <xsl:text>            let cached_val = cache[realindex];
       
   993 </xsl:text>
       
   994     <xsl:text>            if(cached_val != undefined)
       
   995 </xsl:text>
       
   996     <xsl:text>                dispatch_value_to_widget(this, realindex, cached_val, cached_val);
       
   997 </xsl:text>
       
   998     <xsl:text>        }
       
   999 </xsl:text>
       
  1000     <xsl:text>    }
       
  1001 </xsl:text>
       
  1002     <xsl:text>
       
  1003 </xsl:text>
       
  1004     <xsl:text>    get_idx(index) {
       
  1005 </xsl:text>
       
  1006     <xsl:text>         let orig = this.indexes[index];
       
  1007 </xsl:text>
       
  1008     <xsl:text>         return this.offset ? orig + this.offset : orig;
       
  1009 </xsl:text>
       
  1010     <xsl:text>    }
       
  1011 </xsl:text>
       
  1012     <xsl:text>    change_hmi_value(index,opstr) {
       
  1013 </xsl:text>
       
  1014     <xsl:text>        return change_hmi_value(this.get_idx(index), opstr);
       
  1015 </xsl:text>
       
  1016     <xsl:text>    }
       
  1017 </xsl:text>
       
  1018     <xsl:text>
       
  1019 </xsl:text>
       
  1020     <xsl:text>    apply_hmi_value(index, new_val) {
       
  1021 </xsl:text>
       
  1022     <xsl:text>        return apply_hmi_value(this.get_idx(0), new_val);
       
  1023 </xsl:text>
  1091 </xsl:text>
  1024     <xsl:text>    }
  1092     <xsl:text>    }
  1025 </xsl:text>
  1093 </xsl:text>
  1026     <xsl:text>}
  1094     <xsl:text>}
  1027 </xsl:text>
  1095 </xsl:text>
  1890     <xsl:text>    },
  1958     <xsl:text>    },
  1891 </xsl:text>
  1959 </xsl:text>
  1892   </xsl:template>
  1960   </xsl:template>
  1893   <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
  1961   <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
  1894     <xsl:param name="hmi_element"/>
  1962     <xsl:param name="hmi_element"/>
       
  1963     <xsl:if test="count(path) != 1">
       
  1964       <xsl:message terminate="yes">
       
  1965         <xsl:text>ForEach widget </xsl:text>
       
  1966         <xsl:value-of select="$hmi_element/@id"/>
       
  1967         <xsl:text> must have one HMI path given.</xsl:text>
       
  1968       </xsl:message>
       
  1969     </xsl:if>
       
  1970     <xsl:if test="count(arg) != 1">
       
  1971       <xsl:message terminate="yes">
       
  1972         <xsl:text>ForEach widget </xsl:text>
       
  1973         <xsl:value-of select="$hmi_element/@id"/>
       
  1974         <xsl:text> must have one argument given : a class name.</xsl:text>
       
  1975       </xsl:message>
       
  1976     </xsl:if>
  1895     <xsl:variable name="class" select="arg[1]/@value"/>
  1977     <xsl:variable name="class" select="arg[1]/@value"/>
  1896     <xsl:variable name="base_path" select="path/@value"/>
  1978     <xsl:variable name="base_path" select="path/@value"/>
  1897     <xsl:variable name="hmi_index_base" select="$indexed_hmitree/*[@hmipath = $base_path]"/>
  1979     <xsl:variable name="hmi_index_base" select="$indexed_hmitree/*[@hmipath = $base_path]"/>
  1898     <xsl:variable name="hmi_tree_base" select="$hmitree/descendant-or-self::*[@path = $hmi_index_base/@path]"/>
  1980     <xsl:variable name="hmi_tree_base" select="$hmitree/descendant-or-self::*[@path = $hmi_index_base/@path]"/>
  1899     <xsl:variable name="hmi_tree_items" select="$hmi_tree_base/*[@class = $class]"/>
  1981     <xsl:variable name="hmi_tree_items" select="$hmi_tree_base/*[@class = $class]"/>
  1991 </xsl:text>
  2073 </xsl:text>
  1992   </xsl:template>
  2074   </xsl:template>
  1993   <xsl:template mode="widget_class" match="widget[@type='ForEach']">
  2075   <xsl:template mode="widget_class" match="widget[@type='ForEach']">
  1994     <xsl:text>class ForEachWidget extends Widget{
  2076     <xsl:text>class ForEachWidget extends Widget{
  1995 </xsl:text>
  2077 </xsl:text>
       
  2078     <xsl:text>
       
  2079 </xsl:text>
       
  2080     <xsl:text>    unsub_items(){
       
  2081 </xsl:text>
       
  2082     <xsl:text>        for(let item of this.items){
       
  2083 </xsl:text>
       
  2084     <xsl:text>            for(let widget of item) {
       
  2085 </xsl:text>
       
  2086     <xsl:text>                widget.unsub();
       
  2087 </xsl:text>
       
  2088     <xsl:text>            }
       
  2089 </xsl:text>
       
  2090     <xsl:text>        }
       
  2091 </xsl:text>
       
  2092     <xsl:text>    }
       
  2093 </xsl:text>
       
  2094     <xsl:text>
       
  2095 </xsl:text>
  1996     <xsl:text>    unsub(){
  2096     <xsl:text>    unsub(){
  1997 </xsl:text>
  2097 </xsl:text>
  1998     <xsl:text>        for(let item of this.items){
  2098     <xsl:text>        this.unsub_items();
       
  2099 </xsl:text>
       
  2100     <xsl:text>        this.offset = 0;
       
  2101 </xsl:text>
       
  2102     <xsl:text>        this.relativeness = undefined;
       
  2103 </xsl:text>
       
  2104     <xsl:text>    }
       
  2105 </xsl:text>
       
  2106     <xsl:text>
       
  2107 </xsl:text>
       
  2108     <xsl:text>    sub_items(){
       
  2109 </xsl:text>
       
  2110     <xsl:text>        for(let i = 0; i &lt; this.items.length; i++) {
       
  2111 </xsl:text>
       
  2112     <xsl:text>            let item = this.items[i];
       
  2113 </xsl:text>
       
  2114     <xsl:text>            let orig_item_index = this.index_pool[i];
       
  2115 </xsl:text>
       
  2116     <xsl:text>            let item_index = this.index_pool[i+this.item_offset];
       
  2117 </xsl:text>
       
  2118     <xsl:text>            let item_index_offset = item_index - orig_item_index;
       
  2119 </xsl:text>
       
  2120     <xsl:text>            if(this.relativeness[0])
       
  2121 </xsl:text>
       
  2122     <xsl:text>                item_index_offset += this.offset;
  1999 </xsl:text>
  2123 </xsl:text>
  2000     <xsl:text>            for(let widget of item) {
  2124     <xsl:text>            for(let widget of item) {
  2001 </xsl:text>
  2125 </xsl:text>
  2002     <xsl:text>                widget.unsub();
  2126     <xsl:text>                /* all variables of all widgets in a ForEach are all relative. 
       
  2127 </xsl:text>
       
  2128     <xsl:text>                   Really.
       
  2129 </xsl:text>
       
  2130     <xsl:text>
       
  2131 </xsl:text>
       
  2132     <xsl:text>                   TODO: allow absolute variables in ForEach widgets
       
  2133 </xsl:text>
       
  2134     <xsl:text>                */
       
  2135 </xsl:text>
       
  2136     <xsl:text>                widget.sub(item_index_offset, widget.indexes.map(_=&gt;true));
  2003 </xsl:text>
  2137 </xsl:text>
  2004     <xsl:text>            }
  2138     <xsl:text>            }
  2005 </xsl:text>
  2139 </xsl:text>
  2006     <xsl:text>        }
  2140     <xsl:text>        }
  2007 </xsl:text>
  2141 </xsl:text>
  2008     <xsl:text>        this.offset = 0;
       
  2009 </xsl:text>
       
  2010     <xsl:text>    }
  2142     <xsl:text>    }
  2011 </xsl:text>
  2143 </xsl:text>
  2012     <xsl:text>
  2144     <xsl:text>
  2013 </xsl:text>
  2145 </xsl:text>
  2014     <xsl:text>    foreach_widgets_do(todo){
  2146     <xsl:text>    sub(new_offset=0, relativeness=[]){
  2015 </xsl:text>
  2147 </xsl:text>
  2016     <xsl:text>        for(let i = 0; i &lt; this.items.length; i++) {
  2148     <xsl:text>        this.offset = new_offset;
  2017 </xsl:text>
  2149 </xsl:text>
  2018     <xsl:text>            let item = this.items[i];
  2150     <xsl:text>        this.relativeness = relativeness;
  2019 </xsl:text>
  2151 </xsl:text>
  2020     <xsl:text>            let orig_item_index = this.index_pool[i];
  2152     <xsl:text>        this.sub_items();
  2021 </xsl:text>
  2153 </xsl:text>
  2022     <xsl:text>            let item_index = this.index_pool[i+this.item_offset];
  2154     <xsl:text>    }
  2023 </xsl:text>
  2155 </xsl:text>
  2024     <xsl:text>            let item_index_offset = item_index - orig_item_index;
  2156     <xsl:text>
  2025 </xsl:text>
  2157 </xsl:text>
  2026     <xsl:text>            for(let widget of item) {
  2158     <xsl:text>    apply_cache() {
  2027 </xsl:text>
  2159 </xsl:text>
  2028     <xsl:text>                todo(widget).call(widget, this.offset + item_index_offset);
  2160     <xsl:text>        this.items.forEach(item=&gt;item.forEach(widget=&gt;widget.apply_cache()));
  2029 </xsl:text>
  2161 </xsl:text>
  2030     <xsl:text>            }
  2162     <xsl:text>    }
       
  2163 </xsl:text>
       
  2164     <xsl:text>
       
  2165 </xsl:text>
       
  2166     <xsl:text>    on_click(opstr, evt) {
       
  2167 </xsl:text>
       
  2168     <xsl:text>        let new_item_offset = eval(String(this.item_offset)+opstr);
       
  2169 </xsl:text>
       
  2170     <xsl:text>        if(new_item_offset + this.items.length &gt; this.index_pool.length) {
       
  2171 </xsl:text>
       
  2172     <xsl:text>            if(this.item_offset + this.items.length == this.index_pool.length)
       
  2173 </xsl:text>
       
  2174     <xsl:text>                new_item_offset = 0;
       
  2175 </xsl:text>
       
  2176     <xsl:text>            else
       
  2177 </xsl:text>
       
  2178     <xsl:text>                new_item_offset = this.index_pool.length - this.items.length;
       
  2179 </xsl:text>
       
  2180     <xsl:text>        } else if(new_item_offset &lt; 0) {
       
  2181 </xsl:text>
       
  2182     <xsl:text>            if(this.item_offset == 0)
       
  2183 </xsl:text>
       
  2184     <xsl:text>                new_item_offset = this.index_pool.length - this.items.length;
       
  2185 </xsl:text>
       
  2186     <xsl:text>            else
       
  2187 </xsl:text>
       
  2188     <xsl:text>                new_item_offset = 0;
  2031 </xsl:text>
  2189 </xsl:text>
  2032     <xsl:text>        }
  2190     <xsl:text>        }
  2033 </xsl:text>
  2191 </xsl:text>
  2034     <xsl:text>    }
       
  2035 </xsl:text>
       
  2036     <xsl:text>
       
  2037 </xsl:text>
       
  2038     <xsl:text>    sub(new_offset=0){
       
  2039 </xsl:text>
       
  2040     <xsl:text>        this.offset = new_offset;
       
  2041 </xsl:text>
       
  2042     <xsl:text>        this.foreach_widgets_do(w=&gt;w.sub);
       
  2043 </xsl:text>
       
  2044     <xsl:text>    }
       
  2045 </xsl:text>
       
  2046     <xsl:text>
       
  2047 </xsl:text>
       
  2048     <xsl:text>    apply_cache() {
       
  2049 </xsl:text>
       
  2050     <xsl:text>        this.foreach_widgets_do(w=&gt;w.apply_cache);
       
  2051 </xsl:text>
       
  2052     <xsl:text>    }
       
  2053 </xsl:text>
       
  2054     <xsl:text>
       
  2055 </xsl:text>
       
  2056     <xsl:text>    on_click(opstr, evt) {
       
  2057 </xsl:text>
       
  2058     <xsl:text>        let new_item_offset = eval(String(this.item_offset)+opstr);
       
  2059 </xsl:text>
       
  2060     <xsl:text>        if(new_item_offset + this.items.length &gt; this.index_pool.length) {
       
  2061 </xsl:text>
       
  2062     <xsl:text>            if(this.item_offset + this.items.length == this.index_pool.length)
       
  2063 </xsl:text>
       
  2064     <xsl:text>                new_item_offset = 0;
       
  2065 </xsl:text>
       
  2066     <xsl:text>            else
       
  2067 </xsl:text>
       
  2068     <xsl:text>                new_item_offset = this.index_pool.length - this.items.length;
       
  2069 </xsl:text>
       
  2070     <xsl:text>        } else if(new_item_offset &lt; 0) {
       
  2071 </xsl:text>
       
  2072     <xsl:text>            if(this.item_offset == 0)
       
  2073 </xsl:text>
       
  2074     <xsl:text>                new_item_offset = this.index_pool.length - this.items.length;
       
  2075 </xsl:text>
       
  2076     <xsl:text>            else
       
  2077 </xsl:text>
       
  2078     <xsl:text>                new_item_offset = 0;
       
  2079 </xsl:text>
       
  2080     <xsl:text>        }
       
  2081 </xsl:text>
       
  2082     <xsl:text>        this.item_offset = new_item_offset;
  2192     <xsl:text>        this.item_offset = new_item_offset;
  2083 </xsl:text>
  2193 </xsl:text>
  2084     <xsl:text>        this.unsub();
  2194     <xsl:text>        this.unsub_items();
  2085 </xsl:text>
  2195 </xsl:text>
  2086     <xsl:text>        this.sub(this.offset);
  2196     <xsl:text>        this.sub_items();
  2087 </xsl:text>
  2197 </xsl:text>
  2088     <xsl:text>        update_subscriptions();
  2198     <xsl:text>        update_subscriptions();
  2089 </xsl:text>
  2199 </xsl:text>
  2090     <xsl:text>        need_cache_apply.push(this);
  2200     <xsl:text>        need_cache_apply.push(this);
  2091 </xsl:text>
  2201 </xsl:text>
  3047 </xsl:text>
  3157 </xsl:text>
  3048           <xsl:text>var need_cache_apply = []; 
  3158           <xsl:text>var need_cache_apply = []; 
  3049 </xsl:text>
  3159 </xsl:text>
  3050           <xsl:text>
  3160           <xsl:text>
  3051 </xsl:text>
  3161 </xsl:text>
  3052           <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
  3162           <xsl:text>
       
  3163 </xsl:text>
       
  3164           <xsl:text>function dispatch_value(index, value) {
       
  3165 </xsl:text>
       
  3166           <xsl:text>    let widgets = subscribers[index];
       
  3167 </xsl:text>
       
  3168           <xsl:text>
       
  3169 </xsl:text>
       
  3170           <xsl:text>    let oldval = cache[index];
       
  3171 </xsl:text>
       
  3172           <xsl:text>    cache[index] = value;
       
  3173 </xsl:text>
       
  3174           <xsl:text>
       
  3175 </xsl:text>
       
  3176           <xsl:text>    if(widgets.size &gt; 0) {
       
  3177 </xsl:text>
       
  3178           <xsl:text>        for(let widget of widgets){
       
  3179 </xsl:text>
       
  3180           <xsl:text>            widget.new_hmi_value(index, value, oldval);
       
  3181 </xsl:text>
       
  3182           <xsl:text>        }
       
  3183 </xsl:text>
       
  3184           <xsl:text>    }
       
  3185 </xsl:text>
       
  3186           <xsl:text>};
       
  3187 </xsl:text>
       
  3188           <xsl:text>
       
  3189 </xsl:text>
       
  3190           <xsl:text>function init_widgets() {
       
  3191 </xsl:text>
       
  3192           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
       
  3193 </xsl:text>
       
  3194           <xsl:text>        let widget = hmi_widgets[id];
       
  3195 </xsl:text>
       
  3196           <xsl:text>        let init = widget.init;
       
  3197 </xsl:text>
       
  3198           <xsl:text>        if(typeof(init) == "function"){
       
  3199 </xsl:text>
       
  3200           <xsl:text>            try {
       
  3201 </xsl:text>
       
  3202           <xsl:text>                init.call(widget);
       
  3203 </xsl:text>
       
  3204           <xsl:text>            } catch(err) {
       
  3205 </xsl:text>
       
  3206           <xsl:text>                console.log(err);
       
  3207 </xsl:text>
       
  3208           <xsl:text>            }
       
  3209 </xsl:text>
       
  3210           <xsl:text>        }
       
  3211 </xsl:text>
       
  3212           <xsl:text>    });
       
  3213 </xsl:text>
       
  3214           <xsl:text>};
       
  3215 </xsl:text>
       
  3216           <xsl:text>
       
  3217 </xsl:text>
       
  3218           <xsl:text>// Open WebSocket to relative "/ws" address
       
  3219 </xsl:text>
       
  3220           <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
       
  3221 </xsl:text>
       
  3222           <xsl:text>ws.binaryType = 'arraybuffer';
       
  3223 </xsl:text>
       
  3224           <xsl:text>
       
  3225 </xsl:text>
       
  3226           <xsl:text>const dvgetters = {
       
  3227 </xsl:text>
       
  3228           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  3229 </xsl:text>
       
  3230           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  3231 </xsl:text>
       
  3232           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  3233 </xsl:text>
       
  3234           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  3235 </xsl:text>
       
  3236           <xsl:text>        size = dv.getInt8(offset);
       
  3237 </xsl:text>
       
  3238           <xsl:text>        return [
       
  3239 </xsl:text>
       
  3240           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  3241 </xsl:text>
       
  3242           <xsl:text>                dv.buffer, /* original buffer */
       
  3243 </xsl:text>
       
  3244           <xsl:text>                offset + 1, /* string starts after size*/
       
  3245 </xsl:text>
       
  3246           <xsl:text>                size /* size of string */
       
  3247 </xsl:text>
       
  3248           <xsl:text>            )), size + 1]; /* total increment */
       
  3249 </xsl:text>
       
  3250           <xsl:text>    }
       
  3251 </xsl:text>
       
  3252           <xsl:text>};
       
  3253 </xsl:text>
       
  3254           <xsl:text>
       
  3255 </xsl:text>
       
  3256           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
       
  3257 </xsl:text>
       
  3258           <xsl:text>function apply_updates() {
       
  3259 </xsl:text>
       
  3260           <xsl:text>    for(let index in updates){
       
  3261 </xsl:text>
       
  3262           <xsl:text>        // serving as a key, index becomes a string
       
  3263 </xsl:text>
       
  3264           <xsl:text>        // -&gt; pass Number(index) instead
       
  3265 </xsl:text>
       
  3266           <xsl:text>        dispatch_value(Number(index), updates[index]);
       
  3267 </xsl:text>
       
  3268           <xsl:text>        delete updates[index];
       
  3269 </xsl:text>
       
  3270           <xsl:text>    }
       
  3271 </xsl:text>
       
  3272           <xsl:text>}
       
  3273 </xsl:text>
       
  3274           <xsl:text>
       
  3275 </xsl:text>
       
  3276           <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
  3277 </xsl:text>
       
  3278           <xsl:text>var requestAnimationFrameID = null;
       
  3279 </xsl:text>
       
  3280           <xsl:text>function animate() {
       
  3281 </xsl:text>
       
  3282           <xsl:text>    // Do the page swith if any one pending
       
  3283 </xsl:text>
       
  3284           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  3285 </xsl:text>
       
  3286           <xsl:text>        switch_visible_page(current_subscribed_page);
       
  3287 </xsl:text>
       
  3288           <xsl:text>    }
       
  3289 </xsl:text>
       
  3290           <xsl:text>
       
  3291 </xsl:text>
       
  3292           <xsl:text>    while(widget = need_cache_apply.pop()){
       
  3293 </xsl:text>
       
  3294           <xsl:text>        widget.apply_cache();
       
  3295 </xsl:text>
       
  3296           <xsl:text>    }
       
  3297 </xsl:text>
       
  3298           <xsl:text>
       
  3299 </xsl:text>
       
  3300           <xsl:text>    if(jumps_need_update) update_jumps();
       
  3301 </xsl:text>
       
  3302           <xsl:text>
       
  3303 </xsl:text>
       
  3304           <xsl:text>    apply_updates();
       
  3305 </xsl:text>
       
  3306           <xsl:text>    requestAnimationFrameID = null;
       
  3307 </xsl:text>
       
  3308           <xsl:text>}
       
  3309 </xsl:text>
       
  3310           <xsl:text>
       
  3311 </xsl:text>
       
  3312           <xsl:text>function requestHMIAnimation() {
       
  3313 </xsl:text>
       
  3314           <xsl:text>    if(requestAnimationFrameID == null){
       
  3315 </xsl:text>
       
  3316           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  3317 </xsl:text>
       
  3318           <xsl:text>    }
       
  3319 </xsl:text>
       
  3320           <xsl:text>}
       
  3321 </xsl:text>
       
  3322           <xsl:text>
       
  3323 </xsl:text>
       
  3324           <xsl:text>// Message reception handler
       
  3325 </xsl:text>
       
  3326           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  3327 </xsl:text>
       
  3328           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  3329 </xsl:text>
       
  3330           <xsl:text>ws.onmessage = function (evt) {
       
  3331 </xsl:text>
       
  3332           <xsl:text>
       
  3333 </xsl:text>
       
  3334           <xsl:text>    let data = evt.data;
       
  3335 </xsl:text>
       
  3336           <xsl:text>    let dv = new DataView(data);
       
  3337 </xsl:text>
       
  3338           <xsl:text>    let i = 0;
  3053 </xsl:text>
  3339 </xsl:text>
  3054           <xsl:text>    try {
  3340           <xsl:text>    try {
  3055 </xsl:text>
  3341 </xsl:text>
  3056           <xsl:text>        let idx = widget.offset ? index - widget.offset : index;
  3342           <xsl:text>        for(let hash_int of hmi_hash) {
  3057 </xsl:text>
  3343 </xsl:text>
  3058           <xsl:text>        let idxidx = widget.indexes.indexOf(idx);
  3344           <xsl:text>            if(hash_int != dv.getUint8(i)){
  3059 </xsl:text>
  3345 </xsl:text>
  3060           <xsl:text>        let d = widget.dispatch;
  3346           <xsl:text>                throw new Error("Hash doesn't match");
  3061 </xsl:text>
  3347 </xsl:text>
  3062           <xsl:text>        if(typeof(d) == "function" &amp;&amp; idxidx == 0){
  3348           <xsl:text>            };
  3063 </xsl:text>
  3349 </xsl:text>
  3064           <xsl:text>            d.call(widget, value, oldval);
  3350           <xsl:text>            i++;
       
  3351 </xsl:text>
       
  3352           <xsl:text>        };
       
  3353 </xsl:text>
       
  3354           <xsl:text>
       
  3355 </xsl:text>
       
  3356           <xsl:text>        while(i &lt; data.byteLength){
       
  3357 </xsl:text>
       
  3358           <xsl:text>            let index = dv.getUint32(i, true);
       
  3359 </xsl:text>
       
  3360           <xsl:text>            i += 4;
       
  3361 </xsl:text>
       
  3362           <xsl:text>            let iectype = hmitree_types[index];
       
  3363 </xsl:text>
       
  3364           <xsl:text>            if(iectype != undefined){
       
  3365 </xsl:text>
       
  3366           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  3367 </xsl:text>
       
  3368           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  3369 </xsl:text>
       
  3370           <xsl:text>                updates[index] = value;
       
  3371 </xsl:text>
       
  3372           <xsl:text>                i += bytesize;
       
  3373 </xsl:text>
       
  3374           <xsl:text>            } else {
       
  3375 </xsl:text>
       
  3376           <xsl:text>                throw new Error("Unknown index "+index);
       
  3377 </xsl:text>
       
  3378           <xsl:text>            }
       
  3379 </xsl:text>
       
  3380           <xsl:text>        };
       
  3381 </xsl:text>
       
  3382           <xsl:text>        // register for rendering on next frame, since there are updates
       
  3383 </xsl:text>
       
  3384           <xsl:text>        requestHMIAnimation();
       
  3385 </xsl:text>
       
  3386           <xsl:text>    } catch(err) {
       
  3387 </xsl:text>
       
  3388           <xsl:text>        // 1003 is for "Unsupported Data"
       
  3389 </xsl:text>
       
  3390           <xsl:text>        // ws.close(1003, err.message);
       
  3391 </xsl:text>
       
  3392           <xsl:text>
       
  3393 </xsl:text>
       
  3394           <xsl:text>        // TODO : remove debug alert ?
       
  3395 </xsl:text>
       
  3396           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  3397 </xsl:text>
       
  3398           <xsl:text>
       
  3399 </xsl:text>
       
  3400           <xsl:text>        // force reload ignoring cache
       
  3401 </xsl:text>
       
  3402           <xsl:text>        location.reload(true);
       
  3403 </xsl:text>
       
  3404           <xsl:text>    }
       
  3405 </xsl:text>
       
  3406           <xsl:text>};
       
  3407 </xsl:text>
       
  3408           <xsl:text>
       
  3409 </xsl:text>
       
  3410           <xsl:text>
       
  3411 </xsl:text>
       
  3412           <xsl:text>function send_blob(data) {
       
  3413 </xsl:text>
       
  3414           <xsl:text>    if(data.length &gt; 0) {
       
  3415 </xsl:text>
       
  3416           <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
       
  3417 </xsl:text>
       
  3418           <xsl:text>    };
       
  3419 </xsl:text>
       
  3420           <xsl:text>};
       
  3421 </xsl:text>
       
  3422           <xsl:text>
       
  3423 </xsl:text>
       
  3424           <xsl:text>const typedarray_types = {
       
  3425 </xsl:text>
       
  3426           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  3427 </xsl:text>
       
  3428           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  3429 </xsl:text>
       
  3430           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  3431 </xsl:text>
       
  3432           <xsl:text>    STRING: (str) =&gt; {
       
  3433 </xsl:text>
       
  3434           <xsl:text>        // beremiz default string max size is 128
       
  3435 </xsl:text>
       
  3436           <xsl:text>        str = str.slice(0,128);
       
  3437 </xsl:text>
       
  3438           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  3439 </xsl:text>
       
  3440           <xsl:text>        binary[0] = str.length;
       
  3441 </xsl:text>
       
  3442           <xsl:text>        for(var i = 0; i &lt; str.length; i++){
       
  3443 </xsl:text>
       
  3444           <xsl:text>            binary[i+1] = str.charCodeAt(i);
  3065 </xsl:text>
  3445 </xsl:text>
  3066           <xsl:text>        }
  3446           <xsl:text>        }
  3067 </xsl:text>
  3447 </xsl:text>
  3068           <xsl:text>        else if(typeof(d) == "object" &amp;&amp; d.length &gt;= idxidx){
  3448           <xsl:text>        return binary;
  3069 </xsl:text>
  3449 </xsl:text>
  3070           <xsl:text>            d[idxidx].call(widget, value, oldval);
  3450           <xsl:text>    }
       
  3451 </xsl:text>
       
  3452           <xsl:text>    /* TODO */
       
  3453 </xsl:text>
       
  3454           <xsl:text>};
       
  3455 </xsl:text>
       
  3456           <xsl:text>
       
  3457 </xsl:text>
       
  3458           <xsl:text>function send_reset() {
       
  3459 </xsl:text>
       
  3460           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  3461 </xsl:text>
       
  3462           <xsl:text>};
       
  3463 </xsl:text>
       
  3464           <xsl:text>
       
  3465 </xsl:text>
       
  3466           <xsl:text>// subscription state, as it should be in hmi server
       
  3467 </xsl:text>
       
  3468           <xsl:text>// hmitree indexed array of integers
       
  3469 </xsl:text>
       
  3470           <xsl:text>var subscriptions =  hmitree_types.map(_ignored =&gt; 0);
       
  3471 </xsl:text>
       
  3472           <xsl:text>
       
  3473 </xsl:text>
       
  3474           <xsl:text>// subscription state as needed by widget now
       
  3475 </xsl:text>
       
  3476           <xsl:text>// hmitree indexed array of Sets of widgets objects
       
  3477 </xsl:text>
       
  3478           <xsl:text>var subscribers = hmitree_types.map(_ignored =&gt; new Set());
       
  3479 </xsl:text>
       
  3480           <xsl:text>
       
  3481 </xsl:text>
       
  3482           <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  3483 </xsl:text>
       
  3484           <xsl:text>// Since dispatch directly calls change_hmi_value,
       
  3485 </xsl:text>
       
  3486           <xsl:text>// PLC will periodically send variable at given frequency
       
  3487 </xsl:text>
       
  3488           <xsl:text>subscribers[heartbeat_index].add({
       
  3489 </xsl:text>
       
  3490           <xsl:text>    /* type: "Watchdog", */
       
  3491 </xsl:text>
       
  3492           <xsl:text>    frequency: 1,
       
  3493 </xsl:text>
       
  3494           <xsl:text>    indexes: [heartbeat_index],
       
  3495 </xsl:text>
       
  3496           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  3497 </xsl:text>
       
  3498           <xsl:text>        apply_hmi_value(heartbeat_index, value+1);
       
  3499 </xsl:text>
       
  3500           <xsl:text>    }
       
  3501 </xsl:text>
       
  3502           <xsl:text>});
       
  3503 </xsl:text>
       
  3504           <xsl:text>
       
  3505 </xsl:text>
       
  3506           <xsl:text>function update_subscriptions() {
       
  3507 </xsl:text>
       
  3508           <xsl:text>    let delta = [];
       
  3509 </xsl:text>
       
  3510           <xsl:text>    for(let index = 0; index &lt; subscribers.length; index++){
       
  3511 </xsl:text>
       
  3512           <xsl:text>        let widgets = subscribers[index];
       
  3513 </xsl:text>
       
  3514           <xsl:text>
       
  3515 </xsl:text>
       
  3516           <xsl:text>        // periods are in ms
       
  3517 </xsl:text>
       
  3518           <xsl:text>        let previous_period = subscriptions[index];
       
  3519 </xsl:text>
       
  3520           <xsl:text>
       
  3521 </xsl:text>
       
  3522           <xsl:text>        // subscribing with a zero period is unsubscribing
       
  3523 </xsl:text>
       
  3524           <xsl:text>        let new_period = 0;
       
  3525 </xsl:text>
       
  3526           <xsl:text>        if(widgets.size &gt; 0) {
       
  3527 </xsl:text>
       
  3528           <xsl:text>            let maxfreq = 0;
       
  3529 </xsl:text>
       
  3530           <xsl:text>            for(let widget of widgets){
       
  3531 </xsl:text>
       
  3532           <xsl:text>                let wf = widget.frequency;
       
  3533 </xsl:text>
       
  3534           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
  3535 </xsl:text>
       
  3536           <xsl:text>                    maxfreq = wf;
       
  3537 </xsl:text>
       
  3538           <xsl:text>            }
       
  3539 </xsl:text>
       
  3540           <xsl:text>
       
  3541 </xsl:text>
       
  3542           <xsl:text>            if(maxfreq != 0)
       
  3543 </xsl:text>
       
  3544           <xsl:text>                new_period = 1000/maxfreq;
  3071 </xsl:text>
  3545 </xsl:text>
  3072           <xsl:text>        }
  3546           <xsl:text>        }
  3073 </xsl:text>
  3547 </xsl:text>
  3074           <xsl:text>        /* else dispatch_0, ..., dispatch_n ? */
  3548           <xsl:text>
  3075 </xsl:text>
  3549 </xsl:text>
  3076           <xsl:text>        /*else {
  3550           <xsl:text>        if(previous_period != new_period) {
  3077 </xsl:text>
  3551 </xsl:text>
  3078           <xsl:text>            throw new Error("Dunno how to dispatch to widget at index = " + index);
  3552           <xsl:text>            subscriptions[index] = new_period;
  3079 </xsl:text>
  3553 </xsl:text>
  3080           <xsl:text>        }*/
  3554           <xsl:text>            delta.push(
  3081 </xsl:text>
  3555 </xsl:text>
  3082           <xsl:text>    } catch(err) {
  3556           <xsl:text>                new Uint8Array([2]), /* subscribe = 2 */
  3083 </xsl:text>
  3557 </xsl:text>
  3084           <xsl:text>        console.log(err);
  3558           <xsl:text>                new Uint32Array([index]),
       
  3559 </xsl:text>
       
  3560           <xsl:text>                new Uint16Array([new_period]));
       
  3561 </xsl:text>
       
  3562           <xsl:text>        }
  3085 </xsl:text>
  3563 </xsl:text>
  3086           <xsl:text>    }
  3564           <xsl:text>    }
  3087 </xsl:text>
  3565 </xsl:text>
       
  3566           <xsl:text>    send_blob(delta);
       
  3567 </xsl:text>
       
  3568           <xsl:text>};
       
  3569 </xsl:text>
       
  3570           <xsl:text>
       
  3571 </xsl:text>
       
  3572           <xsl:text>function send_hmi_value(index, value) {
       
  3573 </xsl:text>
       
  3574           <xsl:text>    let iectype = hmitree_types[index];
       
  3575 </xsl:text>
       
  3576           <xsl:text>    let tobinary = typedarray_types[iectype];
       
  3577 </xsl:text>
       
  3578           <xsl:text>    send_blob([
       
  3579 </xsl:text>
       
  3580           <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
       
  3581 </xsl:text>
       
  3582           <xsl:text>        new Uint32Array([index]),
       
  3583 </xsl:text>
       
  3584           <xsl:text>        tobinary(value)]);
       
  3585 </xsl:text>
       
  3586           <xsl:text>
       
  3587 </xsl:text>
       
  3588           <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
  3589 </xsl:text>
       
  3590           <xsl:text>    // cache[index] = value;
       
  3591 </xsl:text>
       
  3592           <xsl:text>};
       
  3593 </xsl:text>
       
  3594           <xsl:text>
       
  3595 </xsl:text>
       
  3596           <xsl:text>function apply_hmi_value(index, new_val) {
       
  3597 </xsl:text>
       
  3598           <xsl:text>    let old_val = cache[index]
       
  3599 </xsl:text>
       
  3600           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  3601 </xsl:text>
       
  3602           <xsl:text>        send_hmi_value(index, new_val);
       
  3603 </xsl:text>
       
  3604           <xsl:text>    return new_val;
       
  3605 </xsl:text>
  3088           <xsl:text>}
  3606           <xsl:text>}
  3089 </xsl:text>
  3607 </xsl:text>
  3090           <xsl:text>
  3608           <xsl:text>
  3091 </xsl:text>
  3609 </xsl:text>
  3092           <xsl:text>function dispatch_value(index, value) {
  3610           <xsl:text>quotes = {"'":null, '"':null};
  3093 </xsl:text>
  3611 </xsl:text>
  3094           <xsl:text>    let widgets = subscribers[index];
  3612           <xsl:text>
  3095 </xsl:text>
  3613 </xsl:text>
  3096           <xsl:text>
  3614           <xsl:text>function change_hmi_value(index, opstr) {
  3097 </xsl:text>
  3615 </xsl:text>
  3098           <xsl:text>    let oldval = cache[index];
  3616           <xsl:text>    let op = opstr[0];
  3099 </xsl:text>
  3617 </xsl:text>
  3100           <xsl:text>    cache[index] = value;
  3618           <xsl:text>    let given_val;
  3101 </xsl:text>
  3619 </xsl:text>
  3102           <xsl:text>
  3620           <xsl:text>    if(opstr.length &lt; 2) 
  3103 </xsl:text>
  3621 </xsl:text>
  3104           <xsl:text>    if(widgets.size &gt; 0) {
  3622           <xsl:text>        return undefined; // TODO raise
  3105 </xsl:text>
  3623 </xsl:text>
  3106           <xsl:text>        for(let widget of widgets){
  3624           <xsl:text>    if(opstr[1] in quotes){
  3107 </xsl:text>
  3625 </xsl:text>
  3108           <xsl:text>            dispatch_value_to_widget(widget, index, value, oldval);
  3626           <xsl:text>        if(opstr.length &lt; 3) 
       
  3627 </xsl:text>
       
  3628           <xsl:text>            return undefined; // TODO raise
       
  3629 </xsl:text>
       
  3630           <xsl:text>        if(opstr[opstr.length-1] == opstr[1]){
       
  3631 </xsl:text>
       
  3632           <xsl:text>            given_val = opstr.slice(2,opstr.length-1);
  3109 </xsl:text>
  3633 </xsl:text>
  3110           <xsl:text>        }
  3634           <xsl:text>        }
  3111 </xsl:text>
  3635 </xsl:text>
       
  3636           <xsl:text>    } else {
       
  3637 </xsl:text>
       
  3638           <xsl:text>        given_val = Number(opstr.slice(1));
       
  3639 </xsl:text>
  3112           <xsl:text>    }
  3640           <xsl:text>    }
  3113 </xsl:text>
  3641 </xsl:text>
       
  3642           <xsl:text>    let old_val = cache[index];
       
  3643 </xsl:text>
       
  3644           <xsl:text>    let new_val;
       
  3645 </xsl:text>
       
  3646           <xsl:text>    switch(op){
       
  3647 </xsl:text>
       
  3648           <xsl:text>      case "=":
       
  3649 </xsl:text>
       
  3650           <xsl:text>        new_val = given_val;
       
  3651 </xsl:text>
       
  3652           <xsl:text>        break;
       
  3653 </xsl:text>
       
  3654           <xsl:text>      case "+":
       
  3655 </xsl:text>
       
  3656           <xsl:text>        new_val = old_val + given_val;
       
  3657 </xsl:text>
       
  3658           <xsl:text>        break;
       
  3659 </xsl:text>
       
  3660           <xsl:text>      case "-":
       
  3661 </xsl:text>
       
  3662           <xsl:text>        new_val = old_val - given_val;
       
  3663 </xsl:text>
       
  3664           <xsl:text>        break;
       
  3665 </xsl:text>
       
  3666           <xsl:text>      case "*":
       
  3667 </xsl:text>
       
  3668           <xsl:text>        new_val = old_val * given_val;
       
  3669 </xsl:text>
       
  3670           <xsl:text>        break;
       
  3671 </xsl:text>
       
  3672           <xsl:text>      case "/":
       
  3673 </xsl:text>
       
  3674           <xsl:text>        new_val = old_val / given_val;
       
  3675 </xsl:text>
       
  3676           <xsl:text>        break;
       
  3677 </xsl:text>
       
  3678           <xsl:text>    }
       
  3679 </xsl:text>
       
  3680           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  3681 </xsl:text>
       
  3682           <xsl:text>        send_hmi_value(index, new_val);
       
  3683 </xsl:text>
       
  3684           <xsl:text>    // TODO else raise
       
  3685 </xsl:text>
       
  3686           <xsl:text>    return new_val;
       
  3687 </xsl:text>
       
  3688           <xsl:text>}
       
  3689 </xsl:text>
       
  3690           <xsl:text>
       
  3691 </xsl:text>
       
  3692           <xsl:text>var current_visible_page;
       
  3693 </xsl:text>
       
  3694           <xsl:text>var current_subscribed_page;
       
  3695 </xsl:text>
       
  3696           <xsl:text>var current_page_index;
       
  3697 </xsl:text>
       
  3698           <xsl:text>
       
  3699 </xsl:text>
       
  3700           <xsl:text>function prepare_svg() {
       
  3701 </xsl:text>
       
  3702           <xsl:text>    for(let eltid in detachable_elements){
       
  3703 </xsl:text>
       
  3704           <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
  3705 </xsl:text>
       
  3706           <xsl:text>        parent.removeChild(element);
       
  3707 </xsl:text>
       
  3708           <xsl:text>    }
       
  3709 </xsl:text>
  3114           <xsl:text>};
  3710           <xsl:text>};
  3115 </xsl:text>
  3711 </xsl:text>
  3116           <xsl:text>
  3712           <xsl:text>
  3117 </xsl:text>
  3713 </xsl:text>
  3118           <xsl:text>function init_widgets() {
  3714           <xsl:text>function switch_page(page_name, page_index) {
  3119 </xsl:text>
  3715 </xsl:text>
  3120           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
  3716           <xsl:text>    if(current_subscribed_page != current_visible_page){
  3121 </xsl:text>
  3717 </xsl:text>
  3122           <xsl:text>        let widget = hmi_widgets[id];
  3718           <xsl:text>        /* page switch already going */
  3123 </xsl:text>
  3719 </xsl:text>
  3124           <xsl:text>        let init = widget.init;
  3720           <xsl:text>        /* TODO LOG ERROR */
  3125 </xsl:text>
  3721 </xsl:text>
  3126           <xsl:text>        if(typeof(init) == "function"){
  3722           <xsl:text>        return false;
  3127 </xsl:text>
       
  3128           <xsl:text>            try {
       
  3129 </xsl:text>
       
  3130           <xsl:text>                init.call(widget);
       
  3131 </xsl:text>
       
  3132           <xsl:text>            } catch(err) {
       
  3133 </xsl:text>
       
  3134           <xsl:text>                console.log(err);
       
  3135 </xsl:text>
       
  3136           <xsl:text>            }
       
  3137 </xsl:text>
       
  3138           <xsl:text>        }
       
  3139 </xsl:text>
       
  3140           <xsl:text>    });
       
  3141 </xsl:text>
       
  3142           <xsl:text>};
       
  3143 </xsl:text>
       
  3144           <xsl:text>
       
  3145 </xsl:text>
       
  3146           <xsl:text>// Open WebSocket to relative "/ws" address
       
  3147 </xsl:text>
       
  3148           <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
       
  3149 </xsl:text>
       
  3150           <xsl:text>ws.binaryType = 'arraybuffer';
       
  3151 </xsl:text>
       
  3152           <xsl:text>
       
  3153 </xsl:text>
       
  3154           <xsl:text>const dvgetters = {
       
  3155 </xsl:text>
       
  3156           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  3157 </xsl:text>
       
  3158           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  3159 </xsl:text>
       
  3160           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  3161 </xsl:text>
       
  3162           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  3163 </xsl:text>
       
  3164           <xsl:text>        size = dv.getInt8(offset);
       
  3165 </xsl:text>
       
  3166           <xsl:text>        return [
       
  3167 </xsl:text>
       
  3168           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  3169 </xsl:text>
       
  3170           <xsl:text>                dv.buffer, /* original buffer */
       
  3171 </xsl:text>
       
  3172           <xsl:text>                offset + 1, /* string starts after size*/
       
  3173 </xsl:text>
       
  3174           <xsl:text>                size /* size of string */
       
  3175 </xsl:text>
       
  3176           <xsl:text>            )), size + 1]; /* total increment */
       
  3177 </xsl:text>
  3723 </xsl:text>
  3178           <xsl:text>    }
  3724           <xsl:text>    }
  3179 </xsl:text>
  3725 </xsl:text>
  3180           <xsl:text>};
  3726           <xsl:text>
  3181 </xsl:text>
  3727 </xsl:text>
  3182           <xsl:text>
  3728           <xsl:text>    if(page_name == undefined)
  3183 </xsl:text>
  3729 </xsl:text>
  3184           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
  3730           <xsl:text>        page_name = current_subscribed_page;
  3185 </xsl:text>
  3731 </xsl:text>
  3186           <xsl:text>function apply_updates() {
  3732           <xsl:text>
  3187 </xsl:text>
  3733 </xsl:text>
  3188           <xsl:text>    for(let index in updates){
  3734           <xsl:text>
  3189 </xsl:text>
  3735 </xsl:text>
  3190           <xsl:text>        // serving as a key, index becomes a string
  3736           <xsl:text>    let old_desc = page_desc[current_subscribed_page];
  3191 </xsl:text>
  3737 </xsl:text>
  3192           <xsl:text>        // -&gt; pass Number(index) instead
  3738           <xsl:text>    let new_desc = page_desc[page_name];
  3193 </xsl:text>
  3739 </xsl:text>
  3194           <xsl:text>        dispatch_value(Number(index), updates[index]);
  3740           <xsl:text>
  3195 </xsl:text>
  3741 </xsl:text>
  3196           <xsl:text>        delete updates[index];
  3742           <xsl:text>    if(new_desc == undefined){
       
  3743 </xsl:text>
       
  3744           <xsl:text>        /* TODO LOG ERROR */
       
  3745 </xsl:text>
       
  3746           <xsl:text>        return false;
  3197 </xsl:text>
  3747 </xsl:text>
  3198           <xsl:text>    }
  3748           <xsl:text>    }
  3199 </xsl:text>
  3749 </xsl:text>
  3200           <xsl:text>}
  3750           <xsl:text>
  3201 </xsl:text>
  3751 </xsl:text>
  3202           <xsl:text>
  3752           <xsl:text>    if(page_index == undefined){
  3203 </xsl:text>
  3753 </xsl:text>
  3204           <xsl:text>// Called on requestAnimationFrame, modifies DOM
  3754           <xsl:text>        page_index = new_desc.page_index;
  3205 </xsl:text>
       
  3206           <xsl:text>var requestAnimationFrameID = null;
       
  3207 </xsl:text>
       
  3208           <xsl:text>function animate() {
       
  3209 </xsl:text>
       
  3210           <xsl:text>    // Do the page swith if any one pending
       
  3211 </xsl:text>
       
  3212           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  3213 </xsl:text>
       
  3214           <xsl:text>        switch_visible_page(current_subscribed_page);
       
  3215 </xsl:text>
  3755 </xsl:text>
  3216           <xsl:text>    }
  3756           <xsl:text>    }
  3217 </xsl:text>
  3757 </xsl:text>
  3218           <xsl:text>
  3758           <xsl:text>
  3219 </xsl:text>
  3759 </xsl:text>
  3220           <xsl:text>    while(widget = need_cache_apply.pop()){
  3760           <xsl:text>    if(old_desc){
  3221 </xsl:text>
  3761 </xsl:text>
  3222           <xsl:text>        widget.apply_cache();
  3762           <xsl:text>        old_desc.widgets.map(([widget,relativeness])=&gt;widget.unsub());
  3223 </xsl:text>
  3763 </xsl:text>
  3224           <xsl:text>    }
  3764           <xsl:text>    }
  3225 </xsl:text>
  3765 </xsl:text>
  3226           <xsl:text>
       
  3227 </xsl:text>
       
  3228           <xsl:text>    if(jumps_need_update) update_jumps();
       
  3229 </xsl:text>
       
  3230           <xsl:text>
       
  3231 </xsl:text>
       
  3232           <xsl:text>    apply_updates();
       
  3233 </xsl:text>
       
  3234           <xsl:text>    requestAnimationFrameID = null;
       
  3235 </xsl:text>
       
  3236           <xsl:text>}
       
  3237 </xsl:text>
       
  3238           <xsl:text>
       
  3239 </xsl:text>
       
  3240           <xsl:text>function requestHMIAnimation() {
       
  3241 </xsl:text>
       
  3242           <xsl:text>    if(requestAnimationFrameID == null){
       
  3243 </xsl:text>
       
  3244           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  3245 </xsl:text>
       
  3246           <xsl:text>    }
       
  3247 </xsl:text>
       
  3248           <xsl:text>}
       
  3249 </xsl:text>
       
  3250           <xsl:text>
       
  3251 </xsl:text>
       
  3252           <xsl:text>// Message reception handler
       
  3253 </xsl:text>
       
  3254           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  3255 </xsl:text>
       
  3256           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  3257 </xsl:text>
       
  3258           <xsl:text>ws.onmessage = function (evt) {
       
  3259 </xsl:text>
       
  3260           <xsl:text>
       
  3261 </xsl:text>
       
  3262           <xsl:text>    let data = evt.data;
       
  3263 </xsl:text>
       
  3264           <xsl:text>    let dv = new DataView(data);
       
  3265 </xsl:text>
       
  3266           <xsl:text>    let i = 0;
       
  3267 </xsl:text>
       
  3268           <xsl:text>    try {
       
  3269 </xsl:text>
       
  3270           <xsl:text>        for(let hash_int of hmi_hash) {
       
  3271 </xsl:text>
       
  3272           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  3273 </xsl:text>
       
  3274           <xsl:text>                throw new Error("Hash doesn't match");
       
  3275 </xsl:text>
       
  3276           <xsl:text>            };
       
  3277 </xsl:text>
       
  3278           <xsl:text>            i++;
       
  3279 </xsl:text>
       
  3280           <xsl:text>        };
       
  3281 </xsl:text>
       
  3282           <xsl:text>
       
  3283 </xsl:text>
       
  3284           <xsl:text>        while(i &lt; data.byteLength){
       
  3285 </xsl:text>
       
  3286           <xsl:text>            let index = dv.getUint32(i, true);
       
  3287 </xsl:text>
       
  3288           <xsl:text>            i += 4;
       
  3289 </xsl:text>
       
  3290           <xsl:text>            let iectype = hmitree_types[index];
       
  3291 </xsl:text>
       
  3292           <xsl:text>            if(iectype != undefined){
       
  3293 </xsl:text>
       
  3294           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  3295 </xsl:text>
       
  3296           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  3297 </xsl:text>
       
  3298           <xsl:text>                updates[index] = value;
       
  3299 </xsl:text>
       
  3300           <xsl:text>                i += bytesize;
       
  3301 </xsl:text>
       
  3302           <xsl:text>            } else {
       
  3303 </xsl:text>
       
  3304           <xsl:text>                throw new Error("Unknown index "+index);
       
  3305 </xsl:text>
       
  3306           <xsl:text>            }
       
  3307 </xsl:text>
       
  3308           <xsl:text>        };
       
  3309 </xsl:text>
       
  3310           <xsl:text>        // register for rendering on next frame, since there are updates
       
  3311 </xsl:text>
       
  3312           <xsl:text>        requestHMIAnimation();
       
  3313 </xsl:text>
       
  3314           <xsl:text>    } catch(err) {
       
  3315 </xsl:text>
       
  3316           <xsl:text>        // 1003 is for "Unsupported Data"
       
  3317 </xsl:text>
       
  3318           <xsl:text>        // ws.close(1003, err.message);
       
  3319 </xsl:text>
       
  3320           <xsl:text>
       
  3321 </xsl:text>
       
  3322           <xsl:text>        // TODO : remove debug alert ?
       
  3323 </xsl:text>
       
  3324           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  3325 </xsl:text>
       
  3326           <xsl:text>
       
  3327 </xsl:text>
       
  3328           <xsl:text>        // force reload ignoring cache
       
  3329 </xsl:text>
       
  3330           <xsl:text>        location.reload(true);
       
  3331 </xsl:text>
       
  3332           <xsl:text>    }
       
  3333 </xsl:text>
       
  3334           <xsl:text>};
       
  3335 </xsl:text>
       
  3336           <xsl:text>
       
  3337 </xsl:text>
       
  3338           <xsl:text>
       
  3339 </xsl:text>
       
  3340           <xsl:text>function send_blob(data) {
       
  3341 </xsl:text>
       
  3342           <xsl:text>    if(data.length &gt; 0) {
       
  3343 </xsl:text>
       
  3344           <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
       
  3345 </xsl:text>
       
  3346           <xsl:text>    };
       
  3347 </xsl:text>
       
  3348           <xsl:text>};
       
  3349 </xsl:text>
       
  3350           <xsl:text>
       
  3351 </xsl:text>
       
  3352           <xsl:text>const typedarray_types = {
       
  3353 </xsl:text>
       
  3354           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  3355 </xsl:text>
       
  3356           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  3357 </xsl:text>
       
  3358           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  3359 </xsl:text>
       
  3360           <xsl:text>    STRING: (str) =&gt; {
       
  3361 </xsl:text>
       
  3362           <xsl:text>        // beremiz default string max size is 128
       
  3363 </xsl:text>
       
  3364           <xsl:text>        str = str.slice(0,128);
       
  3365 </xsl:text>
       
  3366           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  3367 </xsl:text>
       
  3368           <xsl:text>        binary[0] = str.length;
       
  3369 </xsl:text>
       
  3370           <xsl:text>        for(var i = 0; i &lt; str.length; i++){
       
  3371 </xsl:text>
       
  3372           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  3373 </xsl:text>
       
  3374           <xsl:text>        }
       
  3375 </xsl:text>
       
  3376           <xsl:text>        return binary;
       
  3377 </xsl:text>
       
  3378           <xsl:text>    }
       
  3379 </xsl:text>
       
  3380           <xsl:text>    /* TODO */
       
  3381 </xsl:text>
       
  3382           <xsl:text>};
       
  3383 </xsl:text>
       
  3384           <xsl:text>
       
  3385 </xsl:text>
       
  3386           <xsl:text>function send_reset() {
       
  3387 </xsl:text>
       
  3388           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  3389 </xsl:text>
       
  3390           <xsl:text>};
       
  3391 </xsl:text>
       
  3392           <xsl:text>
       
  3393 </xsl:text>
       
  3394           <xsl:text>// subscription state, as it should be in hmi server
       
  3395 </xsl:text>
       
  3396           <xsl:text>// hmitree indexed array of integers
       
  3397 </xsl:text>
       
  3398           <xsl:text>var subscriptions =  hmitree_types.map(_ignored =&gt; 0);
       
  3399 </xsl:text>
       
  3400           <xsl:text>
       
  3401 </xsl:text>
       
  3402           <xsl:text>// subscription state as needed by widget now
       
  3403 </xsl:text>
       
  3404           <xsl:text>// hmitree indexed array of Sets of widgets objects
       
  3405 </xsl:text>
       
  3406           <xsl:text>var subscribers = hmitree_types.map(_ignored =&gt; new Set());
       
  3407 </xsl:text>
       
  3408           <xsl:text>
       
  3409 </xsl:text>
       
  3410           <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  3411 </xsl:text>
       
  3412           <xsl:text>// Since dispatch directly calls change_hmi_value,
       
  3413 </xsl:text>
       
  3414           <xsl:text>// PLC will periodically send variable at given frequency
       
  3415 </xsl:text>
       
  3416           <xsl:text>subscribers[heartbeat_index].add({
       
  3417 </xsl:text>
       
  3418           <xsl:text>    /* type: "Watchdog", */
       
  3419 </xsl:text>
       
  3420           <xsl:text>    frequency: 1,
       
  3421 </xsl:text>
       
  3422           <xsl:text>    indexes: [heartbeat_index],
       
  3423 </xsl:text>
       
  3424           <xsl:text>    dispatch: function(value) {
       
  3425 </xsl:text>
       
  3426           <xsl:text>        apply_hmi_value(heartbeat_index, value+1);
       
  3427 </xsl:text>
       
  3428           <xsl:text>    }
       
  3429 </xsl:text>
       
  3430           <xsl:text>});
       
  3431 </xsl:text>
       
  3432           <xsl:text>
       
  3433 </xsl:text>
       
  3434           <xsl:text>function update_subscriptions() {
       
  3435 </xsl:text>
       
  3436           <xsl:text>    let delta = [];
       
  3437 </xsl:text>
       
  3438           <xsl:text>    for(let index = 0; index &lt; subscribers.length; index++){
       
  3439 </xsl:text>
       
  3440           <xsl:text>        let widgets = subscribers[index];
       
  3441 </xsl:text>
       
  3442           <xsl:text>
       
  3443 </xsl:text>
       
  3444           <xsl:text>        // periods are in ms
       
  3445 </xsl:text>
       
  3446           <xsl:text>        let previous_period = subscriptions[index];
       
  3447 </xsl:text>
       
  3448           <xsl:text>
       
  3449 </xsl:text>
       
  3450           <xsl:text>        // subscribing with a zero period is unsubscribing
       
  3451 </xsl:text>
       
  3452           <xsl:text>        let new_period = 0;
       
  3453 </xsl:text>
       
  3454           <xsl:text>        if(widgets.size &gt; 0) {
       
  3455 </xsl:text>
       
  3456           <xsl:text>            let maxfreq = 0;
       
  3457 </xsl:text>
       
  3458           <xsl:text>            for(let widget of widgets){
       
  3459 </xsl:text>
       
  3460           <xsl:text>                let wf = widget.frequency;
       
  3461 </xsl:text>
       
  3462           <xsl:text>                if(wf != undefined &amp;&amp; maxfreq &lt; wf)
       
  3463 </xsl:text>
       
  3464           <xsl:text>                    maxfreq = wf;
       
  3465 </xsl:text>
       
  3466           <xsl:text>            }
       
  3467 </xsl:text>
       
  3468           <xsl:text>
       
  3469 </xsl:text>
       
  3470           <xsl:text>            if(maxfreq != 0)
       
  3471 </xsl:text>
       
  3472           <xsl:text>                new_period = 1000/maxfreq;
       
  3473 </xsl:text>
       
  3474           <xsl:text>        }
       
  3475 </xsl:text>
       
  3476           <xsl:text>
       
  3477 </xsl:text>
       
  3478           <xsl:text>        if(previous_period != new_period) {
       
  3479 </xsl:text>
       
  3480           <xsl:text>            subscriptions[index] = new_period;
       
  3481 </xsl:text>
       
  3482           <xsl:text>            delta.push(
       
  3483 </xsl:text>
       
  3484           <xsl:text>                new Uint8Array([2]), /* subscribe = 2 */
       
  3485 </xsl:text>
       
  3486           <xsl:text>                new Uint32Array([index]),
       
  3487 </xsl:text>
       
  3488           <xsl:text>                new Uint16Array([new_period]));
       
  3489 </xsl:text>
       
  3490           <xsl:text>        }
       
  3491 </xsl:text>
       
  3492           <xsl:text>    }
       
  3493 </xsl:text>
       
  3494           <xsl:text>    send_blob(delta);
       
  3495 </xsl:text>
       
  3496           <xsl:text>};
       
  3497 </xsl:text>
       
  3498           <xsl:text>
       
  3499 </xsl:text>
       
  3500           <xsl:text>function send_hmi_value(index, value) {
       
  3501 </xsl:text>
       
  3502           <xsl:text>    let iectype = hmitree_types[index];
       
  3503 </xsl:text>
       
  3504           <xsl:text>    let tobinary = typedarray_types[iectype];
       
  3505 </xsl:text>
       
  3506           <xsl:text>    send_blob([
       
  3507 </xsl:text>
       
  3508           <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
       
  3509 </xsl:text>
       
  3510           <xsl:text>        new Uint32Array([index]),
       
  3511 </xsl:text>
       
  3512           <xsl:text>        tobinary(value)]);
       
  3513 </xsl:text>
       
  3514           <xsl:text>
       
  3515 </xsl:text>
       
  3516           <xsl:text>    // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf
       
  3517 </xsl:text>
       
  3518           <xsl:text>    // cache[index] = value;
       
  3519 </xsl:text>
       
  3520           <xsl:text>};
       
  3521 </xsl:text>
       
  3522           <xsl:text>
       
  3523 </xsl:text>
       
  3524           <xsl:text>function apply_hmi_value(index, new_val) {
       
  3525 </xsl:text>
       
  3526           <xsl:text>    let old_val = cache[index]
       
  3527 </xsl:text>
       
  3528           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  3529 </xsl:text>
       
  3530           <xsl:text>        send_hmi_value(index, new_val);
       
  3531 </xsl:text>
       
  3532           <xsl:text>    return new_val;
       
  3533 </xsl:text>
       
  3534           <xsl:text>}
       
  3535 </xsl:text>
       
  3536           <xsl:text>
       
  3537 </xsl:text>
       
  3538           <xsl:text>quotes = {"'":null, '"':null};
       
  3539 </xsl:text>
       
  3540           <xsl:text>
       
  3541 </xsl:text>
       
  3542           <xsl:text>function change_hmi_value(index, opstr) {
       
  3543 </xsl:text>
       
  3544           <xsl:text>    let op = opstr[0];
       
  3545 </xsl:text>
       
  3546           <xsl:text>    let given_val;
       
  3547 </xsl:text>
       
  3548           <xsl:text>    if(opstr.length &lt; 2) 
       
  3549 </xsl:text>
       
  3550           <xsl:text>        return undefined; // TODO raise
       
  3551 </xsl:text>
       
  3552           <xsl:text>    if(opstr[1] in quotes){
       
  3553 </xsl:text>
       
  3554           <xsl:text>        if(opstr.length &lt; 3) 
       
  3555 </xsl:text>
       
  3556           <xsl:text>            return undefined; // TODO raise
       
  3557 </xsl:text>
       
  3558           <xsl:text>        if(opstr[opstr.length-1] == opstr[1]){
       
  3559 </xsl:text>
       
  3560           <xsl:text>            given_val = opstr.slice(2,opstr.length-1);
       
  3561 </xsl:text>
       
  3562           <xsl:text>        }
       
  3563 </xsl:text>
       
  3564           <xsl:text>    } else {
       
  3565 </xsl:text>
       
  3566           <xsl:text>        given_val = Number(opstr.slice(1));
       
  3567 </xsl:text>
       
  3568           <xsl:text>    }
       
  3569 </xsl:text>
       
  3570           <xsl:text>    let old_val = cache[index];
       
  3571 </xsl:text>
       
  3572           <xsl:text>    let new_val;
       
  3573 </xsl:text>
       
  3574           <xsl:text>    switch(op){
       
  3575 </xsl:text>
       
  3576           <xsl:text>      case "=":
       
  3577 </xsl:text>
       
  3578           <xsl:text>        new_val = given_val;
       
  3579 </xsl:text>
       
  3580           <xsl:text>        break;
       
  3581 </xsl:text>
       
  3582           <xsl:text>      case "+":
       
  3583 </xsl:text>
       
  3584           <xsl:text>        new_val = old_val + given_val;
       
  3585 </xsl:text>
       
  3586           <xsl:text>        break;
       
  3587 </xsl:text>
       
  3588           <xsl:text>      case "-":
       
  3589 </xsl:text>
       
  3590           <xsl:text>        new_val = old_val - given_val;
       
  3591 </xsl:text>
       
  3592           <xsl:text>        break;
       
  3593 </xsl:text>
       
  3594           <xsl:text>      case "*":
       
  3595 </xsl:text>
       
  3596           <xsl:text>        new_val = old_val * given_val;
       
  3597 </xsl:text>
       
  3598           <xsl:text>        break;
       
  3599 </xsl:text>
       
  3600           <xsl:text>      case "/":
       
  3601 </xsl:text>
       
  3602           <xsl:text>        new_val = old_val / given_val;
       
  3603 </xsl:text>
       
  3604           <xsl:text>        break;
       
  3605 </xsl:text>
       
  3606           <xsl:text>    }
       
  3607 </xsl:text>
       
  3608           <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
       
  3609 </xsl:text>
       
  3610           <xsl:text>        send_hmi_value(index, new_val);
       
  3611 </xsl:text>
       
  3612           <xsl:text>    // TODO else raise
       
  3613 </xsl:text>
       
  3614           <xsl:text>    return new_val;
       
  3615 </xsl:text>
       
  3616           <xsl:text>}
       
  3617 </xsl:text>
       
  3618           <xsl:text>
       
  3619 </xsl:text>
       
  3620           <xsl:text>var current_visible_page;
       
  3621 </xsl:text>
       
  3622           <xsl:text>var current_subscribed_page;
       
  3623 </xsl:text>
       
  3624           <xsl:text>var current_page_index;
       
  3625 </xsl:text>
       
  3626           <xsl:text>
       
  3627 </xsl:text>
       
  3628           <xsl:text>function prepare_svg() {
       
  3629 </xsl:text>
       
  3630           <xsl:text>    for(let eltid in detachable_elements){
       
  3631 </xsl:text>
       
  3632           <xsl:text>        let [element,parent] = detachable_elements[eltid];
       
  3633 </xsl:text>
       
  3634           <xsl:text>        parent.removeChild(element);
       
  3635 </xsl:text>
       
  3636           <xsl:text>    }
       
  3637 </xsl:text>
       
  3638           <xsl:text>};
       
  3639 </xsl:text>
       
  3640           <xsl:text>
       
  3641 </xsl:text>
       
  3642           <xsl:text>function switch_page(page_name, page_index) {
       
  3643 </xsl:text>
       
  3644           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
  3645 </xsl:text>
       
  3646           <xsl:text>        /* page switch already going */
       
  3647 </xsl:text>
       
  3648           <xsl:text>        /* TODO LOG ERROR */
       
  3649 </xsl:text>
       
  3650           <xsl:text>        return false;
       
  3651 </xsl:text>
       
  3652           <xsl:text>    }
       
  3653 </xsl:text>
       
  3654           <xsl:text>
       
  3655 </xsl:text>
       
  3656           <xsl:text>    if(page_name == undefined)
       
  3657 </xsl:text>
       
  3658           <xsl:text>        page_name = current_subscribed_page;
       
  3659 </xsl:text>
       
  3660           <xsl:text>
       
  3661 </xsl:text>
       
  3662           <xsl:text>
       
  3663 </xsl:text>
       
  3664           <xsl:text>    let old_desc = page_desc[current_subscribed_page];
       
  3665 </xsl:text>
       
  3666           <xsl:text>    let new_desc = page_desc[page_name];
       
  3667 </xsl:text>
       
  3668           <xsl:text>
       
  3669 </xsl:text>
       
  3670           <xsl:text>    if(new_desc == undefined){
       
  3671 </xsl:text>
       
  3672           <xsl:text>        /* TODO LOG ERROR */
       
  3673 </xsl:text>
       
  3674           <xsl:text>        return false;
       
  3675 </xsl:text>
       
  3676           <xsl:text>    }
       
  3677 </xsl:text>
       
  3678           <xsl:text>
       
  3679 </xsl:text>
       
  3680           <xsl:text>    if(page_index == undefined){
       
  3681 </xsl:text>
       
  3682           <xsl:text>        page_index = new_desc.page_index;
       
  3683 </xsl:text>
       
  3684           <xsl:text>    }
       
  3685 </xsl:text>
       
  3686           <xsl:text>
       
  3687 </xsl:text>
       
  3688           <xsl:text>    if(old_desc){
       
  3689 </xsl:text>
       
  3690           <xsl:text>        old_desc.absolute_widgets.map(w=&gt;w.unsub());
       
  3691 </xsl:text>
       
  3692           <xsl:text>        old_desc.relative_widgets.map(w=&gt;w.unsub());
       
  3693 </xsl:text>
       
  3694           <xsl:text>    }
       
  3695 </xsl:text>
       
  3696           <xsl:text>    new_desc.absolute_widgets.map(w=&gt;w.sub());
       
  3697 </xsl:text>
       
  3698           <xsl:text>    var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
  3766           <xsl:text>    var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index;
  3699 </xsl:text>
  3767 </xsl:text>
  3700           <xsl:text>    new_desc.relative_widgets.map(w=&gt;w.sub(new_offset));
  3768           <xsl:text>    new_desc.widgets.map(([widget,relativeness])=&gt;widget.sub(new_offset,relativeness));
  3701 </xsl:text>
  3769 </xsl:text>
  3702           <xsl:text>
  3770           <xsl:text>
  3703 </xsl:text>
  3771 </xsl:text>
  3704           <xsl:text>    update_subscriptions();
  3772           <xsl:text>    update_subscriptions();
  3705 </xsl:text>
  3773 </xsl:text>