svghmi/gen_index_xhtml.xslt
changeset 3506 ca312be56929
parent 3418 a1d9a0353053
child 3507 e87a2daace80
equal deleted inserted replaced
3505:a27b5862e363 3506:ca312be56929
   157       <xsl:with-param name="parentpath">
   157       <xsl:with-param name="parentpath">
   158         <xsl:value-of select="$parentpath"/>
   158         <xsl:value-of select="$parentpath"/>
   159       </xsl:with-param>
   159       </xsl:with-param>
   160     </xsl:apply-templates>
   160     </xsl:apply-templates>
   161   </xsl:template>
   161   </xsl:template>
   162   <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"/>
   162   <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([-.\d,]*)$'"/>
   163   <xsl:template mode="parselabel" match="*">
   163   <xsl:template mode="parselabel" match="*">
   164     <xsl:variable name="label" select="@inkscape:label"/>
   164     <xsl:variable name="label" select="@inkscape:label"/>
   165     <xsl:variable name="id" select="@id"/>
   165     <xsl:variable name="id" select="@id"/>
   166     <xsl:variable name="description" select="substring-after($label,'HMI:')"/>
   166     <xsl:variable name="description" select="substring-after($label,'HMI:')"/>
   167     <xsl:variable name="_args" select="substring-before($description,'@')"/>
   167     <xsl:variable name="_args" select="substring-before($description,'@')"/>
   205         </xsl:attribute>
   205         </xsl:attribute>
   206         <xsl:attribute name="type">
   206         <xsl:attribute name="type">
   207           <xsl:value-of select="$type"/>
   207           <xsl:value-of select="$type"/>
   208         </xsl:attribute>
   208         </xsl:attribute>
   209         <xsl:if test="$freq">
   209         <xsl:if test="$freq">
       
   210           <xsl:if test="not(regexp:test($freq,'^[0-9]*(\.[0-9]+)?[smh]?'))">
       
   211             <xsl:message terminate="yes">
       
   212               <xsl:text>Widget id:</xsl:text>
       
   213               <xsl:value-of select="$id"/>
       
   214               <xsl:text> label:</xsl:text>
       
   215               <xsl:value-of select="$label"/>
       
   216               <xsl:text> has wrong syntax of frequency forcing </xsl:text>
       
   217               <xsl:value-of select="$freq"/>
       
   218             </xsl:message>
       
   219           </xsl:if>
   210           <xsl:attribute name="freq">
   220           <xsl:attribute name="freq">
   211             <xsl:value-of select="$freq"/>
   221             <xsl:value-of select="$freq"/>
   212           </xsl:attribute>
   222           </xsl:attribute>
   213         </xsl:if>
   223         </xsl:if>
   214         <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
   224         <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
  1096         <xsl:value-of select="@id"/>
  1106         <xsl:value-of select="@id"/>
  1097       </xsl:attribute>
  1107       </xsl:attribute>
  1098       <xsl:attribute name="label">
  1108       <xsl:attribute name="label">
  1099         <xsl:value-of select="substring(@inkscape:label,2)"/>
  1109         <xsl:value-of select="substring(@inkscape:label,2)"/>
  1100       </xsl:attribute>
  1110       </xsl:attribute>
       
  1111       <xsl:if test="string-length(text()) &gt; 0">
       
  1112         <line>
       
  1113           <xsl:value-of select="text()"/>
       
  1114         </line>
       
  1115       </xsl:if>
  1101       <xsl:apply-templates mode="extract_i18n" select="svg:*"/>
  1116       <xsl:apply-templates mode="extract_i18n" select="svg:*"/>
  1102     </msg>
  1117     </msg>
  1103   </xsl:template>
  1118   </xsl:template>
  1104   <xsl:variable name="translatable_texts" select="//svg:text[starts-with(@inkscape:label, '_')]"/>
  1119   <xsl:variable name="translatable_texts" select="//svg:text[starts-with(@inkscape:label, '_')]"/>
  1105   <xsl:variable name="translatable_strings">
  1120   <xsl:variable name="translatable_strings">
  1246       </xsl:for-each>
  1261       </xsl:for-each>
  1247     </xsl:variable>
  1262     </xsl:variable>
  1248     <xsl:variable name="freq">
  1263     <xsl:variable name="freq">
  1249       <xsl:choose>
  1264       <xsl:choose>
  1250         <xsl:when test="$widget/@freq">
  1265         <xsl:when test="$widget/@freq">
       
  1266           <xsl:text>"</xsl:text>
  1251           <xsl:value-of select="$widget/@freq"/>
  1267           <xsl:value-of select="$widget/@freq"/>
       
  1268           <xsl:text>"</xsl:text>
  1252         </xsl:when>
  1269         </xsl:when>
  1253         <xsl:otherwise>
  1270         <xsl:otherwise>
  1254           <xsl:text>undefined</xsl:text>
  1271           <xsl:text>undefined</xsl:text>
  1255         </xsl:otherwise>
  1272         </xsl:otherwise>
  1256       </xsl:choose>
  1273       </xsl:choose>
  1471 </xsl:text>
  1488 </xsl:text>
  1472     <xsl:text>        this.inhibit = indexes.map(() =&gt; undefined);
  1489     <xsl:text>        this.inhibit = indexes.map(() =&gt; undefined);
  1473 </xsl:text>
  1490 </xsl:text>
  1474     <xsl:text>        this.pending = indexes.map(() =&gt; undefined);
  1491     <xsl:text>        this.pending = indexes.map(() =&gt; undefined);
  1475 </xsl:text>
  1492 </xsl:text>
  1476     <xsl:text>        this.bound_unhinibit = this.unhinibit.bind(this);
  1493     <xsl:text>        this.bound_uninhibit = this.uninhibit.bind(this);
       
  1494 </xsl:text>
       
  1495     <xsl:text>
       
  1496 </xsl:text>
       
  1497     <xsl:text>        this.lastdispatch = indexes.map(() =&gt; undefined);
       
  1498 </xsl:text>
       
  1499     <xsl:text>        this.deafen = indexes.map(() =&gt; undefined);
       
  1500 </xsl:text>
       
  1501     <xsl:text>        this.incoming = indexes.map(() =&gt; undefined);
       
  1502 </xsl:text>
       
  1503     <xsl:text>        this.bound_undeafen = this.undeafen.bind(this);
       
  1504 </xsl:text>
       
  1505     <xsl:text>
  1477 </xsl:text>
  1506 </xsl:text>
  1478     <xsl:text>        this.forced_frequency = freq;
  1507     <xsl:text>        this.forced_frequency = freq;
  1479 </xsl:text>
  1508 </xsl:text>
       
  1509     <xsl:text>        this.clip = true;
       
  1510 </xsl:text>
       
  1511     <xsl:text>    }
       
  1512 </xsl:text>
       
  1513     <xsl:text>
       
  1514 </xsl:text>
       
  1515     <xsl:text>    do_init(){
       
  1516 </xsl:text>
       
  1517     <xsl:text>        let forced = this.forced_frequency;
       
  1518 </xsl:text>
       
  1519     <xsl:text>        if(forced !== undefined){
       
  1520 </xsl:text>
       
  1521     <xsl:text>            /*
       
  1522 </xsl:text>
       
  1523     <xsl:text>            once every 10 seconds : 10s
       
  1524 </xsl:text>
       
  1525     <xsl:text>            once per minute : 1m
       
  1526 </xsl:text>
       
  1527     <xsl:text>            once per hour : 1h
       
  1528 </xsl:text>
       
  1529     <xsl:text>            once per day : 1d
       
  1530 </xsl:text>
       
  1531     <xsl:text>            */
       
  1532 </xsl:text>
       
  1533     <xsl:text>            let unit = forced.slice(-1);
       
  1534 </xsl:text>
       
  1535     <xsl:text>            let factor = {
       
  1536 </xsl:text>
       
  1537     <xsl:text>                "s":1,
       
  1538 </xsl:text>
       
  1539     <xsl:text>                "m":60,
       
  1540 </xsl:text>
       
  1541     <xsl:text>                "h":3600,
       
  1542 </xsl:text>
       
  1543     <xsl:text>                "d":86400}[unit];
       
  1544 </xsl:text>
       
  1545     <xsl:text>
       
  1546 </xsl:text>
       
  1547     <xsl:text>            this.frequency = factor ? 1/(factor * Number(forced.slice(0,-1)))
       
  1548 </xsl:text>
       
  1549     <xsl:text>                                      : Number(forced);
       
  1550 </xsl:text>
       
  1551     <xsl:text>        }
       
  1552 </xsl:text>
       
  1553     <xsl:text>
       
  1554 </xsl:text>
       
  1555     <xsl:text>        let init = this.init;
       
  1556 </xsl:text>
       
  1557     <xsl:text>        if(typeof(init) == "function"){
       
  1558 </xsl:text>
       
  1559     <xsl:text>            // try {
       
  1560 </xsl:text>
       
  1561     <xsl:text>                init.call(this);
       
  1562 </xsl:text>
       
  1563     <xsl:text>            // } catch(err) {
       
  1564 </xsl:text>
       
  1565     <xsl:text>            //     console.log(err);
       
  1566 </xsl:text>
       
  1567     <xsl:text>            // }
       
  1568 </xsl:text>
       
  1569     <xsl:text>        }
       
  1570 </xsl:text>
  1480     <xsl:text>    }
  1571     <xsl:text>    }
  1481 </xsl:text>
  1572 </xsl:text>
  1482     <xsl:text>
  1573     <xsl:text>
  1483 </xsl:text>
  1574 </xsl:text>
  1484     <xsl:text>    unsub(){
  1575     <xsl:text>    unsub(){
  1497 </xsl:text>
  1588 </xsl:text>
  1498     <xsl:text>                    clearTimeout(inhibition);
  1589     <xsl:text>                    clearTimeout(inhibition);
  1499 </xsl:text>
  1590 </xsl:text>
  1500     <xsl:text>                    this.lastapply[i] = undefined;
  1591     <xsl:text>                    this.lastapply[i] = undefined;
  1501 </xsl:text>
  1592 </xsl:text>
  1502     <xsl:text>                    this.unhinibit(i);
  1593     <xsl:text>                    this.uninhibit(i);
  1503 </xsl:text>
  1594 </xsl:text>
  1504     <xsl:text>                }
  1595     <xsl:text>                }
  1505 </xsl:text>
  1596 </xsl:text>
       
  1597     <xsl:text>                let deafened = this.deafen[i];
       
  1598 </xsl:text>
       
  1599     <xsl:text>                if(deafened != undefined){
       
  1600 </xsl:text>
       
  1601     <xsl:text>                    clearTimeout(deafened);
       
  1602 </xsl:text>
       
  1603     <xsl:text>                    this.lastdispatch[i] = undefined;
       
  1604 </xsl:text>
       
  1605     <xsl:text>                    this.undeafen(i);
       
  1606 </xsl:text>
       
  1607     <xsl:text>                }
       
  1608 </xsl:text>
  1506     <xsl:text>                let index = this.indexes[i];
  1609     <xsl:text>                let index = this.indexes[i];
  1507 </xsl:text>
  1610 </xsl:text>
  1508     <xsl:text>                if(this.relativeness[i])
  1611     <xsl:text>                if(this.relativeness[i])
  1509 </xsl:text>
  1612 </xsl:text>
  1510     <xsl:text>                    index += this.offset;
  1613     <xsl:text>                    index += this.offset;
  1647 </xsl:text>
  1750 </xsl:text>
  1648     <xsl:text>        let old_val = cache[realindex];
  1751     <xsl:text>        let old_val = cache[realindex];
  1649 </xsl:text>
  1752 </xsl:text>
  1650     <xsl:text>        let new_val = eval_operation_string(old_val, opstr);
  1753     <xsl:text>        let new_val = eval_operation_string(old_val, opstr);
  1651 </xsl:text>
  1754 </xsl:text>
  1652     <xsl:text>        new_val = this.clip_min_max(index, new_val);
  1755     <xsl:text>        if(this.clip)
       
  1756 </xsl:text>
       
  1757     <xsl:text>            new_val = this.clip_min_max(index, new_val);
  1653 </xsl:text>
  1758 </xsl:text>
  1654     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1759     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1655 </xsl:text>
  1760 </xsl:text>
  1656     <xsl:text>    }
  1761     <xsl:text>    }
  1657 </xsl:text>
  1762 </xsl:text>
  1661 </xsl:text>
  1766 </xsl:text>
  1662     <xsl:text>        let realindex = this.get_variable_index(index);
  1767     <xsl:text>        let realindex = this.get_variable_index(index);
  1663 </xsl:text>
  1768 </xsl:text>
  1664     <xsl:text>        if(realindex == undefined) return undefined;
  1769     <xsl:text>        if(realindex == undefined) return undefined;
  1665 </xsl:text>
  1770 </xsl:text>
  1666     <xsl:text>        new_val = this.clip_min_max(index, new_val);
  1771     <xsl:text>        if(this.clip)
       
  1772 </xsl:text>
       
  1773     <xsl:text>            new_val = this.clip_min_max(index, new_val);
  1667 </xsl:text>
  1774 </xsl:text>
  1668     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1775     <xsl:text>        return apply_hmi_value(realindex, new_val);
  1669 </xsl:text>
  1776 </xsl:text>
  1670     <xsl:text>    }
  1777     <xsl:text>    }
  1671 </xsl:text>
  1778 </xsl:text>
  1672     <xsl:text>
  1779     <xsl:text>
  1673 </xsl:text>
  1780 </xsl:text>
  1674     <xsl:text>    unhinibit(index){
  1781     <xsl:text>    uninhibit(index){
  1675 </xsl:text>
  1782 </xsl:text>
  1676     <xsl:text>        this.inhibit[index] = undefined;
  1783     <xsl:text>        this.inhibit[index] = undefined;
  1677 </xsl:text>
  1784 </xsl:text>
  1678     <xsl:text>        let new_val = this.pending[index];
  1785     <xsl:text>        let new_val = this.pending[index];
  1679 </xsl:text>
  1786 </xsl:text>
  1707 </xsl:text>
  1814 </xsl:text>
  1708     <xsl:text>                let elapsed = now - lastapply;
  1815     <xsl:text>                let elapsed = now - lastapply;
  1709 </xsl:text>
  1816 </xsl:text>
  1710     <xsl:text>                this.pending[index] = new_val;
  1817     <xsl:text>                this.pending[index] = new_val;
  1711 </xsl:text>
  1818 </xsl:text>
  1712     <xsl:text>                this.inhibit[index] = setTimeout(this.bound_unhinibit, min_interval - elapsed, index);
  1819     <xsl:text>                this.inhibit[index] = setTimeout(this.bound_uninhibit, min_interval - elapsed, index);
  1713 </xsl:text>
  1820 </xsl:text>
  1714     <xsl:text>            }
  1821     <xsl:text>            }
  1715 </xsl:text>
  1822 </xsl:text>
  1716     <xsl:text>        }
  1823     <xsl:text>        }
  1717 </xsl:text>
  1824 </xsl:text>
  1751 </xsl:text>
  1858 </xsl:text>
  1752     <xsl:text>    }
  1859     <xsl:text>    }
  1753 </xsl:text>
  1860 </xsl:text>
  1754     <xsl:text>    
  1861     <xsl:text>    
  1755 </xsl:text>
  1862 </xsl:text>
       
  1863     <xsl:text>    undeafen(index){
       
  1864 </xsl:text>
       
  1865     <xsl:text>        this.deafen[index] = undefined;
       
  1866 </xsl:text>
       
  1867     <xsl:text>        let [new_val, old_val] = this.incoming[index];
       
  1868 </xsl:text>
       
  1869     <xsl:text>        this.incoming[index] = undefined;
       
  1870 </xsl:text>
       
  1871     <xsl:text>        this.dispatch(new_val, old_val, index);
       
  1872 </xsl:text>
       
  1873     <xsl:text>    }
       
  1874 </xsl:text>
       
  1875     <xsl:text>
       
  1876 </xsl:text>
  1756     <xsl:text>    _dispatch(value, oldval, varnum) {
  1877     <xsl:text>    _dispatch(value, oldval, varnum) {
  1757 </xsl:text>
  1878 </xsl:text>
  1758     <xsl:text>        let dispatch = this.dispatch;
  1879     <xsl:text>        let dispatch = this.dispatch;
  1759 </xsl:text>
  1880 </xsl:text>
  1760     <xsl:text>        if(dispatch != undefined){
  1881     <xsl:text>        if(dispatch != undefined){
  1761 </xsl:text>
  1882 </xsl:text>
  1762     <xsl:text>            try {
  1883     <xsl:text>            if(this.deafen[varnum] == undefined){
  1763 </xsl:text>
  1884 </xsl:text>
  1764     <xsl:text>                dispatch.call(this, value, oldval, varnum);
  1885     <xsl:text>                let now = Date.now();
  1765 </xsl:text>
  1886 </xsl:text>
  1766     <xsl:text>            } catch(err) {
  1887     <xsl:text>                let min_interval = 1000/this.frequency;
  1767 </xsl:text>
  1888 </xsl:text>
  1768     <xsl:text>                console.log(err);
  1889     <xsl:text>                let lastdispatch = this.lastdispatch[varnum];
       
  1890 </xsl:text>
       
  1891     <xsl:text>                if(lastdispatch == undefined || now &gt; lastdispatch + min_interval){
       
  1892 </xsl:text>
       
  1893     <xsl:text>                    this.lastdispatch[varnum] = now;
       
  1894 </xsl:text>
       
  1895     <xsl:text>                    try {
       
  1896 </xsl:text>
       
  1897     <xsl:text>                        dispatch.call(this, value, oldval, varnum);
       
  1898 </xsl:text>
       
  1899     <xsl:text>                    } catch(err) {
       
  1900 </xsl:text>
       
  1901     <xsl:text>                        console.log(err);
       
  1902 </xsl:text>
       
  1903     <xsl:text>                    }
       
  1904 </xsl:text>
       
  1905     <xsl:text>                }
       
  1906 </xsl:text>
       
  1907     <xsl:text>                else {
       
  1908 </xsl:text>
       
  1909     <xsl:text>                    let elapsed = now - lastdispatch;
       
  1910 </xsl:text>
       
  1911     <xsl:text>                    this.incoming[varnum] = [value, oldval];
       
  1912 </xsl:text>
       
  1913     <xsl:text>                    this.deafen[varnum] = setTimeout(this.bound_undeafen, min_interval - elapsed, varnum);
       
  1914 </xsl:text>
       
  1915     <xsl:text>                }
       
  1916 </xsl:text>
       
  1917     <xsl:text>            }
       
  1918 </xsl:text>
       
  1919     <xsl:text>            else {
       
  1920 </xsl:text>
       
  1921     <xsl:text>                this.incoming[varnum] = [value, oldval];
  1769 </xsl:text>
  1922 </xsl:text>
  1770     <xsl:text>            }
  1923     <xsl:text>            }
  1771 </xsl:text>
  1924 </xsl:text>
  1772     <xsl:text>        }
  1925     <xsl:text>        }
  1773 </xsl:text>
  1926 </xsl:text>
  1881     <xsl:param name="mandatory" select="'yes'"/>
  2034     <xsl:param name="mandatory" select="'yes'"/>
  1882     <xsl:param name="subelements" select="/.."/>
  2035     <xsl:param name="subelements" select="/.."/>
  1883     <xsl:param name="hmi_element"/>
  2036     <xsl:param name="hmi_element"/>
  1884     <xsl:variable name="widget_type" select="@type"/>
  2037     <xsl:variable name="widget_type" select="@type"/>
  1885     <xsl:for-each select="str:split($labels)">
  2038     <xsl:for-each select="str:split($labels)">
  1886       <xsl:variable name="name" select="."/>
  2039       <xsl:variable name="absolute" select="starts-with(., '/')"/>
  1887       <xsl:variable name="elt" select="$result_widgets[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]"/>
  2040       <xsl:variable name="name" select="substring(.,number($absolute)+1)"/>
       
  2041       <xsl:variable name="widget" select="$result_widgets[@id = $hmi_element/@id]"/>
       
  2042       <xsl:variable name="elt" select="($widget//*[not($absolute) and @inkscape:label=$name] | $widget/*[$absolute and @inkscape:label=$name])[1]"/>
  1888       <xsl:choose>
  2043       <xsl:choose>
  1889         <xsl:when test="not($elt/@id)">
  2044         <xsl:when test="not($elt/@id)">
  1890           <xsl:if test="$mandatory='yes'">
  2045           <xsl:if test="$mandatory='yes'">
  1891             <xsl:message terminate="yes">
  2046             <xsl:message terminate="yes">
  1892               <xsl:value-of select="$widget_type"/>
  2047               <xsl:value-of select="$widget_type"/>
  2398   <xsl:template mode="actions" match="state">
  2553   <xsl:template mode="actions" match="state">
  2399     <xsl:text>    </xsl:text>
  2554     <xsl:text>    </xsl:text>
  2400     <xsl:value-of select="@name"/>
  2555     <xsl:value-of select="@name"/>
  2401     <xsl:text>_action(){
  2556     <xsl:text>_action(){
  2402 </xsl:text>
  2557 </xsl:text>
  2403     <xsl:text>console.log("Entering state </xsl:text>
       
  2404     <xsl:value-of select="@name"/>
       
  2405     <xsl:text>", this.frequency);
       
  2406 </xsl:text>
       
  2407     <xsl:apply-templates mode="actions" select="*"/>
  2558     <xsl:apply-templates mode="actions" select="*"/>
  2408     <xsl:text>    }
  2559     <xsl:text>    }
  2409 </xsl:text>
  2560 </xsl:text>
  2410   </xsl:template>
  2561   </xsl:template>
  2411   <xsl:template mode="actions" match="show">
  2562   <xsl:template mode="actions" match="show">
  2422     <xsl:text>);
  2573     <xsl:text>);
  2423 </xsl:text>
  2574 </xsl:text>
  2424   </xsl:template>
  2575   </xsl:template>
  2425   <xsl:template name="generated_button_class">
  2576   <xsl:template name="generated_button_class">
  2426     <xsl:param name="fsm"/>
  2577     <xsl:param name="fsm"/>
  2427     <xsl:text>    frequency = 5;
       
  2428 </xsl:text>
       
  2429     <xsl:text>    display = "inactive";
  2578     <xsl:text>    display = "inactive";
  2430 </xsl:text>
  2579 </xsl:text>
  2431     <xsl:text>    state = "init";
  2580     <xsl:text>    state = "init";
  2432 </xsl:text>
  2581 </xsl:text>
  2433     <xsl:text>    dispatch(value) {
  2582     <xsl:text>    dispatch(value) {
  2489   </xsl:template>
  2638   </xsl:template>
  2490   <xsl:template match="widget[@type='Button']" mode="widget_class">
  2639   <xsl:template match="widget[@type='Button']" mode="widget_class">
  2491     <xsl:text>class </xsl:text>
  2640     <xsl:text>class </xsl:text>
  2492     <xsl:text>ButtonWidget</xsl:text>
  2641     <xsl:text>ButtonWidget</xsl:text>
  2493     <xsl:text> extends Widget{
  2642     <xsl:text> extends Widget{
       
  2643 </xsl:text>
       
  2644     <xsl:text>    frequency = 5;
  2494 </xsl:text>
  2645 </xsl:text>
  2495     <xsl:variable name="fsm" select="exsl:node-set($_button_fsm)"/>
  2646     <xsl:variable name="fsm" select="exsl:node-set($_button_fsm)"/>
  2496     <xsl:call-template name="generated_button_class">
  2647     <xsl:call-template name="generated_button_class">
  2497       <xsl:with-param name="fsm" select="$fsm"/>
  2648       <xsl:with-param name="fsm" select="$fsm"/>
  2498     </xsl:call-template>
  2649     </xsl:call-template>
  2511   </xsl:template>
  2662   </xsl:template>
  2512   <xsl:template match="widget[@type='PushButton']" mode="widget_class">
  2663   <xsl:template match="widget[@type='PushButton']" mode="widget_class">
  2513     <xsl:text>class </xsl:text>
  2664     <xsl:text>class </xsl:text>
  2514     <xsl:text>PushButtonWidget</xsl:text>
  2665     <xsl:text>PushButtonWidget</xsl:text>
  2515     <xsl:text> extends Widget{
  2666     <xsl:text> extends Widget{
       
  2667 </xsl:text>
       
  2668     <xsl:text>    frequency = 20;
  2516 </xsl:text>
  2669 </xsl:text>
  2517     <xsl:variable name="fsm" select="exsl:node-set($_push_button_fsm)"/>
  2670     <xsl:variable name="fsm" select="exsl:node-set($_push_button_fsm)"/>
  2518     <xsl:call-template name="generated_button_class">
  2671     <xsl:call-template name="generated_button_class">
  2519       <xsl:with-param name="fsm" select="$fsm"/>
  2672       <xsl:with-param name="fsm" select="$fsm"/>
  2520     </xsl:call-template>
  2673     </xsl:call-template>
  4131       <xsl:when test="count(arg) = 1 and arg[1]/@value = '#langs'">
  4284       <xsl:when test="count(arg) = 1 and arg[1]/@value = '#langs'">
  4132         <xsl:text>  this.text_elt = id("</xsl:text>
  4285         <xsl:text>  this.text_elt = id("</xsl:text>
  4133         <xsl:value-of select="$text_elt/@id"/>
  4286         <xsl:value-of select="$text_elt/@id"/>
  4134         <xsl:text>");
  4287         <xsl:text>");
  4135 </xsl:text>
  4288 </xsl:text>
  4136         <xsl:text>  this.content = langs;
  4289         <xsl:text>  this.content = langs.map(([lname,lcode]) =&gt; lname);
  4137 </xsl:text>
  4290 </xsl:text>
  4138       </xsl:when>
  4291       </xsl:when>
  4139       <xsl:when test="count(arg) = 0">
  4292       <xsl:when test="count(arg) = 0">
  4140         <xsl:if test="not($text_elt[self::svg:use])">
  4293         <xsl:if test="not($text_elt[self::svg:use])">
  4141           <xsl:message terminate="yes">
  4294           <xsl:message terminate="yes">
  6114   <xsl:template match="widget[@type='PathSlider']" mode="widget_desc">
  6267   <xsl:template match="widget[@type='PathSlider']" mode="widget_desc">
  6115     <type>
  6268     <type>
  6116       <xsl:value-of select="@type"/>
  6269       <xsl:value-of select="@type"/>
  6117     </type>
  6270     </type>
  6118     <longdesc>
  6271     <longdesc>
  6119       <xsl:text>PathSlider - 
  6272       <xsl:text>PathSlider -
  6120 </xsl:text>
  6273 </xsl:text>
  6121     </longdesc>
  6274     </longdesc>
  6122     <shortdesc>
  6275     <shortdesc>
  6123       <xsl:text>Slide an SVG element along a path by dragging it</xsl:text>
  6276       <xsl:text>Slide an SVG element along a path by dragging it</xsl:text>
  6124     </shortdesc>
  6277     </shortdesc>
  6157 </xsl:text>
  6310 </xsl:text>
  6158     <xsl:text>    precision = undefined;
  6311     <xsl:text>    precision = undefined;
  6159 </xsl:text>
  6312 </xsl:text>
  6160     <xsl:text>    origPt = undefined;
  6313     <xsl:text>    origPt = undefined;
  6161 </xsl:text>
  6314 </xsl:text>
  6162     <xsl:text>    
  6315     <xsl:text>
  6163 </xsl:text>
  6316 </xsl:text>
  6164     <xsl:text>
  6317     <xsl:text>
  6165 </xsl:text>
  6318 </xsl:text>
  6166     <xsl:text>    scanPath() {
  6319     <xsl:text>    scanPath() {
  6167 </xsl:text>
  6320 </xsl:text>
  6241 </xsl:text>
  6394 </xsl:text>
  6242     <xsl:text>          bestLength = beforeLength,
  6395     <xsl:text>          bestLength = beforeLength,
  6243 </xsl:text>
  6396 </xsl:text>
  6244     <xsl:text>          bestDistance = beforeDistance;
  6397     <xsl:text>          bestDistance = beforeDistance;
  6245 </xsl:text>
  6398 </xsl:text>
  6246     <xsl:text>        } else if ((afterLength = bestLength + precision) &lt;= this.pathLength &amp;&amp; 
  6399     <xsl:text>        } else if ((afterLength = bestLength + precision) &lt;= this.pathLength &amp;&amp;
  6247 </xsl:text>
  6400 </xsl:text>
  6248     <xsl:text>                   (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) &lt; bestDistance) {
  6401     <xsl:text>                   (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) &lt; bestDistance) {
  6249 </xsl:text>
  6402 </xsl:text>
  6250     <xsl:text>          bestPoint = afterPoint,
  6403     <xsl:text>          bestPoint = afterPoint,
  6251 </xsl:text>
  6404 </xsl:text>
  7670         <xsl:text>active inactive</xsl:text>
  7823         <xsl:text>active inactive</xsl:text>
  7671       </xsl:with-param>
  7824       </xsl:with-param>
  7672       <xsl:with-param name="mandatory" select="'no'"/>
  7825       <xsl:with-param name="mandatory" select="'no'"/>
  7673     </xsl:call-template>
  7826     </xsl:call-template>
  7674   </xsl:template>
  7827   </xsl:template>
       
  7828   <xsl:template match="widget[@type='XYGraph']" mode="widget_desc">
       
  7829     <type>
       
  7830       <xsl:value-of select="@type"/>
       
  7831     </type>
       
  7832     <longdesc>
       
  7833       <xsl:text>XYGraph draws a cartesian trend graph re-using styles given for axis,
       
  7834 </xsl:text>
       
  7835       <xsl:text>grid/marks, legends and curves.
       
  7836 </xsl:text>
       
  7837       <xsl:text>
       
  7838 </xsl:text>
       
  7839       <xsl:text>Elements labeled "x_axis" and "y_axis" are svg:groups containg:
       
  7840 </xsl:text>
       
  7841       <xsl:text> - "axis_label" svg:text gives style an alignment for axis labels.
       
  7842 </xsl:text>
       
  7843       <xsl:text> - "interval_major_mark" and "interval_minor_mark" are svg elements to be
       
  7844 </xsl:text>
       
  7845       <xsl:text>   duplicated along axis line to form intervals marks.
       
  7846 </xsl:text>
       
  7847       <xsl:text> - "axis_line"  svg:path is the axis line. Paths must be intersect and their
       
  7848 </xsl:text>
       
  7849       <xsl:text>   bounding box is the chart wall.
       
  7850 </xsl:text>
       
  7851       <xsl:text>
       
  7852 </xsl:text>
       
  7853       <xsl:text>Elements labeled "curve_0", "curve_1", ... are paths whose styles are used
       
  7854 </xsl:text>
       
  7855       <xsl:text>to draw curves corresponding to data from variables passed as HMI tree paths.
       
  7856 </xsl:text>
       
  7857       <xsl:text>"curve_0" is mandatory. HMI variables outnumbering given curves are ignored.
       
  7858 </xsl:text>
       
  7859       <xsl:text>
       
  7860 </xsl:text>
       
  7861     </longdesc>
       
  7862     <shortdesc>
       
  7863       <xsl:text>Cartesian trend graph showing values of given variables over time</xsl:text>
       
  7864     </shortdesc>
       
  7865     <path name="value" count="1+" accepts="HMI_INT,HMI_REAL">
       
  7866       <xsl:text>value</xsl:text>
       
  7867     </path>
       
  7868     <arg name="size" accepts="int">
       
  7869       <xsl:text>buffer size</xsl:text>
       
  7870     </arg>
       
  7871     <arg name="xformat" count="optional" accepts="string">
       
  7872       <xsl:text>format string for X label</xsl:text>
       
  7873     </arg>
       
  7874     <arg name="yformat" count="optional" accepts="string">
       
  7875       <xsl:text>format string for Y label</xsl:text>
       
  7876     </arg>
       
  7877     <arg name="xmin" count="optional" accepts="int,real">
       
  7878       <xsl:text>minimum value foe X axis</xsl:text>
       
  7879     </arg>
       
  7880     <arg name="xmax" count="optional" accepts="int,real">
       
  7881       <xsl:text>maximum value for X axis</xsl:text>
       
  7882     </arg>
       
  7883   </xsl:template>
       
  7884   <xsl:template match="widget[@type='XYGraph']" mode="widget_class">
       
  7885     <xsl:text>class </xsl:text>
       
  7886     <xsl:text>XYGraphWidget</xsl:text>
       
  7887     <xsl:text> extends Widget{
       
  7888 </xsl:text>
       
  7889     <xsl:text>    frequency = 1;
       
  7890 </xsl:text>
       
  7891     <xsl:text>    init() {
       
  7892 </xsl:text>
       
  7893     <xsl:text>        let x_duration_s;
       
  7894 </xsl:text>
       
  7895     <xsl:text>        [x_duration_s,
       
  7896 </xsl:text>
       
  7897     <xsl:text>         this.x_format, this.y_format] = this.args;
       
  7898 </xsl:text>
       
  7899     <xsl:text>
       
  7900 </xsl:text>
       
  7901     <xsl:text>        let timeunit = x_duration_s.slice(-1);
       
  7902 </xsl:text>
       
  7903     <xsl:text>        let factor = {
       
  7904 </xsl:text>
       
  7905     <xsl:text>            "s":1,
       
  7906 </xsl:text>
       
  7907     <xsl:text>            "m":60,
       
  7908 </xsl:text>
       
  7909     <xsl:text>            "h":3600,
       
  7910 </xsl:text>
       
  7911     <xsl:text>            "d":86400}[timeunit];
       
  7912 </xsl:text>
       
  7913     <xsl:text>        if(factor == undefined){
       
  7914 </xsl:text>
       
  7915     <xsl:text>            this.max_data_length = Number(x_duration_s);
       
  7916 </xsl:text>
       
  7917     <xsl:text>            this.x_duration = undefined;
       
  7918 </xsl:text>
       
  7919     <xsl:text>        }else{
       
  7920 </xsl:text>
       
  7921     <xsl:text>            let duration = factor*Number(x_duration_s.slice(0,-1));
       
  7922 </xsl:text>
       
  7923     <xsl:text>            this.max_data_length = undefined;
       
  7924 </xsl:text>
       
  7925     <xsl:text>            this.x_duration = duration*1000;
       
  7926 </xsl:text>
       
  7927     <xsl:text>        }
       
  7928 </xsl:text>
       
  7929     <xsl:text>
       
  7930 </xsl:text>
       
  7931     <xsl:text>
       
  7932 </xsl:text>
       
  7933     <xsl:text>        // Min and Max given with paths are meant to describe visible range,
       
  7934 </xsl:text>
       
  7935     <xsl:text>        // not to clip data.
       
  7936 </xsl:text>
       
  7937     <xsl:text>        this.clip = false;
       
  7938 </xsl:text>
       
  7939     <xsl:text>
       
  7940 </xsl:text>
       
  7941     <xsl:text>        let y_min = Infinity, y_max = -Infinity;
       
  7942 </xsl:text>
       
  7943     <xsl:text>
       
  7944 </xsl:text>
       
  7945     <xsl:text>        // Compute visible Y range by merging fixed curves Y ranges
       
  7946 </xsl:text>
       
  7947     <xsl:text>        for(let minmax of this.minmaxes){
       
  7948 </xsl:text>
       
  7949     <xsl:text>           if(minmax){
       
  7950 </xsl:text>
       
  7951     <xsl:text>               let [min,max] = minmax;
       
  7952 </xsl:text>
       
  7953     <xsl:text>               if(min &lt; y_min)
       
  7954 </xsl:text>
       
  7955     <xsl:text>                   y_min = min;
       
  7956 </xsl:text>
       
  7957     <xsl:text>               if(max &gt; y_max)
       
  7958 </xsl:text>
       
  7959     <xsl:text>                   y_max = max;
       
  7960 </xsl:text>
       
  7961     <xsl:text>           }
       
  7962 </xsl:text>
       
  7963     <xsl:text>        }
       
  7964 </xsl:text>
       
  7965     <xsl:text>
       
  7966 </xsl:text>
       
  7967     <xsl:text>        if(y_min !== Infinity &amp;&amp; y_max !== -Infinity){
       
  7968 </xsl:text>
       
  7969     <xsl:text>           this.fixed_y_range = true;
       
  7970 </xsl:text>
       
  7971     <xsl:text>        } else {
       
  7972 </xsl:text>
       
  7973     <xsl:text>           this.fixed_y_range = false;
       
  7974 </xsl:text>
       
  7975     <xsl:text>        }
       
  7976 </xsl:text>
       
  7977     <xsl:text>
       
  7978 </xsl:text>
       
  7979     <xsl:text>        this.ymin = y_min;
       
  7980 </xsl:text>
       
  7981     <xsl:text>        this.ymax = y_max;
       
  7982 </xsl:text>
       
  7983     <xsl:text>
       
  7984 </xsl:text>
       
  7985     <xsl:text>        this.curves = [];
       
  7986 </xsl:text>
       
  7987     <xsl:text>        this.init_specific();
       
  7988 </xsl:text>
       
  7989     <xsl:text>
       
  7990 </xsl:text>
       
  7991     <xsl:text>        this.reference = new ReferenceFrame(
       
  7992 </xsl:text>
       
  7993     <xsl:text>            [[this.x_interval_minor_mark_elt, this.x_interval_major_mark_elt],
       
  7994 </xsl:text>
       
  7995     <xsl:text>             [this.y_interval_minor_mark_elt, this.y_interval_major_mark_elt]],
       
  7996 </xsl:text>
       
  7997     <xsl:text>            [this.x_axis_label_elt, this.y_axis_label_elt],
       
  7998 </xsl:text>
       
  7999     <xsl:text>            [this.x_axis_line_elt, this.y_axis_line_elt],
       
  8000 </xsl:text>
       
  8001     <xsl:text>            [this.x_format, this.y_format]);
       
  8002 </xsl:text>
       
  8003     <xsl:text>
       
  8004 </xsl:text>
       
  8005     <xsl:text>        let max_stroke_width = 0;
       
  8006 </xsl:text>
       
  8007     <xsl:text>        for(let curve of this.curves){
       
  8008 </xsl:text>
       
  8009     <xsl:text>            if(curve.style.strokeWidth &gt; max_stroke_width){
       
  8010 </xsl:text>
       
  8011     <xsl:text>                max_stroke_width = curve.style.strokeWidth;
       
  8012 </xsl:text>
       
  8013     <xsl:text>            }
       
  8014 </xsl:text>
       
  8015     <xsl:text>        }
       
  8016 </xsl:text>
       
  8017     <xsl:text>
       
  8018 </xsl:text>
       
  8019     <xsl:text>        this.Margins=this.reference.getLengths().map(length =&gt; max_stroke_width/length);
       
  8020 </xsl:text>
       
  8021     <xsl:text>
       
  8022 </xsl:text>
       
  8023     <xsl:text>        // create &lt;clipPath&gt; path and attach it to widget
       
  8024 </xsl:text>
       
  8025     <xsl:text>        let clipPath = document.createElementNS(xmlns,"clipPath");
       
  8026 </xsl:text>
       
  8027     <xsl:text>        let clipPathPath = document.createElementNS(xmlns,"path");
       
  8028 </xsl:text>
       
  8029     <xsl:text>        let clipPathPathDattr = document.createAttribute("d");
       
  8030 </xsl:text>
       
  8031     <xsl:text>        clipPathPathDattr.value = this.reference.getClipPathPathDattr();
       
  8032 </xsl:text>
       
  8033     <xsl:text>        clipPathPath.setAttributeNode(clipPathPathDattr);
       
  8034 </xsl:text>
       
  8035     <xsl:text>        clipPath.appendChild(clipPathPath);
       
  8036 </xsl:text>
       
  8037     <xsl:text>        clipPath.id = randomId();
       
  8038 </xsl:text>
       
  8039     <xsl:text>        this.element.appendChild(clipPath);
       
  8040 </xsl:text>
       
  8041     <xsl:text>
       
  8042 </xsl:text>
       
  8043     <xsl:text>        // assign created clipPath to clip-path property of curves
       
  8044 </xsl:text>
       
  8045     <xsl:text>        for(let curve of this.curves){
       
  8046 </xsl:text>
       
  8047     <xsl:text>            curve.setAttribute("clip-path", "url(#" + clipPath.id + ")");
       
  8048 </xsl:text>
       
  8049     <xsl:text>        }
       
  8050 </xsl:text>
       
  8051     <xsl:text>
       
  8052 </xsl:text>
       
  8053     <xsl:text>        this.curves_data = this.curves.map(_unused =&gt; []);
       
  8054 </xsl:text>
       
  8055     <xsl:text>    }
       
  8056 </xsl:text>
       
  8057     <xsl:text>
       
  8058 </xsl:text>
       
  8059     <xsl:text>    dispatch(value,oldval, index) {
       
  8060 </xsl:text>
       
  8061     <xsl:text>        // TODO: get PLC time instead of browser time
       
  8062 </xsl:text>
       
  8063     <xsl:text>        let time = Date.now();
       
  8064 </xsl:text>
       
  8065     <xsl:text>
       
  8066 </xsl:text>
       
  8067     <xsl:text>        // naive local buffer impl. 
       
  8068 </xsl:text>
       
  8069     <xsl:text>        // data is updated only when graph is visible
       
  8070 </xsl:text>
       
  8071     <xsl:text>        // TODO: replace with separate recording
       
  8072 </xsl:text>
       
  8073     <xsl:text>
       
  8074 </xsl:text>
       
  8075     <xsl:text>        this.curves_data[index].push([time, value]);
       
  8076 </xsl:text>
       
  8077     <xsl:text>        let data_length = this.curves_data[index].length;
       
  8078 </xsl:text>
       
  8079     <xsl:text>        let ymin_damaged = false;
       
  8080 </xsl:text>
       
  8081     <xsl:text>        let ymax_damaged = false;
       
  8082 </xsl:text>
       
  8083     <xsl:text>        let overflow;
       
  8084 </xsl:text>
       
  8085     <xsl:text>
       
  8086 </xsl:text>
       
  8087     <xsl:text>        if(this.max_data_length == undefined){
       
  8088 </xsl:text>
       
  8089     <xsl:text>            let peremption = time - this.x_duration;
       
  8090 </xsl:text>
       
  8091     <xsl:text>            let oldest = this.curves_data[index][0][0]
       
  8092 </xsl:text>
       
  8093     <xsl:text>            this.xmin = peremption;
       
  8094 </xsl:text>
       
  8095     <xsl:text>            if(oldest &lt; peremption){
       
  8096 </xsl:text>
       
  8097     <xsl:text>                // remove first item
       
  8098 </xsl:text>
       
  8099     <xsl:text>                overflow = this.curves_data[index].shift()[1];
       
  8100 </xsl:text>
       
  8101     <xsl:text>                data_length = data_length - 1;
       
  8102 </xsl:text>
       
  8103     <xsl:text>            }
       
  8104 </xsl:text>
       
  8105     <xsl:text>        } else {
       
  8106 </xsl:text>
       
  8107     <xsl:text>            if(data_length &gt; this.max_data_length){
       
  8108 </xsl:text>
       
  8109     <xsl:text>                // remove first item
       
  8110 </xsl:text>
       
  8111     <xsl:text>                [this.xmin, overflow] = this.curves_data[index].shift();
       
  8112 </xsl:text>
       
  8113     <xsl:text>                data_length = data_length - 1;
       
  8114 </xsl:text>
       
  8115     <xsl:text>            } else {
       
  8116 </xsl:text>
       
  8117     <xsl:text>                if(this.xmin == undefined){
       
  8118 </xsl:text>
       
  8119     <xsl:text>                    this.xmin = time;
       
  8120 </xsl:text>
       
  8121     <xsl:text>                }
       
  8122 </xsl:text>
       
  8123     <xsl:text>            }
       
  8124 </xsl:text>
       
  8125     <xsl:text>        }
       
  8126 </xsl:text>
       
  8127     <xsl:text>
       
  8128 </xsl:text>
       
  8129     <xsl:text>        this.xmax = time;
       
  8130 </xsl:text>
       
  8131     <xsl:text>        let Xrange = this.xmax - this.xmin;
       
  8132 </xsl:text>
       
  8133     <xsl:text>
       
  8134 </xsl:text>
       
  8135     <xsl:text>        if(!this.fixed_y_range){
       
  8136 </xsl:text>
       
  8137     <xsl:text>            ymin_damaged = overflow &lt;= this.ymin;
       
  8138 </xsl:text>
       
  8139     <xsl:text>            ymax_damaged = overflow &gt;= this.ymax;
       
  8140 </xsl:text>
       
  8141     <xsl:text>            if(value &gt; this.ymax){
       
  8142 </xsl:text>
       
  8143     <xsl:text>                ymax_damaged = false;
       
  8144 </xsl:text>
       
  8145     <xsl:text>                this.ymax = value;
       
  8146 </xsl:text>
       
  8147     <xsl:text>            }
       
  8148 </xsl:text>
       
  8149     <xsl:text>            if(value &lt; this.ymin){
       
  8150 </xsl:text>
       
  8151     <xsl:text>                ymin_damaged = false;
       
  8152 </xsl:text>
       
  8153     <xsl:text>                this.ymin = value;
       
  8154 </xsl:text>
       
  8155     <xsl:text>            }
       
  8156 </xsl:text>
       
  8157     <xsl:text>        }
       
  8158 </xsl:text>
       
  8159     <xsl:text>        let Yrange = this.ymax - this.ymin;
       
  8160 </xsl:text>
       
  8161     <xsl:text>
       
  8162 </xsl:text>
       
  8163     <xsl:text>        // apply margin by moving min and max to enlarge range
       
  8164 </xsl:text>
       
  8165     <xsl:text>        let [xMargin,yMargin] = zip(this.Margins, [Xrange, Yrange]).map(([m,l]) =&gt; m*l);
       
  8166 </xsl:text>
       
  8167     <xsl:text>        [[this.dxmin, this.dxmax],[this.dymin,this.dymax]] =
       
  8168 </xsl:text>
       
  8169     <xsl:text>            [[this.xmin-xMargin, this.xmax+xMargin],
       
  8170 </xsl:text>
       
  8171     <xsl:text>             [this.ymin-yMargin, this.ymax+yMargin]];
       
  8172 </xsl:text>
       
  8173     <xsl:text>        Xrange += 2*xMargin;
       
  8174 </xsl:text>
       
  8175     <xsl:text>        Yrange += 2*yMargin;
       
  8176 </xsl:text>
       
  8177     <xsl:text>
       
  8178 </xsl:text>
       
  8179     <xsl:text>        // recompute curves "d" attribute
       
  8180 </xsl:text>
       
  8181     <xsl:text>        // FIXME: use SVG getPathData and setPathData when available.
       
  8182 </xsl:text>
       
  8183     <xsl:text>        //        https://svgwg.org/specs/paths/#InterfaceSVGPathData
       
  8184 </xsl:text>
       
  8185     <xsl:text>        //        https://github.com/jarek-foksa/path-data-polyfill
       
  8186 </xsl:text>
       
  8187     <xsl:text>
       
  8188 </xsl:text>
       
  8189     <xsl:text>        let [base_point, xvect, yvect] = this.reference.getBaseRef();
       
  8190 </xsl:text>
       
  8191     <xsl:text>        this.curves_d_attr =
       
  8192 </xsl:text>
       
  8193     <xsl:text>            zip(this.curves_data, this.curves).map(([data,curve]) =&gt; {
       
  8194 </xsl:text>
       
  8195     <xsl:text>                let new_d = data.map(([x,y], i) =&gt; {
       
  8196 </xsl:text>
       
  8197     <xsl:text>                    // compute curve point from data, ranges, and base_ref
       
  8198 </xsl:text>
       
  8199     <xsl:text>                    let xv = vectorscale(xvect, (x - this.dxmin) / Xrange);
       
  8200 </xsl:text>
       
  8201     <xsl:text>                    let yv = vectorscale(yvect, (y - this.dymin) / Yrange);
       
  8202 </xsl:text>
       
  8203     <xsl:text>                    let px = base_point.x + xv.x + yv.x;
       
  8204 </xsl:text>
       
  8205     <xsl:text>                    let py = base_point.y + xv.y + yv.y;
       
  8206 </xsl:text>
       
  8207     <xsl:text>                    if(!this.fixed_y_range){
       
  8208 </xsl:text>
       
  8209     <xsl:text>                        // update min and max from curve data if needed
       
  8210 </xsl:text>
       
  8211     <xsl:text>                        if(ymin_damaged &amp;&amp; y &lt; this.ymin) this.ymin = y;
       
  8212 </xsl:text>
       
  8213     <xsl:text>                        if(ymax_damaged &amp;&amp; y &gt; this.ymax) this.ymax = y;
       
  8214 </xsl:text>
       
  8215     <xsl:text>                    }
       
  8216 </xsl:text>
       
  8217     <xsl:text>
       
  8218 </xsl:text>
       
  8219     <xsl:text>                    return " " + px + "," + py;
       
  8220 </xsl:text>
       
  8221     <xsl:text>                });
       
  8222 </xsl:text>
       
  8223     <xsl:text>
       
  8224 </xsl:text>
       
  8225     <xsl:text>                new_d.unshift("M ");
       
  8226 </xsl:text>
       
  8227     <xsl:text>
       
  8228 </xsl:text>
       
  8229     <xsl:text>                return new_d.join('');
       
  8230 </xsl:text>
       
  8231     <xsl:text>            });
       
  8232 </xsl:text>
       
  8233     <xsl:text>
       
  8234 </xsl:text>
       
  8235     <xsl:text>        // computed curves "d" attr is applied to svg curve during animate();
       
  8236 </xsl:text>
       
  8237     <xsl:text>
       
  8238 </xsl:text>
       
  8239     <xsl:text>        this.request_animate();
       
  8240 </xsl:text>
       
  8241     <xsl:text>    }
       
  8242 </xsl:text>
       
  8243     <xsl:text>
       
  8244 </xsl:text>
       
  8245     <xsl:text>    animate(){
       
  8246 </xsl:text>
       
  8247     <xsl:text>
       
  8248 </xsl:text>
       
  8249     <xsl:text>        // move elements only if enough data
       
  8250 </xsl:text>
       
  8251     <xsl:text>        if(this.curves_data.some(data =&gt; data.length &gt; 1)){
       
  8252 </xsl:text>
       
  8253     <xsl:text>
       
  8254 </xsl:text>
       
  8255     <xsl:text>            // move marks and update labels
       
  8256 </xsl:text>
       
  8257     <xsl:text>            this.reference.applyRanges([[this.dxmin, this.dxmax],
       
  8258 </xsl:text>
       
  8259     <xsl:text>                                        [this.dymin, this.dymax]]);
       
  8260 </xsl:text>
       
  8261     <xsl:text>
       
  8262 </xsl:text>
       
  8263     <xsl:text>            // apply computed curves "d" attributes
       
  8264 </xsl:text>
       
  8265     <xsl:text>            for(let [curve, d_attr] of zip(this.curves, this.curves_d_attr)){
       
  8266 </xsl:text>
       
  8267     <xsl:text>                curve.setAttribute("d", d_attr);
       
  8268 </xsl:text>
       
  8269     <xsl:text>            }
       
  8270 </xsl:text>
       
  8271     <xsl:text>        }
       
  8272 </xsl:text>
       
  8273     <xsl:text>    }
       
  8274 </xsl:text>
       
  8275     <xsl:text>
       
  8276 </xsl:text>
       
  8277     <xsl:text>}
       
  8278 </xsl:text>
       
  8279   </xsl:template>
       
  8280   <xsl:template match="widget[@type='XYGraph']" mode="widget_defs">
       
  8281     <xsl:param name="hmi_element"/>
       
  8282     <xsl:call-template name="defs_by_labels">
       
  8283       <xsl:with-param name="hmi_element" select="$hmi_element"/>
       
  8284       <xsl:with-param name="labels">
       
  8285         <xsl:text>/x_interval_minor_mark /x_axis_line /x_interval_major_mark /x_axis_label</xsl:text>
       
  8286       </xsl:with-param>
       
  8287     </xsl:call-template>
       
  8288     <xsl:call-template name="defs_by_labels">
       
  8289       <xsl:with-param name="hmi_element" select="$hmi_element"/>
       
  8290       <xsl:with-param name="labels">
       
  8291         <xsl:text>/y_interval_minor_mark /y_axis_line /y_interval_major_mark /y_axis_label</xsl:text>
       
  8292       </xsl:with-param>
       
  8293     </xsl:call-template>
       
  8294     <xsl:text>    init_specific() {
       
  8295 </xsl:text>
       
  8296     <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^curve_[0-9]+$')]">
       
  8297       <xsl:variable name="label" select="@inkscape:label"/>
       
  8298       <xsl:variable name="id" select="@id"/>
       
  8299       <xsl:if test="$hmi_element/*[not($id = @id) and @inkscape:label=$label]">
       
  8300         <xsl:message terminate="yes">
       
  8301           <xsl:text>XYGraph id="</xsl:text>
       
  8302           <xsl:value-of select="$id"/>
       
  8303           <xsl:text>", label="</xsl:text>
       
  8304           <xsl:value-of select="$label"/>
       
  8305           <xsl:text>" : elements with data_n label must be unique.</xsl:text>
       
  8306         </xsl:message>
       
  8307       </xsl:if>
       
  8308       <xsl:text>        this.curves[</xsl:text>
       
  8309       <xsl:value-of select="substring(@inkscape:label, 7)"/>
       
  8310       <xsl:text>] = id("</xsl:text>
       
  8311       <xsl:value-of select="@id"/>
       
  8312       <xsl:text>"); /* </xsl:text>
       
  8313       <xsl:value-of select="@inkscape:label"/>
       
  8314       <xsl:text> */
       
  8315 </xsl:text>
       
  8316     </xsl:for-each>
       
  8317     <xsl:text>    }
       
  8318 </xsl:text>
       
  8319   </xsl:template>
       
  8320   <declarations:XYGraph/>
       
  8321   <xsl:template match="declarations:XYGraph">
       
  8322     <xsl:text>
       
  8323 </xsl:text>
       
  8324     <xsl:text>/* </xsl:text>
       
  8325     <xsl:value-of select="local-name()"/>
       
  8326     <xsl:text> */
       
  8327 </xsl:text>
       
  8328     <xsl:text>
       
  8329 </xsl:text>
       
  8330     <xsl:text>function lineFromPath(path_elt) {
       
  8331 </xsl:text>
       
  8332     <xsl:text>    let start = path_elt.getPointAtLength(0);
       
  8333 </xsl:text>
       
  8334     <xsl:text>    let end = path_elt.getPointAtLength(path_elt.getTotalLength());
       
  8335 </xsl:text>
       
  8336     <xsl:text>    return [start, new DOMPoint(end.x - start.x , end.y - start.y)];
       
  8337 </xsl:text>
       
  8338     <xsl:text>};
       
  8339 </xsl:text>
       
  8340     <xsl:text>
       
  8341 </xsl:text>
       
  8342     <xsl:text>function vector(p1, p2) {
       
  8343 </xsl:text>
       
  8344     <xsl:text>    return new DOMPoint(p2.x - p1.x , p2.y - p1.y);
       
  8345 </xsl:text>
       
  8346     <xsl:text>};
       
  8347 </xsl:text>
       
  8348     <xsl:text>
       
  8349 </xsl:text>
       
  8350     <xsl:text>function vectorscale(p1, p2) {
       
  8351 </xsl:text>
       
  8352     <xsl:text>    return new DOMPoint(p2 * p1.x , p2 * p1.y);
       
  8353 </xsl:text>
       
  8354     <xsl:text>};
       
  8355 </xsl:text>
       
  8356     <xsl:text>
       
  8357 </xsl:text>
       
  8358     <xsl:text>function vectorLength(p1) {
       
  8359 </xsl:text>
       
  8360     <xsl:text>    return Math.sqrt(p1.x*p1.x + p1.y*p1.y);
       
  8361 </xsl:text>
       
  8362     <xsl:text>};
       
  8363 </xsl:text>
       
  8364     <xsl:text>
       
  8365 </xsl:text>
       
  8366     <xsl:text>function randomId(){
       
  8367 </xsl:text>
       
  8368     <xsl:text>    return Date.now().toString(36) + Math.random().toString(36).substr(2);
       
  8369 </xsl:text>
       
  8370     <xsl:text>}
       
  8371 </xsl:text>
       
  8372     <xsl:text>
       
  8373 </xsl:text>
       
  8374     <xsl:text>function move_elements_to_group(elements) {
       
  8375 </xsl:text>
       
  8376     <xsl:text>    let newgroup = document.createElementNS(xmlns,"g");
       
  8377 </xsl:text>
       
  8378     <xsl:text>    newgroup.id = randomId();
       
  8379 </xsl:text>
       
  8380     <xsl:text>
       
  8381 </xsl:text>
       
  8382     <xsl:text>    for(let element of elements){
       
  8383 </xsl:text>
       
  8384     <xsl:text>        let parent = element.parentElement;
       
  8385 </xsl:text>
       
  8386     <xsl:text>        if(parent !== null)
       
  8387 </xsl:text>
       
  8388     <xsl:text>            parent.removeChild(element);
       
  8389 </xsl:text>
       
  8390     <xsl:text>        newgroup.appendChild(element);
       
  8391 </xsl:text>
       
  8392     <xsl:text>    }
       
  8393 </xsl:text>
       
  8394     <xsl:text>    return newgroup;
       
  8395 </xsl:text>
       
  8396     <xsl:text>}
       
  8397 </xsl:text>
       
  8398     <xsl:text>function getLinesIntesection(l1, l2) {
       
  8399 </xsl:text>
       
  8400     <xsl:text>    let [l1start, l1vect] = l1;
       
  8401 </xsl:text>
       
  8402     <xsl:text>    let [l2start, l2vect] = l2;
       
  8403 </xsl:text>
       
  8404     <xsl:text>
       
  8405 </xsl:text>
       
  8406     <xsl:text>
       
  8407 </xsl:text>
       
  8408     <xsl:text>    /*
       
  8409 </xsl:text>
       
  8410     <xsl:text>    Compute intersection of two lines
       
  8411 </xsl:text>
       
  8412     <xsl:text>    =================================
       
  8413 </xsl:text>
       
  8414     <xsl:text>
       
  8415 </xsl:text>
       
  8416     <xsl:text>                          ^ l2vect
       
  8417 </xsl:text>
       
  8418     <xsl:text>                         /
       
  8419 </xsl:text>
       
  8420     <xsl:text>                        /
       
  8421 </xsl:text>
       
  8422     <xsl:text>                       /
       
  8423 </xsl:text>
       
  8424     <xsl:text>    l1start ----------X--------------&gt; l1vect
       
  8425 </xsl:text>
       
  8426     <xsl:text>                     / intersection
       
  8427 </xsl:text>
       
  8428     <xsl:text>                    /
       
  8429 </xsl:text>
       
  8430     <xsl:text>                   /
       
  8431 </xsl:text>
       
  8432     <xsl:text>                   l2start
       
  8433 </xsl:text>
       
  8434     <xsl:text>
       
  8435 </xsl:text>
       
  8436     <xsl:text>	*/
       
  8437 </xsl:text>
       
  8438     <xsl:text>    let [x1, y1, x3, y3] = [l1start.x, l1start.y, l2start.x, l2start.y];
       
  8439 </xsl:text>
       
  8440     <xsl:text>	let [x2, y2, x4, y4] = [x1+l1vect.x, y1+l1vect.y, x3+l2vect.x, y3+l2vect.y];
       
  8441 </xsl:text>
       
  8442     <xsl:text>
       
  8443 </xsl:text>
       
  8444     <xsl:text>	// line intercept math by Paul Bourke http://paulbourke.net/geometry/pointlineplane/
       
  8445 </xsl:text>
       
  8446     <xsl:text>	// Determine the intersection point of two line segments
       
  8447 </xsl:text>
       
  8448     <xsl:text>	// Return FALSE if the lines don't intersect
       
  8449 </xsl:text>
       
  8450     <xsl:text>
       
  8451 </xsl:text>
       
  8452     <xsl:text>    // Check if none of the lines are of length 0
       
  8453 </xsl:text>
       
  8454     <xsl:text>    if ((x1 === x2 &amp;&amp; y1 === y2) || (x3 === x4 &amp;&amp; y3 === y4)) {
       
  8455 </xsl:text>
       
  8456     <xsl:text>        return false
       
  8457 </xsl:text>
       
  8458     <xsl:text>    }
       
  8459 </xsl:text>
       
  8460     <xsl:text>
       
  8461 </xsl:text>
       
  8462     <xsl:text>    denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
       
  8463 </xsl:text>
       
  8464     <xsl:text>
       
  8465 </xsl:text>
       
  8466     <xsl:text>    // Lines are parallel
       
  8467 </xsl:text>
       
  8468     <xsl:text>    if (denominator === 0) {
       
  8469 </xsl:text>
       
  8470     <xsl:text>        return false
       
  8471 </xsl:text>
       
  8472     <xsl:text>    }
       
  8473 </xsl:text>
       
  8474     <xsl:text>
       
  8475 </xsl:text>
       
  8476     <xsl:text>    let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
       
  8477 </xsl:text>
       
  8478     <xsl:text>    let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator
       
  8479 </xsl:text>
       
  8480     <xsl:text>
       
  8481 </xsl:text>
       
  8482     <xsl:text>    // Return a object with the x and y coordinates of the intersection
       
  8483 </xsl:text>
       
  8484     <xsl:text>    let x = x1 + ua * (x2 - x1)
       
  8485 </xsl:text>
       
  8486     <xsl:text>    let y = y1 + ua * (y2 - y1)
       
  8487 </xsl:text>
       
  8488     <xsl:text>
       
  8489 </xsl:text>
       
  8490     <xsl:text>    return new DOMPoint(x,y);
       
  8491 </xsl:text>
       
  8492     <xsl:text>};
       
  8493 </xsl:text>
       
  8494     <xsl:text>
       
  8495 </xsl:text>
       
  8496     <xsl:text>class ReferenceFrame {
       
  8497 </xsl:text>
       
  8498     <xsl:text>    constructor(
       
  8499 </xsl:text>
       
  8500     <xsl:text>        // [[Xminor,Xmajor], [Yminor,Ymajor]]
       
  8501 </xsl:text>
       
  8502     <xsl:text>        marks,
       
  8503 </xsl:text>
       
  8504     <xsl:text>        // [Xlabel, Ylabel]
       
  8505 </xsl:text>
       
  8506     <xsl:text>        labels,
       
  8507 </xsl:text>
       
  8508     <xsl:text>        // [Xline, Yline]
       
  8509 </xsl:text>
       
  8510     <xsl:text>        lines,
       
  8511 </xsl:text>
       
  8512     <xsl:text>        // [Xformat, Yformat] printf-like formating strings
       
  8513 </xsl:text>
       
  8514     <xsl:text>        formats
       
  8515 </xsl:text>
       
  8516     <xsl:text>    ){
       
  8517 </xsl:text>
       
  8518     <xsl:text>        this.axes = zip(labels,marks,lines,formats).map(args =&gt; new Axis(...args));
       
  8519 </xsl:text>
       
  8520     <xsl:text>
       
  8521 </xsl:text>
       
  8522     <xsl:text>        let [lx,ly] = this.axes.map(axis =&gt; axis.line);
       
  8523 </xsl:text>
       
  8524     <xsl:text>        let [[xstart, xvect], [ystart, yvect]] = [lx,ly];
       
  8525 </xsl:text>
       
  8526     <xsl:text>        let base_point = this.getBasePoint();
       
  8527 </xsl:text>
       
  8528     <xsl:text>
       
  8529 </xsl:text>
       
  8530     <xsl:text>        // setup clipping for curves
       
  8531 </xsl:text>
       
  8532     <xsl:text>        this.clipPathPathDattr =
       
  8533 </xsl:text>
       
  8534     <xsl:text>            "m " + base_point.x + "," + base_point.y + " "
       
  8535 </xsl:text>
       
  8536     <xsl:text>                 + xvect.x + "," + xvect.y + " "
       
  8537 </xsl:text>
       
  8538     <xsl:text>                 + yvect.x + "," + yvect.y + " "
       
  8539 </xsl:text>
       
  8540     <xsl:text>                 + -xvect.x + "," + -xvect.y + " "
       
  8541 </xsl:text>
       
  8542     <xsl:text>                 + -yvect.x + "," + -yvect.y + " z";
       
  8543 </xsl:text>
       
  8544     <xsl:text>
       
  8545 </xsl:text>
       
  8546     <xsl:text>        this.base_ref = [base_point, xvect, yvect];
       
  8547 </xsl:text>
       
  8548     <xsl:text>
       
  8549 </xsl:text>
       
  8550     <xsl:text>        this.lengths = [xvect,yvect].map(v =&gt; vectorLength(v));
       
  8551 </xsl:text>
       
  8552     <xsl:text>
       
  8553 </xsl:text>
       
  8554     <xsl:text>        for(let axis of this.axes){
       
  8555 </xsl:text>
       
  8556     <xsl:text>            axis.setBasePoint(base_point);
       
  8557 </xsl:text>
       
  8558     <xsl:text>        }
       
  8559 </xsl:text>
       
  8560     <xsl:text>    }
       
  8561 </xsl:text>
       
  8562     <xsl:text>
       
  8563 </xsl:text>
       
  8564     <xsl:text>    getLengths(){
       
  8565 </xsl:text>
       
  8566     <xsl:text>        return this.lengths;
       
  8567 </xsl:text>
       
  8568     <xsl:text>    }
       
  8569 </xsl:text>
       
  8570     <xsl:text>
       
  8571 </xsl:text>
       
  8572     <xsl:text>	getBaseRef(){
       
  8573 </xsl:text>
       
  8574     <xsl:text>        return this.base_ref;
       
  8575 </xsl:text>
       
  8576     <xsl:text>	}
       
  8577 </xsl:text>
       
  8578     <xsl:text>
       
  8579 </xsl:text>
       
  8580     <xsl:text>    getClipPathPathDattr(){
       
  8581 </xsl:text>
       
  8582     <xsl:text>        return this.clipPathPathDattr;
       
  8583 </xsl:text>
       
  8584     <xsl:text>    }
       
  8585 </xsl:text>
       
  8586     <xsl:text>
       
  8587 </xsl:text>
       
  8588     <xsl:text>    applyRanges(ranges){
       
  8589 </xsl:text>
       
  8590     <xsl:text>        let origin_moves = zip(ranges,this.axes).map(([range,axis]) =&gt; axis.applyRange(...range));
       
  8591 </xsl:text>
       
  8592     <xsl:text>		zip(origin_moves.reverse(),this.axes).forEach(([vect,axis]) =&gt; axis.moveOrigin(vect));
       
  8593 </xsl:text>
       
  8594     <xsl:text>    }
       
  8595 </xsl:text>
       
  8596     <xsl:text>
       
  8597 </xsl:text>
       
  8598     <xsl:text>    getBasePoint() {
       
  8599 </xsl:text>
       
  8600     <xsl:text>        let [[xstart, xvect], [ystart, yvect]] = this.axes.map(axis =&gt; axis.line);
       
  8601 </xsl:text>
       
  8602     <xsl:text>
       
  8603 </xsl:text>
       
  8604     <xsl:text>        /*
       
  8605 </xsl:text>
       
  8606     <xsl:text>        Compute graph clipping region base point
       
  8607 </xsl:text>
       
  8608     <xsl:text>        ========================================
       
  8609 </xsl:text>
       
  8610     <xsl:text>
       
  8611 </xsl:text>
       
  8612     <xsl:text>        Clipping region is a parallelogram containing axes lines,
       
  8613 </xsl:text>
       
  8614     <xsl:text>        and whose sides are parallel to axes line respectively.
       
  8615 </xsl:text>
       
  8616     <xsl:text>        Given axes lines are not starting at the same point, hereafter is
       
  8617 </xsl:text>
       
  8618     <xsl:text>        calculus of parallelogram base point.
       
  8619 </xsl:text>
       
  8620     <xsl:text>
       
  8621 </xsl:text>
       
  8622     <xsl:text>                              ^ given Y axis (yvect)
       
  8623 </xsl:text>
       
  8624     <xsl:text>                   /         /
       
  8625 </xsl:text>
       
  8626     <xsl:text>                  /         /
       
  8627 </xsl:text>
       
  8628     <xsl:text>                 /         /
       
  8629 </xsl:text>
       
  8630     <xsl:text>         xstart *---------*--------------&gt; given X axis (xvect)
       
  8631 </xsl:text>
       
  8632     <xsl:text>               /         /origin
       
  8633 </xsl:text>
       
  8634     <xsl:text>              /         /
       
  8635 </xsl:text>
       
  8636     <xsl:text>             *---------*--------------
       
  8637 </xsl:text>
       
  8638     <xsl:text>        base_point   ystart
       
  8639 </xsl:text>
       
  8640     <xsl:text>
       
  8641 </xsl:text>
       
  8642     <xsl:text>        */
       
  8643 </xsl:text>
       
  8644     <xsl:text>
       
  8645 </xsl:text>
       
  8646     <xsl:text>        let base_point = getLinesIntesection([xstart,yvect],[ystart,xvect]);
       
  8647 </xsl:text>
       
  8648     <xsl:text>
       
  8649 </xsl:text>
       
  8650     <xsl:text>        return base_point;
       
  8651 </xsl:text>
       
  8652     <xsl:text>
       
  8653 </xsl:text>
       
  8654     <xsl:text>    }
       
  8655 </xsl:text>
       
  8656     <xsl:text>
       
  8657 </xsl:text>
       
  8658     <xsl:text>}
       
  8659 </xsl:text>
       
  8660     <xsl:text>
       
  8661 </xsl:text>
       
  8662     <xsl:text>class Axis {
       
  8663 </xsl:text>
       
  8664     <xsl:text>    constructor(label, marks, line, format){
       
  8665 </xsl:text>
       
  8666     <xsl:text>        this.lineElement = line;
       
  8667 </xsl:text>
       
  8668     <xsl:text>        this.line = lineFromPath(line);
       
  8669 </xsl:text>
       
  8670     <xsl:text>        this.format = format;
       
  8671 </xsl:text>
       
  8672     <xsl:text>
       
  8673 </xsl:text>
       
  8674     <xsl:text>        this.label = label;
       
  8675 </xsl:text>
       
  8676     <xsl:text>        this.marks = marks;
       
  8677 </xsl:text>
       
  8678     <xsl:text>
       
  8679 </xsl:text>
       
  8680     <xsl:text>
       
  8681 </xsl:text>
       
  8682     <xsl:text>        // add transforms for elements sliding along the axis line
       
  8683 </xsl:text>
       
  8684     <xsl:text>        for(let [elementname,element] of zip(["minor", "major", "label"],[...marks,label])){
       
  8685 </xsl:text>
       
  8686     <xsl:text>            for(let name of ["base","slide"]){
       
  8687 </xsl:text>
       
  8688     <xsl:text>                let transform = svg_root.createSVGTransform();
       
  8689 </xsl:text>
       
  8690     <xsl:text>                element.transform.baseVal.insertItemBefore(transform,0);
       
  8691 </xsl:text>
       
  8692     <xsl:text>                this[elementname+"_"+name+"_transform"]=transform;
       
  8693 </xsl:text>
       
  8694     <xsl:text>            };
       
  8695 </xsl:text>
       
  8696     <xsl:text>        };
       
  8697 </xsl:text>
       
  8698     <xsl:text>
       
  8699 </xsl:text>
       
  8700     <xsl:text>        // group marks an labels together
       
  8701 </xsl:text>
       
  8702     <xsl:text>        let parent = line.parentElement;
       
  8703 </xsl:text>
       
  8704     <xsl:text>        this.marks_group = move_elements_to_group(marks);
       
  8705 </xsl:text>
       
  8706     <xsl:text>        this.marks_and_label_group = move_elements_to_group([this.marks_group, label]);
       
  8707 </xsl:text>
       
  8708     <xsl:text>        this.group = move_elements_to_group([this.marks_and_label_group,line]);
       
  8709 </xsl:text>
       
  8710     <xsl:text>        parent.appendChild(this.group);
       
  8711 </xsl:text>
       
  8712     <xsl:text>
       
  8713 </xsl:text>
       
  8714     <xsl:text>        // Add transforms to group
       
  8715 </xsl:text>
       
  8716     <xsl:text>        for(let name of ["base","origin"]){
       
  8717 </xsl:text>
       
  8718     <xsl:text>            let transform = svg_root.createSVGTransform();
       
  8719 </xsl:text>
       
  8720     <xsl:text>            this.group.transform.baseVal.appendItem(transform);
       
  8721 </xsl:text>
       
  8722     <xsl:text>            this[name+"_transform"]=transform;
       
  8723 </xsl:text>
       
  8724     <xsl:text>        };
       
  8725 </xsl:text>
       
  8726     <xsl:text>
       
  8727 </xsl:text>
       
  8728     <xsl:text>        this.marks_and_label_group_transform = svg_root.createSVGTransform();
       
  8729 </xsl:text>
       
  8730     <xsl:text>        this.marks_and_label_group.transform.baseVal.appendItem(this.marks_and_label_group_transform);
       
  8731 </xsl:text>
       
  8732     <xsl:text>
       
  8733 </xsl:text>
       
  8734     <xsl:text>        this.duplicates = [];
       
  8735 </xsl:text>
       
  8736     <xsl:text>        this.last_duplicate_index = 0;
       
  8737 </xsl:text>
       
  8738     <xsl:text>    }
       
  8739 </xsl:text>
       
  8740     <xsl:text>
       
  8741 </xsl:text>
       
  8742     <xsl:text>    setBasePoint(base_point){
       
  8743 </xsl:text>
       
  8744     <xsl:text>        // move Axis to base point
       
  8745 </xsl:text>
       
  8746     <xsl:text>        let [start, _vect] = this.line;
       
  8747 </xsl:text>
       
  8748     <xsl:text>        let v = vector(start, base_point);
       
  8749 </xsl:text>
       
  8750     <xsl:text>        this.base_transform.setTranslate(v.x, v.y);
       
  8751 </xsl:text>
       
  8752     <xsl:text>
       
  8753 </xsl:text>
       
  8754     <xsl:text>        // Move marks and label to base point.
       
  8755 </xsl:text>
       
  8756     <xsl:text>        // _|_______          _|________
       
  8757 </xsl:text>
       
  8758     <xsl:text>        //  |  '  |     ==&gt;    '
       
  8759 </xsl:text>
       
  8760     <xsl:text>        //  |     0            0
       
  8761 </xsl:text>
       
  8762     <xsl:text>        //  |                  |
       
  8763 </xsl:text>
       
  8764     <xsl:text>
       
  8765 </xsl:text>
       
  8766     <xsl:text>        for(let [markname,mark] of zip(["minor", "major"],this.marks)){
       
  8767 </xsl:text>
       
  8768     <xsl:text>            let pos = vector(
       
  8769 </xsl:text>
       
  8770     <xsl:text>                // Marks are expected to be paths
       
  8771 </xsl:text>
       
  8772     <xsl:text>                // paths are expected to be lines
       
  8773 </xsl:text>
       
  8774     <xsl:text>                // intersection with axis line is taken 
       
  8775 </xsl:text>
       
  8776     <xsl:text>                // as reference for mark position
       
  8777 </xsl:text>
       
  8778     <xsl:text>                getLinesIntesection(
       
  8779 </xsl:text>
       
  8780     <xsl:text>                    this.line, lineFromPath(mark)),base_point);
       
  8781 </xsl:text>
       
  8782     <xsl:text>            this[markname+"_base_transform"].setTranslate(pos.x - v.x, pos.y - v.y);
       
  8783 </xsl:text>
       
  8784     <xsl:text>            if(markname == "major"){ // label follow major mark
       
  8785 </xsl:text>
       
  8786     <xsl:text>                this.label_base_transform.setTranslate(pos.x - v.x, pos.y - v.y);
       
  8787 </xsl:text>
       
  8788     <xsl:text>            }
       
  8789 </xsl:text>
       
  8790     <xsl:text>        }
       
  8791 </xsl:text>
       
  8792     <xsl:text>    }
       
  8793 </xsl:text>
       
  8794     <xsl:text>
       
  8795 </xsl:text>
       
  8796     <xsl:text>	moveOrigin(vect){
       
  8797 </xsl:text>
       
  8798     <xsl:text>		this.origin_transform.setTranslate(vect.x, vect.y);
       
  8799 </xsl:text>
       
  8800     <xsl:text>	}
       
  8801 </xsl:text>
       
  8802     <xsl:text>
       
  8803 </xsl:text>
       
  8804     <xsl:text>    applyRange(min, max){
       
  8805 </xsl:text>
       
  8806     <xsl:text>        let range = max - min;
       
  8807 </xsl:text>
       
  8808     <xsl:text>
       
  8809 </xsl:text>
       
  8810     <xsl:text>        // compute how many units for a mark
       
  8811 </xsl:text>
       
  8812     <xsl:text>        //
       
  8813 </xsl:text>
       
  8814     <xsl:text>        // - Units are expected to be an order of magnitude smaller than range,
       
  8815 </xsl:text>
       
  8816     <xsl:text>        //   so that marks are not too dense and also not too sparse.
       
  8817 </xsl:text>
       
  8818     <xsl:text>        //   Order of magnitude of range is log10(range)
       
  8819 </xsl:text>
       
  8820     <xsl:text>        //
       
  8821 </xsl:text>
       
  8822     <xsl:text>        // - Units are necessarily power of ten, otherwise it is complicated to
       
  8823 </xsl:text>
       
  8824     <xsl:text>        //   fill the text in labels...
       
  8825 </xsl:text>
       
  8826     <xsl:text>        //   Unit is pow(10, integer_number )
       
  8827 </xsl:text>
       
  8828     <xsl:text>        //
       
  8829 </xsl:text>
       
  8830     <xsl:text>        // - To transform order of magnitude to an integer, floor() is used.
       
  8831 </xsl:text>
       
  8832     <xsl:text>        //   This results in a count of mark fluctuating in between 10 and 100.
       
  8833 </xsl:text>
       
  8834     <xsl:text>        //
       
  8835 </xsl:text>
       
  8836     <xsl:text>        // - To spare resources result is better in between 3 and 30,
       
  8837 </xsl:text>
       
  8838     <xsl:text>        //   and log10(3) is substracted to order of magnitude to obtain this
       
  8839 </xsl:text>
       
  8840     <xsl:text>        let unit = Math.pow(10, Math.floor(Math.log10(range)-Math.log10(3)));
       
  8841 </xsl:text>
       
  8842     <xsl:text>
       
  8843 </xsl:text>
       
  8844     <xsl:text>        // TODO: for time values (ms), units may be :
       
  8845 </xsl:text>
       
  8846     <xsl:text>        //       1       -&gt; ms
       
  8847 </xsl:text>
       
  8848     <xsl:text>        //       10      -&gt; s/100
       
  8849 </xsl:text>
       
  8850     <xsl:text>        //       100     -&gt; s/10
       
  8851 </xsl:text>
       
  8852     <xsl:text>        //       1000    -&gt; s
       
  8853 </xsl:text>
       
  8854     <xsl:text>        //       60000   -&gt; min
       
  8855 </xsl:text>
       
  8856     <xsl:text>        //       3600000 -&gt; hour
       
  8857 </xsl:text>
       
  8858     <xsl:text>        //       ...
       
  8859 </xsl:text>
       
  8860     <xsl:text>        //
       
  8861 </xsl:text>
       
  8862     <xsl:text>
       
  8863 </xsl:text>
       
  8864     <xsl:text>        // Compute position of origin along axis [0...range]
       
  8865 </xsl:text>
       
  8866     <xsl:text>
       
  8867 </xsl:text>
       
  8868     <xsl:text>        // min &lt; 0, max &gt; 0, offset = -min
       
  8869 </xsl:text>
       
  8870     <xsl:text>        // _____________|________________
       
  8871 </xsl:text>
       
  8872     <xsl:text>        // ... -3 -2 -1 |0  1  2  3  4 ...
       
  8873 </xsl:text>
       
  8874     <xsl:text>        // &lt;--offset---&gt; ^
       
  8875 </xsl:text>
       
  8876     <xsl:text>        //               |_original
       
  8877 </xsl:text>
       
  8878     <xsl:text>
       
  8879 </xsl:text>
       
  8880     <xsl:text>        // min &gt; 0, max &gt; 0, offset = 0
       
  8881 </xsl:text>
       
  8882     <xsl:text>        // |________________
       
  8883 </xsl:text>
       
  8884     <xsl:text>        // |6  7  8  9  10...
       
  8885 </xsl:text>
       
  8886     <xsl:text>        //  ^
       
  8887 </xsl:text>
       
  8888     <xsl:text>        //  |_original
       
  8889 </xsl:text>
       
  8890     <xsl:text>
       
  8891 </xsl:text>
       
  8892     <xsl:text>        // min &lt; 0, max &lt; 0, offset = max-min (range)
       
  8893 </xsl:text>
       
  8894     <xsl:text>        // _____________|_
       
  8895 </xsl:text>
       
  8896     <xsl:text>        // ... -5 -4 -3 |-2
       
  8897 </xsl:text>
       
  8898     <xsl:text>        // &lt;--offset---&gt; ^
       
  8899 </xsl:text>
       
  8900     <xsl:text>        //               |_original
       
  8901 </xsl:text>
       
  8902     <xsl:text>
       
  8903 </xsl:text>
       
  8904     <xsl:text>        let offset = (max&gt;=0 &amp;&amp; min&gt;=0) ? 0 : (
       
  8905 </xsl:text>
       
  8906     <xsl:text>                     (max&lt;0 &amp;&amp; min&lt;0)   ? range : -min);
       
  8907 </xsl:text>
       
  8908     <xsl:text>
       
  8909 </xsl:text>
       
  8910     <xsl:text>        // compute unit vector
       
  8911 </xsl:text>
       
  8912     <xsl:text>        let [_start, vect] = this.line;
       
  8913 </xsl:text>
       
  8914     <xsl:text>        let unit_vect = vectorscale(vect, 1/range);
       
  8915 </xsl:text>
       
  8916     <xsl:text>        let [mark_min, mark_max, mark_offset] = [min,max,offset].map(val =&gt; Math.round(val/unit));
       
  8917 </xsl:text>
       
  8918     <xsl:text>        let mark_count = mark_max-mark_min;
       
  8919 </xsl:text>
       
  8920     <xsl:text>
       
  8921 </xsl:text>
       
  8922     <xsl:text>        // apply unit vector to marks and label
       
  8923 </xsl:text>
       
  8924     <xsl:text>        // offset is a representing position of an 
       
  8925 </xsl:text>
       
  8926     <xsl:text>        // axis along the opposit axis line, expressed in major marks units
       
  8927 </xsl:text>
       
  8928     <xsl:text>        // unit_vect is unit vector
       
  8929 </xsl:text>
       
  8930     <xsl:text>
       
  8931 </xsl:text>
       
  8932     <xsl:text>        //              ^
       
  8933 </xsl:text>
       
  8934     <xsl:text>        //              | unit_vect
       
  8935 </xsl:text>
       
  8936     <xsl:text>        //              |&lt;---&gt;
       
  8937 </xsl:text>
       
  8938     <xsl:text>        //     _________|__________&gt;
       
  8939 </xsl:text>
       
  8940     <xsl:text>        //     ^  |  '  |  '  |  '
       
  8941 </xsl:text>
       
  8942     <xsl:text>        //     |yoffset |     1 
       
  8943 </xsl:text>
       
  8944     <xsl:text>        //     |        |
       
  8945 </xsl:text>
       
  8946     <xsl:text>        //     v xoffset|
       
  8947 </xsl:text>
       
  8948     <xsl:text>        //     X&lt;------&gt;|
       
  8949 </xsl:text>
       
  8950     <xsl:text>        // base_point
       
  8951 </xsl:text>
       
  8952     <xsl:text>
       
  8953 </xsl:text>
       
  8954     <xsl:text>        // move major marks and label to first positive mark position
       
  8955 </xsl:text>
       
  8956     <xsl:text>        // let v = vectorscale(unit_vect, unit);
       
  8957 </xsl:text>
       
  8958     <xsl:text>        // this.label_slide_transform.setTranslate(v.x, v.y);
       
  8959 </xsl:text>
       
  8960     <xsl:text>        // this.major_slide_transform.setTranslate(v.x, v.y);
       
  8961 </xsl:text>
       
  8962     <xsl:text>        // move minor mark to first half positive mark position
       
  8963 </xsl:text>
       
  8964     <xsl:text>        let v = vectorscale(unit_vect, unit/2);
       
  8965 </xsl:text>
       
  8966     <xsl:text>        this.minor_slide_transform.setTranslate(v.x, v.y);
       
  8967 </xsl:text>
       
  8968     <xsl:text>
       
  8969 </xsl:text>
       
  8970     <xsl:text>        // duplicate marks and labels as needed
       
  8971 </xsl:text>
       
  8972     <xsl:text>        let current_mark_count = this.duplicates.length;
       
  8973 </xsl:text>
       
  8974     <xsl:text>        for(let i = current_mark_count; i &lt;= mark_count; i++){
       
  8975 </xsl:text>
       
  8976     <xsl:text>            // cloneNode() label and add a svg:use of marks in a new group
       
  8977 </xsl:text>
       
  8978     <xsl:text>            let newgroup = document.createElementNS(xmlns,"g");
       
  8979 </xsl:text>
       
  8980     <xsl:text>            let transform = svg_root.createSVGTransform();
       
  8981 </xsl:text>
       
  8982     <xsl:text>            let newlabel = this.label.cloneNode(true);
       
  8983 </xsl:text>
       
  8984     <xsl:text>            let newuse = document.createElementNS(xmlns,"use");
       
  8985 </xsl:text>
       
  8986     <xsl:text>            let newuseAttr = document.createAttribute("href");
       
  8987 </xsl:text>
       
  8988     <xsl:text>            newuseAttr.value = "#"+this.marks_group.id;
       
  8989 </xsl:text>
       
  8990     <xsl:text>            newuse.setAttributeNode(newuseAttr);
       
  8991 </xsl:text>
       
  8992     <xsl:text>            newgroup.transform.baseVal.appendItem(transform);
       
  8993 </xsl:text>
       
  8994     <xsl:text>            newgroup.appendChild(newlabel);
       
  8995 </xsl:text>
       
  8996     <xsl:text>            newgroup.appendChild(newuse);
       
  8997 </xsl:text>
       
  8998     <xsl:text>            this.duplicates.push([transform,newgroup]);
       
  8999 </xsl:text>
       
  9000     <xsl:text>        }
       
  9001 </xsl:text>
       
  9002     <xsl:text>
       
  9003 </xsl:text>
       
  9004     <xsl:text>        // move marks and labels, set labels
       
  9005 </xsl:text>
       
  9006     <xsl:text>        // 
       
  9007 </xsl:text>
       
  9008     <xsl:text>        // min &gt; 0, max &gt; 0, offset = 0
       
  9009 </xsl:text>
       
  9010     <xsl:text>        //         ^
       
  9011 </xsl:text>
       
  9012     <xsl:text>        //         |________&gt;
       
  9013 </xsl:text>
       
  9014     <xsl:text>        //        '| |  '  |
       
  9015 </xsl:text>
       
  9016     <xsl:text>        //         | 6     7
       
  9017 </xsl:text>
       
  9018     <xsl:text>        //         X
       
  9019 </xsl:text>
       
  9020     <xsl:text>        //     base_point
       
  9021 </xsl:text>
       
  9022     <xsl:text>        //
       
  9023 </xsl:text>
       
  9024     <xsl:text>        // min &lt; 0, max &gt; 0, offset = -min
       
  9025 </xsl:text>
       
  9026     <xsl:text>        //              ^
       
  9027 </xsl:text>
       
  9028     <xsl:text>        //     _________|__________&gt;
       
  9029 </xsl:text>
       
  9030     <xsl:text>        //     '  |  '  |  '  |  '
       
  9031 </xsl:text>
       
  9032     <xsl:text>        //       -1     |     1 
       
  9033 </xsl:text>
       
  9034     <xsl:text>        //       offset |
       
  9035 </xsl:text>
       
  9036     <xsl:text>        //     X&lt;------&gt;|
       
  9037 </xsl:text>
       
  9038     <xsl:text>        // base_point
       
  9039 </xsl:text>
       
  9040     <xsl:text>        //
       
  9041 </xsl:text>
       
  9042     <xsl:text>        // min &lt; 0, max &lt; 0, offset = range
       
  9043 </xsl:text>
       
  9044     <xsl:text>        //                 ^
       
  9045 </xsl:text>
       
  9046     <xsl:text>        //     ____________|    
       
  9047 </xsl:text>
       
  9048     <xsl:text>        //      '  |  '  | |'
       
  9049 </xsl:text>
       
  9050     <xsl:text>        //        -5    -4 |
       
  9051 </xsl:text>
       
  9052     <xsl:text>        //         offset  |
       
  9053 </xsl:text>
       
  9054     <xsl:text>        //     X&lt;---------&gt;|
       
  9055 </xsl:text>
       
  9056     <xsl:text>        // base_point
       
  9057 </xsl:text>
       
  9058     <xsl:text>
       
  9059 </xsl:text>
       
  9060     <xsl:text>        let duplicate_index = 0;
       
  9061 </xsl:text>
       
  9062     <xsl:text>        for(let mark_index = 0; mark_index &lt;= mark_count; mark_index++){
       
  9063 </xsl:text>
       
  9064     <xsl:text>            let val = (mark_min + mark_index) * unit;
       
  9065 </xsl:text>
       
  9066     <xsl:text>            let vec = vectorscale(unit_vect, val - min);
       
  9067 </xsl:text>
       
  9068     <xsl:text>            let text = this.format ? sprintf(this.format, val) : val.toString();
       
  9069 </xsl:text>
       
  9070     <xsl:text>            if(mark_index == mark_offset){
       
  9071 </xsl:text>
       
  9072     <xsl:text>                // apply offset to original marks and label groups
       
  9073 </xsl:text>
       
  9074     <xsl:text>                this.marks_and_label_group_transform.setTranslate(vec.x, vec.y);
       
  9075 </xsl:text>
       
  9076     <xsl:text>
       
  9077 </xsl:text>
       
  9078     <xsl:text>                // update original label text
       
  9079 </xsl:text>
       
  9080     <xsl:text>                this.label.getElementsByTagName("tspan")[0].textContent = text;
       
  9081 </xsl:text>
       
  9082     <xsl:text>            } else {
       
  9083 </xsl:text>
       
  9084     <xsl:text>                let [transform,element] = this.duplicates[duplicate_index++];
       
  9085 </xsl:text>
       
  9086     <xsl:text>
       
  9087 </xsl:text>
       
  9088     <xsl:text>                // apply unit vector*N to marks and label groups
       
  9089 </xsl:text>
       
  9090     <xsl:text>                transform.setTranslate(vec.x, vec.y);
       
  9091 </xsl:text>
       
  9092     <xsl:text>
       
  9093 </xsl:text>
       
  9094     <xsl:text>                // update label text
       
  9095 </xsl:text>
       
  9096     <xsl:text>                element.getElementsByTagName("tspan")[0].textContent = text;
       
  9097 </xsl:text>
       
  9098     <xsl:text>
       
  9099 </xsl:text>
       
  9100     <xsl:text>                // Attach to group if not already
       
  9101 </xsl:text>
       
  9102     <xsl:text>                if(element.parentElement == null){
       
  9103 </xsl:text>
       
  9104     <xsl:text>                    this.group.appendChild(element);
       
  9105 </xsl:text>
       
  9106     <xsl:text>                }
       
  9107 </xsl:text>
       
  9108     <xsl:text>            }
       
  9109 </xsl:text>
       
  9110     <xsl:text>        }
       
  9111 </xsl:text>
       
  9112     <xsl:text>
       
  9113 </xsl:text>
       
  9114     <xsl:text>        let save_duplicate_index = duplicate_index;
       
  9115 </xsl:text>
       
  9116     <xsl:text>        // dettach marks and label from group if not anymore visible
       
  9117 </xsl:text>
       
  9118     <xsl:text>        for(;duplicate_index &lt; this.last_duplicate_index; duplicate_index++){
       
  9119 </xsl:text>
       
  9120     <xsl:text>            let [transform,element] = this.duplicates[duplicate_index];
       
  9121 </xsl:text>
       
  9122     <xsl:text>            this.group.removeChild(element);
       
  9123 </xsl:text>
       
  9124     <xsl:text>        }
       
  9125 </xsl:text>
       
  9126     <xsl:text>
       
  9127 </xsl:text>
       
  9128     <xsl:text>        this.last_duplicate_index = save_duplicate_index;
       
  9129 </xsl:text>
       
  9130     <xsl:text>
       
  9131 </xsl:text>
       
  9132     <xsl:text>		return vectorscale(unit_vect, offset);
       
  9133 </xsl:text>
       
  9134     <xsl:text>    }
       
  9135 </xsl:text>
       
  9136     <xsl:text>}
       
  9137 </xsl:text>
       
  9138     <xsl:text>
       
  9139 </xsl:text>
       
  9140   </xsl:template>
  7675   <xsl:template match="/">
  9141   <xsl:template match="/">
  7676     <xsl:comment>
  9142     <xsl:comment>
  7677       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  9143       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
  7678     </xsl:comment>
  9144     </xsl:comment>
  7679     <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  9145     <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  7749 </xsl:text>
  9215 </xsl:text>
  7750           <xsl:text>        text: /^[^%]+/,
  9216           <xsl:text>        text: /^[^%]+/,
  7751 </xsl:text>
  9217 </xsl:text>
  7752           <xsl:text>        modulo: /^%{2}/,
  9218           <xsl:text>        modulo: /^%{2}/,
  7753 </xsl:text>
  9219 </xsl:text>
  7754           <xsl:text>        placeholder: /^%(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,
  9220           <xsl:text>        placeholder: /^%(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxXD])/,
  7755 </xsl:text>
  9221 </xsl:text>
  7756           <xsl:text>        key: /^([a-z_][a-z_\d]*)/i,
  9222           <xsl:text>        key: /^([a-z_][a-z_\d]*)/i,
  7757 </xsl:text>
  9223 </xsl:text>
  7758           <xsl:text>        key_access: /^\.([a-z_][a-z_\d]*)/i,
  9224           <xsl:text>        key_access: /^\.([a-z_][a-z_\d]*)/i,
  7759 </xsl:text>
  9225 </xsl:text>
  7875 </xsl:text>
  9341 </xsl:text>
  7876           <xsl:text>                        arg = parseInt(arg, 10)
  9342           <xsl:text>                        arg = parseInt(arg, 10)
  7877 </xsl:text>
  9343 </xsl:text>
  7878           <xsl:text>                        break
  9344           <xsl:text>                        break
  7879 </xsl:text>
  9345 </xsl:text>
       
  9346           <xsl:text>                    case 'D':
       
  9347 </xsl:text>
       
  9348           <xsl:text>                        /*  
       
  9349 </xsl:text>
       
  9350           <xsl:text>
       
  9351 </xsl:text>
       
  9352           <xsl:text>                            select date format with width
       
  9353 </xsl:text>
       
  9354           <xsl:text>                            select time format with precision
       
  9355 </xsl:text>
       
  9356           <xsl:text>                            %D  =&gt; 13:31 AM (default)
       
  9357 </xsl:text>
       
  9358           <xsl:text>                            %1D  =&gt; 13:31 AM
       
  9359 </xsl:text>
       
  9360           <xsl:text>                            %.1D  =&gt; 07/07/20
       
  9361 </xsl:text>
       
  9362           <xsl:text>                            %1.1D  =&gt; 07/07/20, 13:31 AM
       
  9363 </xsl:text>
       
  9364           <xsl:text>                            %1.2D  =&gt; 07/07/20, 13:31:55 AM
       
  9365 </xsl:text>
       
  9366           <xsl:text>                            %2.2D  =&gt; May 5, 2022, 9:29:16 AM
       
  9367 </xsl:text>
       
  9368           <xsl:text>                            %3.3D  =&gt; May 5, 2022 at 9:28:16 AM GMT+2
       
  9369 </xsl:text>
       
  9370           <xsl:text>                            %4.4D  =&gt; Thursday, May 5, 2022 at 9:26:59 AM Central European Summer Time
       
  9371 </xsl:text>
       
  9372           <xsl:text>
       
  9373 </xsl:text>
       
  9374           <xsl:text>                            see meaning of DateTimeFormat's options "datestyle" and "timestyle" in MDN 
       
  9375 </xsl:text>
       
  9376           <xsl:text>                        */
       
  9377 </xsl:text>
       
  9378           <xsl:text>
       
  9379 </xsl:text>
       
  9380           <xsl:text>                        let [datestyle, timestyle] = [ph.width, ph.precision].map(val =&gt; ({
       
  9381 </xsl:text>
       
  9382           <xsl:text>                            1: "short",
       
  9383 </xsl:text>
       
  9384           <xsl:text>                            2: "medium",
       
  9385 </xsl:text>
       
  9386           <xsl:text>                            3: "long",
       
  9387 </xsl:text>
       
  9388           <xsl:text>                            4: "full"
       
  9389 </xsl:text>
       
  9390           <xsl:text>                        }[val]));
       
  9391 </xsl:text>
       
  9392           <xsl:text>
       
  9393 </xsl:text>
       
  9394           <xsl:text>                        if(timestyle === undefined &amp;&amp; datestyle === undefined){
       
  9395 </xsl:text>
       
  9396           <xsl:text>                            timestyle = "short";
       
  9397 </xsl:text>
       
  9398           <xsl:text>                        }
       
  9399 </xsl:text>
       
  9400           <xsl:text>
       
  9401 </xsl:text>
       
  9402           <xsl:text>                        let options = {
       
  9403 </xsl:text>
       
  9404           <xsl:text>                            dateStyle: datestyle,
       
  9405 </xsl:text>
       
  9406           <xsl:text>                            timeStyle: timestyle,
       
  9407 </xsl:text>
       
  9408           <xsl:text>                            hour12: false
       
  9409 </xsl:text>
       
  9410           <xsl:text>                        }
       
  9411 </xsl:text>
       
  9412           <xsl:text>
       
  9413 </xsl:text>
       
  9414           <xsl:text>                        /* get lang from globals */
       
  9415 </xsl:text>
       
  9416           <xsl:text>                        let lang = get_current_lang_code();
       
  9417 </xsl:text>
       
  9418           <xsl:text>                        let f;
       
  9419 </xsl:text>
       
  9420           <xsl:text>                        try{
       
  9421 </xsl:text>
       
  9422           <xsl:text>                            f = new Intl.DateTimeFormat(lang, options);
       
  9423 </xsl:text>
       
  9424           <xsl:text>                        } catch(e) {
       
  9425 </xsl:text>
       
  9426           <xsl:text>                            f = new Intl.DateTimeFormat('en-US', options);
       
  9427 </xsl:text>
       
  9428           <xsl:text>                        }
       
  9429 </xsl:text>
       
  9430           <xsl:text>                        arg = f.format(arg);
       
  9431 </xsl:text>
       
  9432           <xsl:text>
       
  9433 </xsl:text>
       
  9434           <xsl:text>                        /*    
       
  9435 </xsl:text>
       
  9436           <xsl:text>                            TODO: select with padding char
       
  9437 </xsl:text>
       
  9438           <xsl:text>                                  a: absolute time and date (default)
       
  9439 </xsl:text>
       
  9440           <xsl:text>                                  r: relative time
       
  9441 </xsl:text>
       
  9442           <xsl:text>                        */
       
  9443 </xsl:text>
       
  9444           <xsl:text>
       
  9445 </xsl:text>
       
  9446           <xsl:text>                        break
       
  9447 </xsl:text>
  7880           <xsl:text>                    case 'j':
  9448           <xsl:text>                    case 'j':
  7881 </xsl:text>
  9449 </xsl:text>
  7882           <xsl:text>                        arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0)
  9450           <xsl:text>                        arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0)
  7883 </xsl:text>
  9451 </xsl:text>
  7884           <xsl:text>                        break
  9452           <xsl:text>                        break
  8179 </xsl:text>
  9747 </xsl:text>
  8180           <xsl:text>    /* eslint-enable quote-props */
  9748           <xsl:text>    /* eslint-enable quote-props */
  8181 </xsl:text>
  9749 </xsl:text>
  8182           <xsl:text>}(); // eslint-disable-line    
  9750           <xsl:text>}(); // eslint-disable-line    
  8183 </xsl:text>
  9751 </xsl:text>
       
  9752           <xsl:text>/*
       
  9753 </xsl:text>
       
  9754           <xsl:text>
       
  9755 </xsl:text>
       
  9756           <xsl:text>From https://github.com/keyvan-m-sadeghi/pythonic
       
  9757 </xsl:text>
       
  9758           <xsl:text>
       
  9759 </xsl:text>
       
  9760           <xsl:text>Slightly modified in order to be usable in browser (i.e. not as a node.js module)
       
  9761 </xsl:text>
       
  9762           <xsl:text>
       
  9763 </xsl:text>
       
  9764           <xsl:text>The MIT License (MIT)
       
  9765 </xsl:text>
       
  9766           <xsl:text>
       
  9767 </xsl:text>
       
  9768           <xsl:text>Copyright (c) 2016 Assister.Ai
       
  9769 </xsl:text>
       
  9770           <xsl:text>
       
  9771 </xsl:text>
       
  9772           <xsl:text>Permission is hereby granted, free of charge, to any person obtaining a copy of
       
  9773 </xsl:text>
       
  9774           <xsl:text>this software and associated documentation files (the "Software"), to deal in
       
  9775 </xsl:text>
       
  9776           <xsl:text>the Software without restriction, including without limitation the rights to
       
  9777 </xsl:text>
       
  9778           <xsl:text>use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
       
  9779 </xsl:text>
       
  9780           <xsl:text>the Software, and to permit persons to whom the Software is furnished to do so,
       
  9781 </xsl:text>
       
  9782           <xsl:text>subject to the following conditions:
       
  9783 </xsl:text>
       
  9784           <xsl:text>
       
  9785 </xsl:text>
       
  9786           <xsl:text>The above copyright notice and this permission notice shall be included in all
       
  9787 </xsl:text>
       
  9788           <xsl:text>copies or substantial portions of the Software.
       
  9789 </xsl:text>
       
  9790           <xsl:text>
       
  9791 </xsl:text>
       
  9792           <xsl:text>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
  9793 </xsl:text>
       
  9794           <xsl:text>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
       
  9795 </xsl:text>
       
  9796           <xsl:text>FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
       
  9797 </xsl:text>
       
  9798           <xsl:text>COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
       
  9799 </xsl:text>
       
  9800           <xsl:text>IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       
  9801 </xsl:text>
       
  9802           <xsl:text>CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       
  9803 </xsl:text>
       
  9804           <xsl:text>*/
       
  9805 </xsl:text>
       
  9806           <xsl:text>
       
  9807 </xsl:text>
       
  9808           <xsl:text>class Iterator {
       
  9809 </xsl:text>
       
  9810           <xsl:text>    constructor(generator) {
       
  9811 </xsl:text>
       
  9812           <xsl:text>        this[Symbol.iterator] = generator;
       
  9813 </xsl:text>
       
  9814           <xsl:text>    }
       
  9815 </xsl:text>
       
  9816           <xsl:text>
       
  9817 </xsl:text>
       
  9818           <xsl:text>    async * [Symbol.asyncIterator]() {
       
  9819 </xsl:text>
       
  9820           <xsl:text>        for (const element of this) {
       
  9821 </xsl:text>
       
  9822           <xsl:text>            yield await element;
       
  9823 </xsl:text>
       
  9824           <xsl:text>        }
       
  9825 </xsl:text>
       
  9826           <xsl:text>    }
       
  9827 </xsl:text>
       
  9828           <xsl:text>
       
  9829 </xsl:text>
       
  9830           <xsl:text>    forEach(callback) {
       
  9831 </xsl:text>
       
  9832           <xsl:text>        for (const element of this) {
       
  9833 </xsl:text>
       
  9834           <xsl:text>            callback(element);
       
  9835 </xsl:text>
       
  9836           <xsl:text>        }
       
  9837 </xsl:text>
       
  9838           <xsl:text>    }
       
  9839 </xsl:text>
       
  9840           <xsl:text>
       
  9841 </xsl:text>
       
  9842           <xsl:text>    map(callback) {
       
  9843 </xsl:text>
       
  9844           <xsl:text>        const result = [];
       
  9845 </xsl:text>
       
  9846           <xsl:text>        for (const element of this) {
       
  9847 </xsl:text>
       
  9848           <xsl:text>            result.push(callback(element));
       
  9849 </xsl:text>
       
  9850           <xsl:text>        }
       
  9851 </xsl:text>
       
  9852           <xsl:text>
       
  9853 </xsl:text>
       
  9854           <xsl:text>        return result;
       
  9855 </xsl:text>
       
  9856           <xsl:text>    }
       
  9857 </xsl:text>
       
  9858           <xsl:text>
       
  9859 </xsl:text>
       
  9860           <xsl:text>    filter(callback) {
       
  9861 </xsl:text>
       
  9862           <xsl:text>        const result = [];
       
  9863 </xsl:text>
       
  9864           <xsl:text>        for (const element of this) {
       
  9865 </xsl:text>
       
  9866           <xsl:text>            if (callback(element)) {
       
  9867 </xsl:text>
       
  9868           <xsl:text>                result.push(element);
       
  9869 </xsl:text>
       
  9870           <xsl:text>            }
       
  9871 </xsl:text>
       
  9872           <xsl:text>        }
       
  9873 </xsl:text>
       
  9874           <xsl:text>
       
  9875 </xsl:text>
       
  9876           <xsl:text>        return result;
       
  9877 </xsl:text>
       
  9878           <xsl:text>    }
       
  9879 </xsl:text>
       
  9880           <xsl:text>
       
  9881 </xsl:text>
       
  9882           <xsl:text>    reduce(callback, initialValue) {
       
  9883 </xsl:text>
       
  9884           <xsl:text>        let empty = typeof initialValue === 'undefined';
       
  9885 </xsl:text>
       
  9886           <xsl:text>        let accumulator = initialValue;
       
  9887 </xsl:text>
       
  9888           <xsl:text>        let index = 0;
       
  9889 </xsl:text>
       
  9890           <xsl:text>        for (const currentValue of this) {
       
  9891 </xsl:text>
       
  9892           <xsl:text>            if (empty) {
       
  9893 </xsl:text>
       
  9894           <xsl:text>                accumulator = currentValue;
       
  9895 </xsl:text>
       
  9896           <xsl:text>                empty = false;
       
  9897 </xsl:text>
       
  9898           <xsl:text>                continue;
       
  9899 </xsl:text>
       
  9900           <xsl:text>            }
       
  9901 </xsl:text>
       
  9902           <xsl:text>
       
  9903 </xsl:text>
       
  9904           <xsl:text>            accumulator = callback(accumulator, currentValue, index, this);
       
  9905 </xsl:text>
       
  9906           <xsl:text>            index++;
       
  9907 </xsl:text>
       
  9908           <xsl:text>        }
       
  9909 </xsl:text>
       
  9910           <xsl:text>
       
  9911 </xsl:text>
       
  9912           <xsl:text>        if (empty) {
       
  9913 </xsl:text>
       
  9914           <xsl:text>            throw new TypeError('Reduce of empty Iterator with no initial value');
       
  9915 </xsl:text>
       
  9916           <xsl:text>        }
       
  9917 </xsl:text>
       
  9918           <xsl:text>
       
  9919 </xsl:text>
       
  9920           <xsl:text>        return accumulator;
       
  9921 </xsl:text>
       
  9922           <xsl:text>    }
       
  9923 </xsl:text>
       
  9924           <xsl:text>
       
  9925 </xsl:text>
       
  9926           <xsl:text>    some(callback) {
       
  9927 </xsl:text>
       
  9928           <xsl:text>        for (const element of this) {
       
  9929 </xsl:text>
       
  9930           <xsl:text>            if (callback(element)) {
       
  9931 </xsl:text>
       
  9932           <xsl:text>                return true;
       
  9933 </xsl:text>
       
  9934           <xsl:text>            }
       
  9935 </xsl:text>
       
  9936           <xsl:text>        }
       
  9937 </xsl:text>
       
  9938           <xsl:text>
       
  9939 </xsl:text>
       
  9940           <xsl:text>        return false;
       
  9941 </xsl:text>
       
  9942           <xsl:text>    }
       
  9943 </xsl:text>
       
  9944           <xsl:text>
       
  9945 </xsl:text>
       
  9946           <xsl:text>    every(callback) {
       
  9947 </xsl:text>
       
  9948           <xsl:text>        for (const element of this) {
       
  9949 </xsl:text>
       
  9950           <xsl:text>            if (!callback(element)) {
       
  9951 </xsl:text>
       
  9952           <xsl:text>                return false;
       
  9953 </xsl:text>
       
  9954           <xsl:text>            }
       
  9955 </xsl:text>
       
  9956           <xsl:text>        }
       
  9957 </xsl:text>
       
  9958           <xsl:text>
       
  9959 </xsl:text>
       
  9960           <xsl:text>        return true;
       
  9961 </xsl:text>
       
  9962           <xsl:text>    }
       
  9963 </xsl:text>
       
  9964           <xsl:text>
       
  9965 </xsl:text>
       
  9966           <xsl:text>    static fromIterable(iterable) {
       
  9967 </xsl:text>
       
  9968           <xsl:text>        return new Iterator(function * () {
       
  9969 </xsl:text>
       
  9970           <xsl:text>            for (const element of iterable) {
       
  9971 </xsl:text>
       
  9972           <xsl:text>                yield element;
       
  9973 </xsl:text>
       
  9974           <xsl:text>            }
       
  9975 </xsl:text>
       
  9976           <xsl:text>        });
       
  9977 </xsl:text>
       
  9978           <xsl:text>    }
       
  9979 </xsl:text>
       
  9980           <xsl:text>
       
  9981 </xsl:text>
       
  9982           <xsl:text>    toArray() {
       
  9983 </xsl:text>
       
  9984           <xsl:text>        return Array.from(this);
       
  9985 </xsl:text>
       
  9986           <xsl:text>    }
       
  9987 </xsl:text>
       
  9988           <xsl:text>
       
  9989 </xsl:text>
       
  9990           <xsl:text>    next() {
       
  9991 </xsl:text>
       
  9992           <xsl:text>        if (!this.currentInvokedGenerator) {
       
  9993 </xsl:text>
       
  9994           <xsl:text>            this.currentInvokedGenerator = this[Symbol.iterator]();
       
  9995 </xsl:text>
       
  9996           <xsl:text>        }
       
  9997 </xsl:text>
       
  9998           <xsl:text>
       
  9999 </xsl:text>
       
 10000           <xsl:text>        return this.currentInvokedGenerator.next();
       
 10001 </xsl:text>
       
 10002           <xsl:text>    }
       
 10003 </xsl:text>
       
 10004           <xsl:text>
       
 10005 </xsl:text>
       
 10006           <xsl:text>    reset() {
       
 10007 </xsl:text>
       
 10008           <xsl:text>        delete this.currentInvokedGenerator;
       
 10009 </xsl:text>
       
 10010           <xsl:text>    }
       
 10011 </xsl:text>
       
 10012           <xsl:text>}
       
 10013 </xsl:text>
       
 10014           <xsl:text>
       
 10015 </xsl:text>
       
 10016           <xsl:text>function rangeSimple(stop) {
       
 10017 </xsl:text>
       
 10018           <xsl:text>    return new Iterator(function * () {
       
 10019 </xsl:text>
       
 10020           <xsl:text>        for (let i = 0; i &lt; stop; i++) {
       
 10021 </xsl:text>
       
 10022           <xsl:text>            yield i;
       
 10023 </xsl:text>
       
 10024           <xsl:text>        }
       
 10025 </xsl:text>
       
 10026           <xsl:text>    });
       
 10027 </xsl:text>
       
 10028           <xsl:text>}
       
 10029 </xsl:text>
       
 10030           <xsl:text>
       
 10031 </xsl:text>
       
 10032           <xsl:text>function rangeOverload(start, stop, step = 1) {
       
 10033 </xsl:text>
       
 10034           <xsl:text>    return new Iterator(function * () {
       
 10035 </xsl:text>
       
 10036           <xsl:text>        for (let i = start; i &lt; stop; i += step) {
       
 10037 </xsl:text>
       
 10038           <xsl:text>            yield i;
       
 10039 </xsl:text>
       
 10040           <xsl:text>        }
       
 10041 </xsl:text>
       
 10042           <xsl:text>    });
       
 10043 </xsl:text>
       
 10044           <xsl:text>}
       
 10045 </xsl:text>
       
 10046           <xsl:text>
       
 10047 </xsl:text>
       
 10048           <xsl:text>function range(...args) {
       
 10049 </xsl:text>
       
 10050           <xsl:text>    if (args.length &lt; 2) {
       
 10051 </xsl:text>
       
 10052           <xsl:text>        return rangeSimple(...args);
       
 10053 </xsl:text>
       
 10054           <xsl:text>    }
       
 10055 </xsl:text>
       
 10056           <xsl:text>
       
 10057 </xsl:text>
       
 10058           <xsl:text>    return rangeOverload(...args);
       
 10059 </xsl:text>
       
 10060           <xsl:text>}
       
 10061 </xsl:text>
       
 10062           <xsl:text>
       
 10063 </xsl:text>
       
 10064           <xsl:text>function enumerate(iterable) {
       
 10065 </xsl:text>
       
 10066           <xsl:text>    return new Iterator(function * () {
       
 10067 </xsl:text>
       
 10068           <xsl:text>        let index = 0;
       
 10069 </xsl:text>
       
 10070           <xsl:text>        for (const element of iterable) {
       
 10071 </xsl:text>
       
 10072           <xsl:text>            yield [index, element];
       
 10073 </xsl:text>
       
 10074           <xsl:text>            index++;
       
 10075 </xsl:text>
       
 10076           <xsl:text>        }
       
 10077 </xsl:text>
       
 10078           <xsl:text>    });
       
 10079 </xsl:text>
       
 10080           <xsl:text>}
       
 10081 </xsl:text>
       
 10082           <xsl:text>
       
 10083 </xsl:text>
       
 10084           <xsl:text>const _zip = longest =&gt; (...iterables) =&gt; {
       
 10085 </xsl:text>
       
 10086           <xsl:text>    if (iterables.length &lt; 2) {
       
 10087 </xsl:text>
       
 10088           <xsl:text>        throw new TypeError("zip takes 2 iterables at least, "+iterables.length+" given");
       
 10089 </xsl:text>
       
 10090           <xsl:text>    }
       
 10091 </xsl:text>
       
 10092           <xsl:text>
       
 10093 </xsl:text>
       
 10094           <xsl:text>    return new Iterator(function * () {
       
 10095 </xsl:text>
       
 10096           <xsl:text>        const iterators = iterables.map(iterable =&gt; Iterator.fromIterable(iterable));
       
 10097 </xsl:text>
       
 10098           <xsl:text>        while (true) {
       
 10099 </xsl:text>
       
 10100           <xsl:text>            const row = iterators.map(iterator =&gt; iterator.next());
       
 10101 </xsl:text>
       
 10102           <xsl:text>            const check = longest ? row.every.bind(row) : row.some.bind(row);
       
 10103 </xsl:text>
       
 10104           <xsl:text>            if (check(next =&gt; next.done)) {
       
 10105 </xsl:text>
       
 10106           <xsl:text>                return;
       
 10107 </xsl:text>
       
 10108           <xsl:text>            }
       
 10109 </xsl:text>
       
 10110           <xsl:text>
       
 10111 </xsl:text>
       
 10112           <xsl:text>            yield row.map(next =&gt; next.value);
       
 10113 </xsl:text>
       
 10114           <xsl:text>        }
       
 10115 </xsl:text>
       
 10116           <xsl:text>    });
       
 10117 </xsl:text>
       
 10118           <xsl:text>};
       
 10119 </xsl:text>
       
 10120           <xsl:text>
       
 10121 </xsl:text>
       
 10122           <xsl:text>const zip = _zip(false), zipLongest= _zip(true);
       
 10123 </xsl:text>
       
 10124           <xsl:text>
       
 10125 </xsl:text>
       
 10126           <xsl:text>function items(obj) {
       
 10127 </xsl:text>
       
 10128           <xsl:text>    let {keys, get} = obj;
       
 10129 </xsl:text>
       
 10130           <xsl:text>    if (obj instanceof Map) {
       
 10131 </xsl:text>
       
 10132           <xsl:text>        keys = keys.bind(obj);
       
 10133 </xsl:text>
       
 10134           <xsl:text>        get = get.bind(obj);
       
 10135 </xsl:text>
       
 10136           <xsl:text>    } else {
       
 10137 </xsl:text>
       
 10138           <xsl:text>        keys = function () {
       
 10139 </xsl:text>
       
 10140           <xsl:text>            return Object.keys(obj);
       
 10141 </xsl:text>
       
 10142           <xsl:text>        };
       
 10143 </xsl:text>
       
 10144           <xsl:text>
       
 10145 </xsl:text>
       
 10146           <xsl:text>        get = function (key) {
       
 10147 </xsl:text>
       
 10148           <xsl:text>            return obj[key];
       
 10149 </xsl:text>
       
 10150           <xsl:text>        };
       
 10151 </xsl:text>
       
 10152           <xsl:text>    }
       
 10153 </xsl:text>
       
 10154           <xsl:text>
       
 10155 </xsl:text>
       
 10156           <xsl:text>    return new Iterator(function * () {
       
 10157 </xsl:text>
       
 10158           <xsl:text>        for (const key of keys()) {
       
 10159 </xsl:text>
       
 10160           <xsl:text>            yield [key, get(key)];
       
 10161 </xsl:text>
       
 10162           <xsl:text>        }
       
 10163 </xsl:text>
       
 10164           <xsl:text>    });
       
 10165 </xsl:text>
       
 10166           <xsl:text>}
       
 10167 </xsl:text>
       
 10168           <xsl:text>
       
 10169 </xsl:text>
       
 10170           <xsl:text>/*
       
 10171 </xsl:text>
       
 10172           <xsl:text>module.exports = {Iterator, range, enumerate, zip: _zip(false), zipLongest: _zip(true), items};
       
 10173 </xsl:text>
       
 10174           <xsl:text>*/
       
 10175 </xsl:text>
  8184           <xsl:text>// svghmi.js
 10176           <xsl:text>// svghmi.js
  8185 </xsl:text>
 10177 </xsl:text>
  8186           <xsl:text>
 10178           <xsl:text>
  8187 </xsl:text>
 10179 </xsl:text>
  8188           <xsl:text>var need_cache_apply = [];
 10180           <xsl:text>var need_cache_apply = [];
  8219 </xsl:text>
 10211 </xsl:text>
  8220           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
 10212           <xsl:text>    Object.keys(hmi_widgets).forEach(function(id) {
  8221 </xsl:text>
 10213 </xsl:text>
  8222           <xsl:text>        let widget = hmi_widgets[id];
 10214           <xsl:text>        let widget = hmi_widgets[id];
  8223 </xsl:text>
 10215 </xsl:text>
  8224           <xsl:text>        let init = widget.init;
 10216           <xsl:text>        widget.do_init();
  8225 </xsl:text>
 10217 </xsl:text>
  8226           <xsl:text>        if(typeof(init) == "function"){
 10218           <xsl:text>    });
  8227 </xsl:text>
 10219 </xsl:text>
  8228           <xsl:text>            try {
 10220           <xsl:text>};
  8229 </xsl:text>
 10221 </xsl:text>
  8230           <xsl:text>                init.call(widget);
 10222           <xsl:text>
  8231 </xsl:text>
 10223 </xsl:text>
  8232           <xsl:text>            } catch(err) {
 10224           <xsl:text>// Open WebSocket to relative "/ws" address
  8233 </xsl:text>
 10225 </xsl:text>
  8234           <xsl:text>                console.log(err);
 10226           <xsl:text>var has_watchdog = window.location.hash == "#watchdog";
       
 10227 </xsl:text>
       
 10228           <xsl:text>
       
 10229 </xsl:text>
       
 10230           <xsl:text>var ws_url = 
       
 10231 </xsl:text>
       
 10232           <xsl:text>    window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')
       
 10233 </xsl:text>
       
 10234           <xsl:text>    + '?mode=' + (has_watchdog ? "watchdog" : "multiclient");
       
 10235 </xsl:text>
       
 10236           <xsl:text>
       
 10237 </xsl:text>
       
 10238           <xsl:text>var ws = new WebSocket(ws_url);
       
 10239 </xsl:text>
       
 10240           <xsl:text>ws.binaryType = 'arraybuffer';
       
 10241 </xsl:text>
       
 10242           <xsl:text>
       
 10243 </xsl:text>
       
 10244           <xsl:text>const dvgetters = {
       
 10245 </xsl:text>
       
 10246           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
 10247 </xsl:text>
       
 10248           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
 10249 </xsl:text>
       
 10250           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
 10251 </xsl:text>
       
 10252           <xsl:text>    REAL: (dv,offset) =&gt; [dv.getFloat32(offset, true), 4],
       
 10253 </xsl:text>
       
 10254           <xsl:text>    STRING: (dv, offset) =&gt; {
       
 10255 </xsl:text>
       
 10256           <xsl:text>        const size = dv.getInt8(offset);
       
 10257 </xsl:text>
       
 10258           <xsl:text>        return [
       
 10259 </xsl:text>
       
 10260           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
 10261 </xsl:text>
       
 10262           <xsl:text>                dv.buffer, /* original buffer */
       
 10263 </xsl:text>
       
 10264           <xsl:text>                offset + 1, /* string starts after size*/
       
 10265 </xsl:text>
       
 10266           <xsl:text>                size /* size of string */
       
 10267 </xsl:text>
       
 10268           <xsl:text>            )), size + 1]; /* total increment */
       
 10269 </xsl:text>
       
 10270           <xsl:text>    }
       
 10271 </xsl:text>
       
 10272           <xsl:text>};
       
 10273 </xsl:text>
       
 10274           <xsl:text>
       
 10275 </xsl:text>
       
 10276           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
       
 10277 </xsl:text>
       
 10278           <xsl:text>function apply_updates() {
       
 10279 </xsl:text>
       
 10280           <xsl:text>    updates.forEach((value, index) =&gt; {
       
 10281 </xsl:text>
       
 10282           <xsl:text>        dispatch_value(index, value);
       
 10283 </xsl:text>
       
 10284           <xsl:text>    });
       
 10285 </xsl:text>
       
 10286           <xsl:text>    updates.clear();
       
 10287 </xsl:text>
       
 10288           <xsl:text>}
       
 10289 </xsl:text>
       
 10290           <xsl:text>
       
 10291 </xsl:text>
       
 10292           <xsl:text>// Called on requestAnimationFrame, modifies DOM
       
 10293 </xsl:text>
       
 10294           <xsl:text>var requestAnimationFrameID = null;
       
 10295 </xsl:text>
       
 10296           <xsl:text>function animate() {
       
 10297 </xsl:text>
       
 10298           <xsl:text>    // Do the page swith if any one pending
       
 10299 </xsl:text>
       
 10300           <xsl:text>    if(current_subscribed_page != current_visible_page){
       
 10301 </xsl:text>
       
 10302           <xsl:text>        switch_visible_page(current_subscribed_page);
       
 10303 </xsl:text>
       
 10304           <xsl:text>    }
       
 10305 </xsl:text>
       
 10306           <xsl:text>
       
 10307 </xsl:text>
       
 10308           <xsl:text>    while(widget = need_cache_apply.pop()){
       
 10309 </xsl:text>
       
 10310           <xsl:text>        widget.apply_cache();
       
 10311 </xsl:text>
       
 10312           <xsl:text>    }
       
 10313 </xsl:text>
       
 10314           <xsl:text>
       
 10315 </xsl:text>
       
 10316           <xsl:text>    if(jumps_need_update) update_jumps();
       
 10317 </xsl:text>
       
 10318           <xsl:text>
       
 10319 </xsl:text>
       
 10320           <xsl:text>    apply_updates();
       
 10321 </xsl:text>
       
 10322           <xsl:text>
       
 10323 </xsl:text>
       
 10324           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
       
 10325 </xsl:text>
       
 10326           <xsl:text>    pending_widget_animates = [];
       
 10327 </xsl:text>
       
 10328           <xsl:text>
       
 10329 </xsl:text>
       
 10330           <xsl:text>    requestAnimationFrameID = null;
       
 10331 </xsl:text>
       
 10332           <xsl:text>}
       
 10333 </xsl:text>
       
 10334           <xsl:text>
       
 10335 </xsl:text>
       
 10336           <xsl:text>function requestHMIAnimation() {
       
 10337 </xsl:text>
       
 10338           <xsl:text>    if(requestAnimationFrameID == null){
       
 10339 </xsl:text>
       
 10340           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
 10341 </xsl:text>
       
 10342           <xsl:text>    }
       
 10343 </xsl:text>
       
 10344           <xsl:text>}
       
 10345 </xsl:text>
       
 10346           <xsl:text>
       
 10347 </xsl:text>
       
 10348           <xsl:text>// Message reception handler
       
 10349 </xsl:text>
       
 10350           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
 10351 </xsl:text>
       
 10352           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
 10353 </xsl:text>
       
 10354           <xsl:text>ws.onmessage = function (evt) {
       
 10355 </xsl:text>
       
 10356           <xsl:text>
       
 10357 </xsl:text>
       
 10358           <xsl:text>    let data = evt.data;
       
 10359 </xsl:text>
       
 10360           <xsl:text>    let dv = new DataView(data);
       
 10361 </xsl:text>
       
 10362           <xsl:text>    let i = 0;
       
 10363 </xsl:text>
       
 10364           <xsl:text>    try {
       
 10365 </xsl:text>
       
 10366           <xsl:text>        for(let hash_int of hmi_hash) {
       
 10367 </xsl:text>
       
 10368           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
 10369 </xsl:text>
       
 10370           <xsl:text>                throw new Error("Hash doesn't match");
       
 10371 </xsl:text>
       
 10372           <xsl:text>            };
       
 10373 </xsl:text>
       
 10374           <xsl:text>            i++;
       
 10375 </xsl:text>
       
 10376           <xsl:text>        };
       
 10377 </xsl:text>
       
 10378           <xsl:text>
       
 10379 </xsl:text>
       
 10380           <xsl:text>        while(i &lt; data.byteLength){
       
 10381 </xsl:text>
       
 10382           <xsl:text>            let index = dv.getUint32(i, true);
       
 10383 </xsl:text>
       
 10384           <xsl:text>            i += 4;
       
 10385 </xsl:text>
       
 10386           <xsl:text>            let iectype = hmitree_types[index];
       
 10387 </xsl:text>
       
 10388           <xsl:text>            if(iectype != undefined){
       
 10389 </xsl:text>
       
 10390           <xsl:text>                let dvgetter = dvgetters[iectype];
       
 10391 </xsl:text>
       
 10392           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
 10393 </xsl:text>
       
 10394           <xsl:text>                updates.set(index, value);
       
 10395 </xsl:text>
       
 10396           <xsl:text>                i += bytesize;
       
 10397 </xsl:text>
       
 10398           <xsl:text>            } else {
       
 10399 </xsl:text>
       
 10400           <xsl:text>                throw new Error("Unknown index "+index);
  8235 </xsl:text>
 10401 </xsl:text>
  8236           <xsl:text>            }
 10402           <xsl:text>            }
  8237 </xsl:text>
 10403 </xsl:text>
       
 10404           <xsl:text>        };
       
 10405 </xsl:text>
       
 10406           <xsl:text>        // register for rendering on next frame, since there are updates
       
 10407 </xsl:text>
       
 10408           <xsl:text>        requestHMIAnimation();
       
 10409 </xsl:text>
       
 10410           <xsl:text>    } catch(err) {
       
 10411 </xsl:text>
       
 10412           <xsl:text>        // 1003 is for "Unsupported Data"
       
 10413 </xsl:text>
       
 10414           <xsl:text>        // ws.close(1003, err.message);
       
 10415 </xsl:text>
       
 10416           <xsl:text>
       
 10417 </xsl:text>
       
 10418           <xsl:text>        // TODO : remove debug alert ?
       
 10419 </xsl:text>
       
 10420           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
 10421 </xsl:text>
       
 10422           <xsl:text>
       
 10423 </xsl:text>
       
 10424           <xsl:text>        // force reload ignoring cache
       
 10425 </xsl:text>
       
 10426           <xsl:text>        location.reload(true);
       
 10427 </xsl:text>
       
 10428           <xsl:text>    }
       
 10429 </xsl:text>
       
 10430           <xsl:text>};
       
 10431 </xsl:text>
       
 10432           <xsl:text>
       
 10433 </xsl:text>
       
 10434           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
       
 10435 </xsl:text>
       
 10436           <xsl:text>
       
 10437 </xsl:text>
       
 10438           <xsl:text>function send_blob(data) {
       
 10439 </xsl:text>
       
 10440           <xsl:text>    if(data.length &gt; 0) {
       
 10441 </xsl:text>
       
 10442           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
       
 10443 </xsl:text>
       
 10444           <xsl:text>    };
       
 10445 </xsl:text>
       
 10446           <xsl:text>};
       
 10447 </xsl:text>
       
 10448           <xsl:text>
       
 10449 </xsl:text>
       
 10450           <xsl:text>const typedarray_types = {
       
 10451 </xsl:text>
       
 10452           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
 10453 </xsl:text>
       
 10454           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
 10455 </xsl:text>
       
 10456           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
 10457 </xsl:text>
       
 10458           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
 10459 </xsl:text>
       
 10460           <xsl:text>    STRING: (str) =&gt; {
       
 10461 </xsl:text>
       
 10462           <xsl:text>        // beremiz default string max size is 128
       
 10463 </xsl:text>
       
 10464           <xsl:text>        str = str.slice(0,128);
       
 10465 </xsl:text>
       
 10466           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
 10467 </xsl:text>
       
 10468           <xsl:text>        binary[0] = str.length;
       
 10469 </xsl:text>
       
 10470           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
 10471 </xsl:text>
       
 10472           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
 10473 </xsl:text>
  8238           <xsl:text>        }
 10474           <xsl:text>        }
  8239 </xsl:text>
 10475 </xsl:text>
  8240           <xsl:text>        if(widget.forced_frequency !== undefined)
 10476           <xsl:text>        return binary;
  8241 </xsl:text>
 10477 </xsl:text>
  8242           <xsl:text>            widget.frequency = widget.forced_frequency;
 10478           <xsl:text>    }
       
 10479 </xsl:text>
       
 10480           <xsl:text>    /* TODO */
       
 10481 </xsl:text>
       
 10482           <xsl:text>};
       
 10483 </xsl:text>
       
 10484           <xsl:text>
       
 10485 </xsl:text>
       
 10486           <xsl:text>function send_reset() {
       
 10487 </xsl:text>
       
 10488           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
 10489 </xsl:text>
       
 10490           <xsl:text>};
       
 10491 </xsl:text>
       
 10492           <xsl:text>
       
 10493 </xsl:text>
       
 10494           <xsl:text>var subscriptions = [];
       
 10495 </xsl:text>
       
 10496           <xsl:text>
       
 10497 </xsl:text>
       
 10498           <xsl:text>function subscribers(index) {
       
 10499 </xsl:text>
       
 10500           <xsl:text>    let entry = subscriptions[index];
       
 10501 </xsl:text>
       
 10502           <xsl:text>    let res;
       
 10503 </xsl:text>
       
 10504           <xsl:text>    if(entry == undefined){
       
 10505 </xsl:text>
       
 10506           <xsl:text>        res = new Set();
       
 10507 </xsl:text>
       
 10508           <xsl:text>        subscriptions[index] = [res,0];
       
 10509 </xsl:text>
       
 10510           <xsl:text>    }else{
       
 10511 </xsl:text>
       
 10512           <xsl:text>        [res, _ign] = entry;
       
 10513 </xsl:text>
       
 10514           <xsl:text>    }
       
 10515 </xsl:text>
       
 10516           <xsl:text>    return res
       
 10517 </xsl:text>
       
 10518           <xsl:text>}
       
 10519 </xsl:text>
       
 10520           <xsl:text>
       
 10521 </xsl:text>
       
 10522           <xsl:text>function get_subscription_period(index) {
       
 10523 </xsl:text>
       
 10524           <xsl:text>    let entry = subscriptions[index];
       
 10525 </xsl:text>
       
 10526           <xsl:text>    if(entry == undefined)
       
 10527 </xsl:text>
       
 10528           <xsl:text>        return 0;
       
 10529 </xsl:text>
       
 10530           <xsl:text>    let [_ign, period] = entry;
       
 10531 </xsl:text>
       
 10532           <xsl:text>    return period;
       
 10533 </xsl:text>
       
 10534           <xsl:text>}
       
 10535 </xsl:text>
       
 10536           <xsl:text>
       
 10537 </xsl:text>
       
 10538           <xsl:text>function set_subscription_period(index, period) {
       
 10539 </xsl:text>
       
 10540           <xsl:text>    let entry = subscriptions[index];
       
 10541 </xsl:text>
       
 10542           <xsl:text>    if(entry == undefined){
       
 10543 </xsl:text>
       
 10544           <xsl:text>        subscriptions[index] = [new Set(), period];
       
 10545 </xsl:text>
       
 10546           <xsl:text>    } else {
       
 10547 </xsl:text>
       
 10548           <xsl:text>        entry[1] = period;
       
 10549 </xsl:text>
       
 10550           <xsl:text>    }
       
 10551 </xsl:text>
       
 10552           <xsl:text>}
       
 10553 </xsl:text>
       
 10554           <xsl:text>
       
 10555 </xsl:text>
       
 10556           <xsl:text>if(has_watchdog){
       
 10557 </xsl:text>
       
 10558           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
 10559 </xsl:text>
       
 10560           <xsl:text>    // Since dispatch directly calls change_hmi_value,
       
 10561 </xsl:text>
       
 10562           <xsl:text>    // PLC will periodically send variable at given frequency
       
 10563 </xsl:text>
       
 10564           <xsl:text>    subscribers(heartbeat_index).add({
       
 10565 </xsl:text>
       
 10566           <xsl:text>        /* type: "Watchdog", */
       
 10567 </xsl:text>
       
 10568           <xsl:text>        frequency: 1,
       
 10569 </xsl:text>
       
 10570           <xsl:text>        indexes: [heartbeat_index],
       
 10571 </xsl:text>
       
 10572           <xsl:text>        new_hmi_value: function(index, value, oldval) {
       
 10573 </xsl:text>
       
 10574           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
       
 10575 </xsl:text>
       
 10576           <xsl:text>        }
  8243 </xsl:text>
 10577 </xsl:text>
  8244           <xsl:text>    });
 10578           <xsl:text>    });
  8245 </xsl:text>
 10579 </xsl:text>
  8246           <xsl:text>};
 10580           <xsl:text>}
  8247 </xsl:text>
 10581 </xsl:text>
  8248           <xsl:text>
 10582           <xsl:text>
  8249 </xsl:text>
 10583 </xsl:text>
  8250           <xsl:text>// Open WebSocket to relative "/ws" address
 10584           <xsl:text>// subscribe to per instance current page hmi variable
  8251 </xsl:text>
 10585 </xsl:text>
  8252           <xsl:text>var has_watchdog = window.location.hash == "#watchdog";
 10586           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
  8253 </xsl:text>
 10587 </xsl:text>
  8254           <xsl:text>
 10588           <xsl:text>subscribers(current_page_var_index).add({
  8255 </xsl:text>
 10589 </xsl:text>
  8256           <xsl:text>var ws_url = 
 10590           <xsl:text>    frequency: 1,
  8257 </xsl:text>
 10591 </xsl:text>
  8258           <xsl:text>    window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')
 10592           <xsl:text>    indexes: [current_page_var_index],
  8259 </xsl:text>
 10593 </xsl:text>
  8260           <xsl:text>    + '?mode=' + (has_watchdog ? "watchdog" : "multiclient");
 10594           <xsl:text>    new_hmi_value: function(index, value, oldval) {
  8261 </xsl:text>
 10595 </xsl:text>
  8262           <xsl:text>
 10596           <xsl:text>        if(value.startsWith("!"))
  8263 </xsl:text>
 10597 </xsl:text>
  8264           <xsl:text>var ws = new WebSocket(ws_url);
 10598           <xsl:text>            switch_page(value.slice(1));
  8265 </xsl:text>
       
  8266           <xsl:text>ws.binaryType = 'arraybuffer';
       
  8267 </xsl:text>
       
  8268           <xsl:text>
       
  8269 </xsl:text>
       
  8270           <xsl:text>const dvgetters = {
       
  8271 </xsl:text>
       
  8272           <xsl:text>    INT: (dv,offset) =&gt; [dv.getInt16(offset, true), 2],
       
  8273 </xsl:text>
       
  8274           <xsl:text>    BOOL: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8275 </xsl:text>
       
  8276           <xsl:text>    NODE: (dv,offset) =&gt; [dv.getInt8(offset, true), 1],
       
  8277 </xsl:text>
       
  8278           <xsl:text>    REAL: (dv,offset) =&gt; [dv.getFloat32(offset, true), 4],
       
  8279 </xsl:text>
       
  8280           <xsl:text>    STRING: (dv, offset) =&gt; {
       
  8281 </xsl:text>
       
  8282           <xsl:text>        const size = dv.getInt8(offset);
       
  8283 </xsl:text>
       
  8284           <xsl:text>        return [
       
  8285 </xsl:text>
       
  8286           <xsl:text>            String.fromCharCode.apply(null, new Uint8Array(
       
  8287 </xsl:text>
       
  8288           <xsl:text>                dv.buffer, /* original buffer */
       
  8289 </xsl:text>
       
  8290           <xsl:text>                offset + 1, /* string starts after size*/
       
  8291 </xsl:text>
       
  8292           <xsl:text>                size /* size of string */
       
  8293 </xsl:text>
       
  8294           <xsl:text>            )), size + 1]; /* total increment */
       
  8295 </xsl:text>
 10599 </xsl:text>
  8296           <xsl:text>    }
 10600           <xsl:text>    }
  8297 </xsl:text>
 10601 </xsl:text>
  8298           <xsl:text>};
 10602           <xsl:text>});
  8299 </xsl:text>
 10603 </xsl:text>
  8300           <xsl:text>
 10604           <xsl:text>
  8301 </xsl:text>
 10605 </xsl:text>
  8302           <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets
 10606           <xsl:text>function svg_text_to_multiline(elt) {
  8303 </xsl:text>
 10607 </xsl:text>
  8304           <xsl:text>function apply_updates() {
 10608           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
  8305 </xsl:text>
       
  8306           <xsl:text>    updates.forEach((value, index) =&gt; {
       
  8307 </xsl:text>
       
  8308           <xsl:text>        dispatch_value(index, value);
       
  8309 </xsl:text>
       
  8310           <xsl:text>    });
       
  8311 </xsl:text>
       
  8312           <xsl:text>    updates.clear();
       
  8313 </xsl:text>
 10609 </xsl:text>
  8314           <xsl:text>}
 10610           <xsl:text>}
  8315 </xsl:text>
 10611 </xsl:text>
  8316           <xsl:text>
 10612           <xsl:text>
  8317 </xsl:text>
 10613 </xsl:text>
  8318           <xsl:text>// Called on requestAnimationFrame, modifies DOM
 10614           <xsl:text>function multiline_to_svg_text(elt, str) {
  8319 </xsl:text>
 10615 </xsl:text>
  8320           <xsl:text>var requestAnimationFrameID = null;
 10616           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
  8321 </xsl:text>
 10617 </xsl:text>
  8322           <xsl:text>function animate() {
 10618           <xsl:text>}
  8323 </xsl:text>
 10619 </xsl:text>
  8324           <xsl:text>    // Do the page swith if any one pending
 10620           <xsl:text>
  8325 </xsl:text>
 10621 </xsl:text>
  8326           <xsl:text>    if(current_subscribed_page != current_visible_page){
 10622           <xsl:text>function switch_langnum(langnum) {
  8327 </xsl:text>
 10623 </xsl:text>
  8328           <xsl:text>        switch_visible_page(current_subscribed_page);
 10624           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
 10625 </xsl:text>
       
 10626           <xsl:text>
       
 10627 </xsl:text>
       
 10628           <xsl:text>    for (let translation of translations) {
       
 10629 </xsl:text>
       
 10630           <xsl:text>        let [objs, msgs] = translation;
       
 10631 </xsl:text>
       
 10632           <xsl:text>        let msg = msgs[langnum];
       
 10633 </xsl:text>
       
 10634           <xsl:text>        for (let obj of objs) {
       
 10635 </xsl:text>
       
 10636           <xsl:text>            multiline_to_svg_text(obj, msg);
       
 10637 </xsl:text>
       
 10638           <xsl:text>            obj.setAttribute("lang",langnum);
       
 10639 </xsl:text>
       
 10640           <xsl:text>        }
  8329 </xsl:text>
 10641 </xsl:text>
  8330           <xsl:text>    }
 10642           <xsl:text>    }
  8331 </xsl:text>
 10643 </xsl:text>
  8332           <xsl:text>
 10644           <xsl:text>    return langnum;
  8333 </xsl:text>
 10645 </xsl:text>
  8334           <xsl:text>    while(widget = need_cache_apply.pop()){
 10646           <xsl:text>}
  8335 </xsl:text>
 10647 </xsl:text>
  8336           <xsl:text>        widget.apply_cache();
 10648           <xsl:text>
       
 10649 </xsl:text>
       
 10650           <xsl:text>// backup original texts
       
 10651 </xsl:text>
       
 10652           <xsl:text>for (let translation of translations) {
       
 10653 </xsl:text>
       
 10654           <xsl:text>    let [objs, msgs] = translation;
       
 10655 </xsl:text>
       
 10656           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
 10657 </xsl:text>
       
 10658           <xsl:text>}
       
 10659 </xsl:text>
       
 10660           <xsl:text>
       
 10661 </xsl:text>
       
 10662           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
 10663 </xsl:text>
       
 10664           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
 10665 </xsl:text>
       
 10666           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
 10667 </xsl:text>
       
 10668           <xsl:text>subscribers(lang_local_index).add({
       
 10669 </xsl:text>
       
 10670           <xsl:text>    indexes: [lang_local_index],
       
 10671 </xsl:text>
       
 10672           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
 10673 </xsl:text>
       
 10674           <xsl:text>        let current_lang =  switch_langnum(value);
       
 10675 </xsl:text>
       
 10676           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
 10677 </xsl:text>
       
 10678           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
 10679 </xsl:text>
       
 10680           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
 10681 </xsl:text>
       
 10682           <xsl:text>        switch_page();
  8337 </xsl:text>
 10683 </xsl:text>
  8338           <xsl:text>    }
 10684           <xsl:text>    }
  8339 </xsl:text>
 10685 </xsl:text>
  8340           <xsl:text>
 10686           <xsl:text>});
  8341 </xsl:text>
 10687 </xsl:text>
  8342           <xsl:text>    if(jumps_need_update) update_jumps();
 10688           <xsl:text>
  8343 </xsl:text>
 10689 </xsl:text>
  8344           <xsl:text>
 10690           <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language
  8345 </xsl:text>
 10691 </xsl:text>
  8346           <xsl:text>    apply_updates();
 10692           <xsl:text>function get_current_lang_code(){
  8347 </xsl:text>
 10693 </xsl:text>
  8348           <xsl:text>
 10694           <xsl:text>    return cache[langcode_local_index];
  8349 </xsl:text>
       
  8350           <xsl:text>    pending_widget_animates.forEach(widget =&gt; widget._animate());
       
  8351 </xsl:text>
       
  8352           <xsl:text>    pending_widget_animates = [];
       
  8353 </xsl:text>
       
  8354           <xsl:text>
       
  8355 </xsl:text>
       
  8356           <xsl:text>    requestAnimationFrameID = null;
       
  8357 </xsl:text>
 10695 </xsl:text>
  8358           <xsl:text>}
 10696           <xsl:text>}
  8359 </xsl:text>
       
  8360           <xsl:text>
       
  8361 </xsl:text>
       
  8362           <xsl:text>function requestHMIAnimation() {
       
  8363 </xsl:text>
       
  8364           <xsl:text>    if(requestAnimationFrameID == null){
       
  8365 </xsl:text>
       
  8366           <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
       
  8367 </xsl:text>
       
  8368           <xsl:text>    }
       
  8369 </xsl:text>
       
  8370           <xsl:text>}
       
  8371 </xsl:text>
       
  8372           <xsl:text>
       
  8373 </xsl:text>
       
  8374           <xsl:text>// Message reception handler
       
  8375 </xsl:text>
       
  8376           <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing
       
  8377 </xsl:text>
       
  8378           <xsl:text>// are stored until browser can compute next frame, DOM is left untouched
       
  8379 </xsl:text>
       
  8380           <xsl:text>ws.onmessage = function (evt) {
       
  8381 </xsl:text>
       
  8382           <xsl:text>
       
  8383 </xsl:text>
       
  8384           <xsl:text>    let data = evt.data;
       
  8385 </xsl:text>
       
  8386           <xsl:text>    let dv = new DataView(data);
       
  8387 </xsl:text>
       
  8388           <xsl:text>    let i = 0;
       
  8389 </xsl:text>
       
  8390           <xsl:text>    try {
       
  8391 </xsl:text>
       
  8392           <xsl:text>        for(let hash_int of hmi_hash) {
       
  8393 </xsl:text>
       
  8394           <xsl:text>            if(hash_int != dv.getUint8(i)){
       
  8395 </xsl:text>
       
  8396           <xsl:text>                throw new Error("Hash doesn't match");
       
  8397 </xsl:text>
       
  8398           <xsl:text>            };
       
  8399 </xsl:text>
       
  8400           <xsl:text>            i++;
       
  8401 </xsl:text>
       
  8402           <xsl:text>        };
       
  8403 </xsl:text>
       
  8404           <xsl:text>
       
  8405 </xsl:text>
       
  8406           <xsl:text>        while(i &lt; data.byteLength){
       
  8407 </xsl:text>
       
  8408           <xsl:text>            let index = dv.getUint32(i, true);
       
  8409 </xsl:text>
       
  8410           <xsl:text>            i += 4;
       
  8411 </xsl:text>
       
  8412           <xsl:text>            let iectype = hmitree_types[index];
       
  8413 </xsl:text>
       
  8414           <xsl:text>            if(iectype != undefined){
       
  8415 </xsl:text>
       
  8416           <xsl:text>                let dvgetter = dvgetters[iectype];
       
  8417 </xsl:text>
       
  8418           <xsl:text>                let [value, bytesize] = dvgetter(dv,i);
       
  8419 </xsl:text>
       
  8420           <xsl:text>                updates.set(index, value);
       
  8421 </xsl:text>
       
  8422           <xsl:text>                i += bytesize;
       
  8423 </xsl:text>
       
  8424           <xsl:text>            } else {
       
  8425 </xsl:text>
       
  8426           <xsl:text>                throw new Error("Unknown index "+index);
       
  8427 </xsl:text>
       
  8428           <xsl:text>            }
       
  8429 </xsl:text>
       
  8430           <xsl:text>        };
       
  8431 </xsl:text>
       
  8432           <xsl:text>        // register for rendering on next frame, since there are updates
       
  8433 </xsl:text>
       
  8434           <xsl:text>        requestHMIAnimation();
       
  8435 </xsl:text>
       
  8436           <xsl:text>    } catch(err) {
       
  8437 </xsl:text>
       
  8438           <xsl:text>        // 1003 is for "Unsupported Data"
       
  8439 </xsl:text>
       
  8440           <xsl:text>        // ws.close(1003, err.message);
       
  8441 </xsl:text>
       
  8442           <xsl:text>
       
  8443 </xsl:text>
       
  8444           <xsl:text>        // TODO : remove debug alert ?
       
  8445 </xsl:text>
       
  8446           <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
       
  8447 </xsl:text>
       
  8448           <xsl:text>
       
  8449 </xsl:text>
       
  8450           <xsl:text>        // force reload ignoring cache
       
  8451 </xsl:text>
       
  8452           <xsl:text>        location.reload(true);
       
  8453 </xsl:text>
       
  8454           <xsl:text>    }
       
  8455 </xsl:text>
       
  8456           <xsl:text>};
       
  8457 </xsl:text>
       
  8458           <xsl:text>
       
  8459 </xsl:text>
       
  8460           <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash);
       
  8461 </xsl:text>
       
  8462           <xsl:text>
       
  8463 </xsl:text>
       
  8464           <xsl:text>function send_blob(data) {
       
  8465 </xsl:text>
       
  8466           <xsl:text>    if(data.length &gt; 0) {
       
  8467 </xsl:text>
       
  8468           <xsl:text>        ws.send(new Blob([hmi_hash_u8].concat(data)));
       
  8469 </xsl:text>
       
  8470           <xsl:text>    };
       
  8471 </xsl:text>
       
  8472           <xsl:text>};
       
  8473 </xsl:text>
       
  8474           <xsl:text>
       
  8475 </xsl:text>
       
  8476           <xsl:text>const typedarray_types = {
       
  8477 </xsl:text>
       
  8478           <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
       
  8479 </xsl:text>
       
  8480           <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
       
  8481 </xsl:text>
       
  8482           <xsl:text>    NODE: (truth) =&gt; new Int16Array([truth]),
       
  8483 </xsl:text>
       
  8484           <xsl:text>    REAL: (number) =&gt; new Float32Array([number]),
       
  8485 </xsl:text>
       
  8486           <xsl:text>    STRING: (str) =&gt; {
       
  8487 </xsl:text>
       
  8488           <xsl:text>        // beremiz default string max size is 128
       
  8489 </xsl:text>
       
  8490           <xsl:text>        str = str.slice(0,128);
       
  8491 </xsl:text>
       
  8492           <xsl:text>        binary = new Uint8Array(str.length + 1);
       
  8493 </xsl:text>
       
  8494           <xsl:text>        binary[0] = str.length;
       
  8495 </xsl:text>
       
  8496           <xsl:text>        for(let i = 0; i &lt; str.length; i++){
       
  8497 </xsl:text>
       
  8498           <xsl:text>            binary[i+1] = str.charCodeAt(i);
       
  8499 </xsl:text>
       
  8500           <xsl:text>        }
       
  8501 </xsl:text>
       
  8502           <xsl:text>        return binary;
       
  8503 </xsl:text>
       
  8504           <xsl:text>    }
       
  8505 </xsl:text>
       
  8506           <xsl:text>    /* TODO */
       
  8507 </xsl:text>
       
  8508           <xsl:text>};
       
  8509 </xsl:text>
       
  8510           <xsl:text>
       
  8511 </xsl:text>
       
  8512           <xsl:text>function send_reset() {
       
  8513 </xsl:text>
       
  8514           <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
       
  8515 </xsl:text>
       
  8516           <xsl:text>};
       
  8517 </xsl:text>
       
  8518           <xsl:text>
       
  8519 </xsl:text>
       
  8520           <xsl:text>var subscriptions = [];
       
  8521 </xsl:text>
       
  8522           <xsl:text>
       
  8523 </xsl:text>
       
  8524           <xsl:text>function subscribers(index) {
       
  8525 </xsl:text>
       
  8526           <xsl:text>    let entry = subscriptions[index];
       
  8527 </xsl:text>
       
  8528           <xsl:text>    let res;
       
  8529 </xsl:text>
       
  8530           <xsl:text>    if(entry == undefined){
       
  8531 </xsl:text>
       
  8532           <xsl:text>        res = new Set();
       
  8533 </xsl:text>
       
  8534           <xsl:text>        subscriptions[index] = [res,0];
       
  8535 </xsl:text>
       
  8536           <xsl:text>    }else{
       
  8537 </xsl:text>
       
  8538           <xsl:text>        [res, _ign] = entry;
       
  8539 </xsl:text>
       
  8540           <xsl:text>    }
       
  8541 </xsl:text>
       
  8542           <xsl:text>    return res
       
  8543 </xsl:text>
       
  8544           <xsl:text>}
       
  8545 </xsl:text>
       
  8546           <xsl:text>
       
  8547 </xsl:text>
       
  8548           <xsl:text>function get_subscription_period(index) {
       
  8549 </xsl:text>
       
  8550           <xsl:text>    let entry = subscriptions[index];
       
  8551 </xsl:text>
       
  8552           <xsl:text>    if(entry == undefined)
       
  8553 </xsl:text>
       
  8554           <xsl:text>        return 0;
       
  8555 </xsl:text>
       
  8556           <xsl:text>    let [_ign, period] = entry;
       
  8557 </xsl:text>
       
  8558           <xsl:text>    return period;
       
  8559 </xsl:text>
       
  8560           <xsl:text>}
       
  8561 </xsl:text>
       
  8562           <xsl:text>
       
  8563 </xsl:text>
       
  8564           <xsl:text>function set_subscription_period(index, period) {
       
  8565 </xsl:text>
       
  8566           <xsl:text>    let entry = subscriptions[index];
       
  8567 </xsl:text>
       
  8568           <xsl:text>    if(entry == undefined){
       
  8569 </xsl:text>
       
  8570           <xsl:text>        subscriptions[index] = [new Set(), period];
       
  8571 </xsl:text>
       
  8572           <xsl:text>    } else {
       
  8573 </xsl:text>
       
  8574           <xsl:text>        entry[1] = period;
       
  8575 </xsl:text>
       
  8576           <xsl:text>    }
       
  8577 </xsl:text>
       
  8578           <xsl:text>}
       
  8579 </xsl:text>
       
  8580           <xsl:text>
       
  8581 </xsl:text>
       
  8582           <xsl:text>if(has_watchdog){
       
  8583 </xsl:text>
       
  8584           <xsl:text>    // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
       
  8585 </xsl:text>
       
  8586           <xsl:text>    // Since dispatch directly calls change_hmi_value,
       
  8587 </xsl:text>
       
  8588           <xsl:text>    // PLC will periodically send variable at given frequency
       
  8589 </xsl:text>
       
  8590           <xsl:text>    subscribers(heartbeat_index).add({
       
  8591 </xsl:text>
       
  8592           <xsl:text>        /* type: "Watchdog", */
       
  8593 </xsl:text>
       
  8594           <xsl:text>        frequency: 1,
       
  8595 </xsl:text>
       
  8596           <xsl:text>        indexes: [heartbeat_index],
       
  8597 </xsl:text>
       
  8598           <xsl:text>        new_hmi_value: function(index, value, oldval) {
       
  8599 </xsl:text>
       
  8600           <xsl:text>            apply_hmi_value(heartbeat_index, value+1);
       
  8601 </xsl:text>
       
  8602           <xsl:text>        }
       
  8603 </xsl:text>
       
  8604           <xsl:text>    });
       
  8605 </xsl:text>
       
  8606           <xsl:text>}
       
  8607 </xsl:text>
       
  8608           <xsl:text>
       
  8609 </xsl:text>
       
  8610           <xsl:text>// subscribe to per instance current page hmi variable
       
  8611 </xsl:text>
       
  8612           <xsl:text>// PLC must prefix page name with "!" for page switch to happen
       
  8613 </xsl:text>
       
  8614           <xsl:text>subscribers(current_page_var_index).add({
       
  8615 </xsl:text>
       
  8616           <xsl:text>    frequency: 1,
       
  8617 </xsl:text>
       
  8618           <xsl:text>    indexes: [current_page_var_index],
       
  8619 </xsl:text>
       
  8620           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  8621 </xsl:text>
       
  8622           <xsl:text>        if(value.startsWith("!"))
       
  8623 </xsl:text>
       
  8624           <xsl:text>            switch_page(value.slice(1));
       
  8625 </xsl:text>
       
  8626           <xsl:text>    }
       
  8627 </xsl:text>
       
  8628           <xsl:text>});
       
  8629 </xsl:text>
       
  8630           <xsl:text>
       
  8631 </xsl:text>
       
  8632           <xsl:text>function svg_text_to_multiline(elt) {
       
  8633 </xsl:text>
       
  8634           <xsl:text>    return(Array.prototype.map.call(elt.children, x=&gt;x.textContent).join("\n")); 
       
  8635 </xsl:text>
       
  8636           <xsl:text>}
       
  8637 </xsl:text>
       
  8638           <xsl:text>
       
  8639 </xsl:text>
       
  8640           <xsl:text>function multiline_to_svg_text(elt, str) {
       
  8641 </xsl:text>
       
  8642           <xsl:text>    str.split('\n').map((line,i) =&gt; {elt.children[i].textContent = line;});
       
  8643 </xsl:text>
       
  8644           <xsl:text>}
       
  8645 </xsl:text>
       
  8646           <xsl:text>
       
  8647 </xsl:text>
       
  8648           <xsl:text>function switch_langnum(langnum) {
       
  8649 </xsl:text>
       
  8650           <xsl:text>    langnum = Math.max(0, Math.min(langs.length - 1, langnum));
       
  8651 </xsl:text>
       
  8652           <xsl:text>
       
  8653 </xsl:text>
       
  8654           <xsl:text>    for (let translation of translations) {
       
  8655 </xsl:text>
       
  8656           <xsl:text>        let [objs, msgs] = translation;
       
  8657 </xsl:text>
       
  8658           <xsl:text>        let msg = msgs[langnum];
       
  8659 </xsl:text>
       
  8660           <xsl:text>        for (let obj of objs) {
       
  8661 </xsl:text>
       
  8662           <xsl:text>            multiline_to_svg_text(obj, msg);
       
  8663 </xsl:text>
       
  8664           <xsl:text>            obj.setAttribute("lang",langnum);
       
  8665 </xsl:text>
       
  8666           <xsl:text>        }
       
  8667 </xsl:text>
       
  8668           <xsl:text>    }
       
  8669 </xsl:text>
       
  8670           <xsl:text>    return langnum;
       
  8671 </xsl:text>
       
  8672           <xsl:text>}
       
  8673 </xsl:text>
       
  8674           <xsl:text>
       
  8675 </xsl:text>
       
  8676           <xsl:text>// backup original texts
       
  8677 </xsl:text>
       
  8678           <xsl:text>for (let translation of translations) {
       
  8679 </xsl:text>
       
  8680           <xsl:text>    let [objs, msgs] = translation;
       
  8681 </xsl:text>
       
  8682           <xsl:text>    msgs.unshift(svg_text_to_multiline(objs[0])); 
       
  8683 </xsl:text>
       
  8684           <xsl:text>}
       
  8685 </xsl:text>
       
  8686           <xsl:text>
       
  8687 </xsl:text>
       
  8688           <xsl:text>var lang_local_index = hmi_local_index("lang");
       
  8689 </xsl:text>
       
  8690           <xsl:text>var langcode_local_index = hmi_local_index("lang_code");
       
  8691 </xsl:text>
       
  8692           <xsl:text>var langname_local_index = hmi_local_index("lang_name");
       
  8693 </xsl:text>
       
  8694           <xsl:text>subscribers(lang_local_index).add({
       
  8695 </xsl:text>
       
  8696           <xsl:text>    indexes: [lang_local_index],
       
  8697 </xsl:text>
       
  8698           <xsl:text>    new_hmi_value: function(index, value, oldval) {
       
  8699 </xsl:text>
       
  8700           <xsl:text>        let current_lang =  switch_langnum(value);
       
  8701 </xsl:text>
       
  8702           <xsl:text>        let [langname,langcode] = langs[current_lang];
       
  8703 </xsl:text>
       
  8704           <xsl:text>        apply_hmi_value(langcode_local_index, langcode);
       
  8705 </xsl:text>
       
  8706           <xsl:text>        apply_hmi_value(langname_local_index, langname);
       
  8707 </xsl:text>
       
  8708           <xsl:text>        switch_page();
       
  8709 </xsl:text>
       
  8710           <xsl:text>    }
       
  8711 </xsl:text>
       
  8712           <xsl:text>});
       
  8713 </xsl:text>
 10697 </xsl:text>
  8714           <xsl:text>
 10698           <xsl:text>
  8715 </xsl:text>
 10699 </xsl:text>
  8716           <xsl:text>function setup_lang(){
 10700           <xsl:text>function setup_lang(){
  8717 </xsl:text>
 10701 </xsl:text>