SVGHMI: Added HMI:VarInitPersistent to initialize persistent HMI_LOCAL and PAGE_LOCAL variables, stored as cookies in browser. svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Tue, 09 Feb 2021 07:46:02 +0100
branchsvghmi
changeset 3128 32a4675af377
parent 3127 d4dfd47f8156
child 3129 f2709923c82c
SVGHMI: Added HMI:VarInitPersistent to initialize persistent HMI_LOCAL and PAGE_LOCAL variables, stored as cookies in browser.
svghmi/gen_index_xhtml.xslt
svghmi/svghmi.js
svghmi/widgets_common.ysl2
--- a/svghmi/gen_index_xhtml.xslt	Tue Feb 09 07:41:24 2021 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Tue Feb 09 07:46:02 2021 +0100
@@ -192,12 +192,12 @@
                 </xsl:when>
               </xsl:choose>
               <xsl:choose>
-                <xsl:when test="regexp:test($path,'^\.[a-zA-Z0-9_]+')">
+                <xsl:when test="regexp:test($path,'^\.[a-zA-Z0-9_]+$')">
                   <xsl:attribute name="type">
                     <xsl:text>PAGE_LOCAL</xsl:text>
                   </xsl:attribute>
                 </xsl:when>
-                <xsl:when test="regexp:test($path,'^[a-zA-Z0-9_]+')">
+                <xsl:when test="regexp:test($path,'^[a-zA-Z0-9_]+$')">
                   <xsl:attribute name="type">
                     <xsl:text>HMI_LOCAL</xsl:text>
                   </xsl:attribute>
@@ -233,6 +233,10 @@
     </xsl:if>
   </xsl:template>
   <xsl:variable name="_parsed_widgets">
+    <widget type="VarInitPersistent">
+      <arg value="0"/>
+      <path value="lang"/>
+    </widget>
     <xsl:apply-templates mode="parselabel" select="$hmi_elements"/>
   </xsl:variable>
   <xsl:variable name="parsed_widgets" select="exsl:node-set($_parsed_widgets)"/>
@@ -972,7 +976,9 @@
     <xsl:variable name="translations" select="ns:GetTranslations($translatable_strings)"/>
     <xsl:text>var langs = [</xsl:text>
     <xsl:for-each select="$translations/langs/lang">
+      <xsl:text>"</xsl:text>
       <xsl:value-of select="."/>
+      <xsl:text>"</xsl:text>
       <xsl:if test="position()!=last()">
         <xsl:text>,</xsl:text>
       </xsl:if>
@@ -983,9 +989,18 @@
 </xsl:text>
     <xsl:for-each select="$translatable_texts">
       <xsl:variable name="n" select="position()"/>
-      <xsl:text>  ["</xsl:text>
-      <xsl:value-of select="@id"/>
-      <xsl:text>",[</xsl:text>
+      <xsl:variable name="current_id" select="@id"/>
+      <xsl:variable name="text_unlinked_uses" select="$result_svg_ns//svg:text[@original = $current_id]/@id"/>
+      <xsl:text>  [[</xsl:text>
+      <xsl:for-each select="@id | $text_unlinked_uses">
+        <xsl:text>id("</xsl:text>
+        <xsl:value-of select="."/>
+        <xsl:text>")</xsl:text>
+        <xsl:if test="position()!=last()">
+          <xsl:text>,</xsl:text>
+        </xsl:if>
+      </xsl:for-each>
+      <xsl:text>],[</xsl:text>
       <xsl:for-each select="$translations/messages/msgid[$n]/msg">
         <xsl:text>"</xsl:text>
         <xsl:for-each select="line">
@@ -1126,11 +1141,13 @@
 </xsl:text>
     <xsl:text>var next_available_index = hmitree_types.length;
 </xsl:text>
+    <xsl:text>let cookies = new Map(document.cookie.split("; ").map(s=&gt;s.split("=")));
+</xsl:text>
     <xsl:text>
 </xsl:text>
     <xsl:text>const local_defaults = {
 </xsl:text>
-    <xsl:for-each select="$parsed_widgets/widget[@type = 'VarInit']">
+    <xsl:for-each select="$parsed_widgets/widget[starts-with(@type,'VarInit')]">
       <xsl:if test="count(path) != 1">
         <xsl:message terminate="yes">
           <xsl:text>VarInit </xsl:text>
@@ -1145,17 +1162,47 @@
           <xsl:text> only applies to HMI variable.</xsl:text>
         </xsl:message>
       </xsl:if>
-      <xsl:text>"</xsl:text>
+      <xsl:text>    "</xsl:text>
       <xsl:value-of select="path/@value"/>
       <xsl:text>":</xsl:text>
-      <xsl:value-of select="arg[1]/@value"/>
+      <xsl:choose>
+        <xsl:when test="@type = 'VarInitPersistent'">
+          <xsl:text>cookies.has("</xsl:text>
+          <xsl:value-of select="path/@value"/>
+          <xsl:text>")?cookies.get("</xsl:text>
+          <xsl:value-of select="path/@value"/>
+          <xsl:text>"):</xsl:text>
+          <xsl:value-of select="arg[1]/@value"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="arg[1]/@value"/>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:text>
+</xsl:text>
+      <xsl:if test="position()!=last()">
+        <xsl:text>,</xsl:text>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>const persistent_locals = new Set([
+</xsl:text>
+    <xsl:for-each select="$parsed_widgets/widget[@type='VarInitPersistent']">
+      <xsl:text>   "</xsl:text>
+      <xsl:value-of select="path/@value"/>
+      <xsl:text>"</xsl:text>
       <xsl:if test="position()!=last()">
         <xsl:text>,</xsl:text>
       </xsl:if>
       <xsl:text>
 </xsl:text>
     </xsl:for-each>
-    <xsl:text>};
+    <xsl:text>]);
+</xsl:text>
+    <xsl:text>var persistent_indexes = new Map();
 </xsl:text>
     <xsl:text>var cache = hmitree_types.map(_ignored =&gt; undefined);
 </xsl:text>
@@ -1193,10 +1240,16 @@
 </xsl:text>
     <xsl:text>    let defaultval = local_defaults[varname];
 </xsl:text>
-    <xsl:text>    if(defaultval != undefined) 
+    <xsl:text>    if(defaultval != undefined) {
 </xsl:text>
     <xsl:text>        cache[new_index] = defaultval; 
 </xsl:text>
+    <xsl:text>        if(persistent_locals.has(varname))
+</xsl:text>
+    <xsl:text>            persistent_indexes.set(new_index, varname);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
     <xsl:text>    return new_index;
 </xsl:text>
     <xsl:text>}
@@ -1506,12 +1559,32 @@
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    activate_activable(eltsub) {
+</xsl:text>
+    <xsl:text>        eltsub.inactive.style.display = "none";
+</xsl:text>
+    <xsl:text>        eltsub.active.style.display = "";
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    inactivate_activable(eltsub) {
+</xsl:text>
+    <xsl:text>        eltsub.active.style.display = "none";
+</xsl:text>
+    <xsl:text>        eltsub.inactive.style.display = "";
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:variable name="excluded_types" select="str:split('Page VarInit')"/>
+  <xsl:variable name="excluded_types" select="str:split('Page VarInit VarInitPersistent')"/>
   <xsl:key name="TypesKey" match="widget" use="@type"/>
   <declarations:hmi-classes/>
   <xsl:template match="declarations:hmi-classes">
@@ -3825,6 +3898,8 @@
 </xsl:text>
     <xsl:text>        highlight_selection(){
 </xsl:text>
+    <xsl:text>            if(this.last_selection == undefined) return;
+</xsl:text>
     <xsl:text>            let highlighted_row = this.last_selection - this.menu_offset;
 </xsl:text>
     <xsl:text>            if(highlighted_row &lt; 0) return;
@@ -5123,7 +5198,7 @@
 </xsl:text>
     <xsl:text>             this._shift = this.shift;
 </xsl:text>
-    <xsl:text>             (this.shift?widget_active_activable:widget_inactive_activable)(this.Shift_sub);
+    <xsl:text>             (this.shift?this.activate_activable:this.inactivate_activable)(this.Shift_sub);
 </xsl:text>
     <xsl:text>         }
 </xsl:text>
@@ -5131,7 +5206,7 @@
 </xsl:text>
     <xsl:text>             this._caps = this.caps;
 </xsl:text>
-    <xsl:text>             (this.caps?widget_active_activable:widget_inactive_activable)(this.CapsLock_sub);
+    <xsl:text>             (this.caps?this.activate_activable:this.inactivate_activable)(this.CapsLock_sub);
 </xsl:text>
     <xsl:text>         }
 </xsl:text>
@@ -6734,6 +6809,72 @@
 </xsl:text>
           <xsl:text>
 </xsl:text>
+          <xsl:text>var translated = false;
+</xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>function switch_langnum(langnum) {
+</xsl:text>
+          <xsl:text>    if(langnum == current_lang) {
+</xsl:text>
+          <xsl:text>        return;
+</xsl:text>
+          <xsl:text>    }
+</xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>    if (!translated) {
+</xsl:text>
+          <xsl:text>        translated = true;
+</xsl:text>
+          <xsl:text>        for (let translation of translations) {
+</xsl:text>
+          <xsl:text>            let [objs] = translation;
+</xsl:text>
+          <xsl:text>            translation.push(Array.prototype.map.call(objs[0].children, x=&gt;x.textContent).join("\n")); 
+</xsl:text>
+          <xsl:text>        }
+</xsl:text>
+          <xsl:text>    }
+</xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>    for (let translation of translations) {
+</xsl:text>
+          <xsl:text>        let [objs, msgs, orig] = translation;
+</xsl:text>
+          <xsl:text>        let msg = langnum == 0 ? orig : msgs[langnum - 1];
+</xsl:text>
+          <xsl:text>        for (let obj of objs) {
+</xsl:text>
+          <xsl:text>            msg.split('\n').map((line,i) =&gt; {obj.children[i].textContent = line;});
+</xsl:text>
+          <xsl:text>        }
+</xsl:text>
+          <xsl:text>    }
+</xsl:text>
+          <xsl:text>    current_lang = langnum;
+</xsl:text>
+          <xsl:text>}
+</xsl:text>
+          <xsl:text>var lang_local_index = hmi_local_index("lang");
+</xsl:text>
+          <xsl:text>subscribers(lang_local_index).add({
+</xsl:text>
+          <xsl:text>    indexes: [lang_local_index],
+</xsl:text>
+          <xsl:text>    new_hmi_value: function(index, value, oldval) {
+</xsl:text>
+          <xsl:text>        switch_langnum(value);
+</xsl:text>
+          <xsl:text>    }
+</xsl:text>
+          <xsl:text>});
+</xsl:text>
+          <xsl:text>var current_lang = 0;
+</xsl:text>
+          <xsl:text>switch_langnum(cache[lang_local_index]);
+</xsl:text>
           <xsl:text>
 </xsl:text>
           <xsl:text>function update_subscriptions() {
@@ -6812,6 +6953,22 @@
 </xsl:text>
           <xsl:text>        updates[index] = value;
 </xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>        if(persistent_indexes.has(index)){
+</xsl:text>
+          <xsl:text>            let varname = persistent_indexes.get(index);
+</xsl:text>
+          <xsl:text>            console.log(varname+"="+value+"; max-age=3153600000");
+</xsl:text>
+          <xsl:text>            document.cookie = varname+"="+value+"; max-age=3153600000";
+</xsl:text>
+          <xsl:text>        }
+</xsl:text>
+          <xsl:text>
+</xsl:text>
+          <xsl:text>        
+</xsl:text>
           <xsl:text>        requestHMIAnimation();
 </xsl:text>
           <xsl:text>        return;
@@ -6930,28 +7087,6 @@
 </xsl:text>
           <xsl:text>
 </xsl:text>
-          <xsl:text>/*
-</xsl:text>
-          <xsl:text>function change_hmi_value(index, opstr) {
-</xsl:text>
-          <xsl:text>    let old_val = cache[index];
-</xsl:text>
-          <xsl:text>    let new_val = eval_operation_string(old_val, opstr);
-</xsl:text>
-          <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
-</xsl:text>
-          <xsl:text>        send_hmi_value(index, new_val);
-</xsl:text>
-          <xsl:text>    // TODO else raise
-</xsl:text>
-          <xsl:text>    return new_val;
-</xsl:text>
-          <xsl:text>}
-</xsl:text>
-          <xsl:text>*/
-</xsl:text>
-          <xsl:text>
-</xsl:text>
           <xsl:text>var current_visible_page;
 </xsl:text>
           <xsl:text>var current_subscribed_page;
@@ -7256,34 +7391,6 @@
 </xsl:text>
           <xsl:text>
 </xsl:text>
-          <xsl:text>function widget_active_activable(eltsub) {
-</xsl:text>
-          <xsl:text>    if(eltsub.inactive_style === undefined)
-</xsl:text>
-          <xsl:text>        eltsub.inactive_style = eltsub.inactive.getAttribute("style");
-</xsl:text>
-          <xsl:text>    eltsub.inactive.setAttribute("style", "display:none");
-</xsl:text>
-          <xsl:text>    if(eltsub.active_style !== undefined)
-</xsl:text>
-          <xsl:text>            eltsub.active.setAttribute("style", eltsub.active_style);
-</xsl:text>
-          <xsl:text>};
-</xsl:text>
-          <xsl:text>function widget_inactive_activable(eltsub) {
-</xsl:text>
-          <xsl:text>    if(eltsub.active_style === undefined)
-</xsl:text>
-          <xsl:text>        eltsub.active_style = eltsub.active.getAttribute("style");
-</xsl:text>
-          <xsl:text>    eltsub.active.setAttribute("style", "display:none");
-</xsl:text>
-          <xsl:text>    if(eltsub.inactive_style !== undefined)
-</xsl:text>
-          <xsl:text>            eltsub.inactive.setAttribute("style", eltsub.inactive_style);
-</xsl:text>
-          <xsl:text>};
-</xsl:text>
         </script>
       </body>
     </html>
--- a/svghmi/svghmi.js	Tue Feb 09 07:41:24 2021 +0100
+++ b/svghmi/svghmi.js	Tue Feb 09 07:46:02 2021 +0100
@@ -243,6 +243,13 @@
 function send_hmi_value(index, value) {
     if(index > last_remote_index){
         updates[index] = value;
+
+        if(persistent_indexes.has(index)){
+            let varname = persistent_indexes.get(index);
+            console.log(varname+"="+value+"; max-age=3153600000");
+            document.cookie = varname+"="+value+"; max-age=3153600000";
+        }
+
         requestHMIAnimation();
         return;
     }
--- a/svghmi/widgets_common.ysl2	Tue Feb 09 07:41:24 2021 +0100
+++ b/svghmi/widgets_common.ysl2	Tue Feb 09 07:46:02 2021 +0100
@@ -68,16 +68,32 @@
     let hmi_locals = {};
     var last_remote_index = hmitree_types.length - 1;
     var next_available_index = hmitree_types.length;
+    let cookies = new Map(document.cookie.split("; ").map(s=>s.split("=")));
 
     const local_defaults = {
     ||
-    foreach "$parsed_widgets/widget[@type = 'VarInit']"{
+    foreach "$parsed_widgets/widget[starts-with(@type,'VarInit')]"{
         if "count(path) != 1" error > VarInit «@id» must have only one variable given.
         if "path/@type != 'PAGE_LOCAL' and path/@type != 'HMI_LOCAL'" error > VarInit «@id» only applies to HMI variable.
-        | "«path/@value»":«arg[1]/@value»`if "position()!=last()" > ,`
+        >     "«path/@value»":
+        choose {
+            when "@type = 'VarInitPersistent'" > cookies.has("«path/@value»")?cookies.get("«path/@value»"):«arg[1]/@value»
+            otherwise > «arg[1]/@value»
+        }
+        > \n
+        if "position()!=last()" > ,
     }
     ||
     };
+
+    const persistent_locals = new Set([
+    ||
+    foreach "$parsed_widgets/widget[@type='VarInitPersistent']"{
+    |    "«path/@value»"`if "position()!=last()" > ,`
+    }
+    ||
+    ]);
+    var persistent_indexes = new Map();
     var cache = hmitree_types.map(_ignored => undefined);
 
     function page_local_index(varname, pagename){
@@ -96,8 +112,11 @@
             pagevars[varname] = new_index;
         }
         let defaultval = local_defaults[varname];
-        if(defaultval != undefined) 
+        if(defaultval != undefined) {
             cache[new_index] = defaultval; 
+            if(persistent_locals.has(varname))
+                persistent_indexes.set(new_index, varname);
+        }
         return new_index;
     }
 
@@ -265,7 +284,7 @@
     ||
 }
 
-const "excluded_types", "str:split('Page VarInit')";
+const "excluded_types", "str:split('Page VarInit VarInitPersistent')";
 
 // Key to filter unique types
 key "TypesKey", "widget", "@type";