svghmi/gen_index_xhtml.xslt
branchsvghmi
changeset 2798 ddb2c4668a6b
parent 2797 c5ba1e77f054
child 2799 f5da343b9b63
equal deleted inserted replaced
2797:c5ba1e77f054 2798:ddb2c4668a6b
     1 <?xml version="1.0"?>
     1 <?xml version="1.0"?>
     2 <xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:str="http://exslt.org/strings" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:exsl="http://exslt.org/common" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:ns="beremiz" xmlns:cc="http://creativecommons.org/ns#" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dc="http://purl.org/dc/elements/1.1/" extension-element-prefixes="ns func" version="1.0" exclude-result-prefixes="ns str regexp exsl func">
     2 <xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:str="http://exslt.org/strings" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:exsl="http://exslt.org/common" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:ns="beremiz" xmlns:cc="http://creativecommons.org/ns#" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dc="http://purl.org/dc/elements/1.1/" extension-element-prefixes="ns func" version="1.0" exclude-result-prefixes="ns str regexp exsl func">
     3   <xsl:output method="xml" cdata-section-elements="script"/>
     3   <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
     4   <xsl:variable name="geometry" select="ns:GetSVGGeometry()"/>
     4   <xsl:variable name="geometry" select="ns:GetSVGGeometry()"/>
     5   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     5   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
     6   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     6   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
     7   <xsl:variable name="hmi_geometry" select="$geometry[@Id = $hmi_elements/@id]"/>
     7   <xsl:variable name="hmi_geometry" select="$geometry[@Id = $hmi_elements/@id]"/>
     8   <xsl:variable name="hmi_pages" select="$hmi_elements[func:parselabel(@inkscape:label)/widget/@type = 'Page']"/>
     8   <xsl:variable name="hmi_pages" select="$hmi_elements[func:parselabel(@inkscape:label)/widget/@type = 'Page']"/>
   177       </xsl:if>
   177       </xsl:if>
   178     </xsl:variable>
   178     </xsl:variable>
   179     <func:result select="exsl:node-set($ast)"/>
   179     <func:result select="exsl:node-set($ast)"/>
   180   </func:function>
   180   </func:function>
   181   <xsl:template name="scripts">
   181   <xsl:template name="scripts">
       
   182     <xsl:text>(function(){
       
   183 </xsl:text>
       
   184     <xsl:text>
       
   185 </xsl:text>
   182     <xsl:text>var hmi_hash = [</xsl:text>
   186     <xsl:text>var hmi_hash = [</xsl:text>
   183     <xsl:value-of select="$hmitree/@hash"/>
   187     <xsl:value-of select="$hmitree/@hash"/>
   184     <xsl:text>]; 
   188     <xsl:text>]; 
   185 </xsl:text>
   189 </xsl:text>
   186     <xsl:text>var hmi_widgets = {
   190     <xsl:text>var hmi_widgets = {
   210         <xsl:text>
   214         <xsl:text>
   211 </xsl:text>
   215 </xsl:text>
   212       </xsl:for-each>
   216       </xsl:for-each>
   213       <xsl:text>    ],
   217       <xsl:text>    ],
   214 </xsl:text>
   218 </xsl:text>
   215       <xsl:text>    paths: [
   219       <xsl:text>    indexes: [
   216 </xsl:text>
   220 </xsl:text>
   217       <xsl:for-each select="$widget/path">
   221       <xsl:for-each select="$widget/path">
   218         <xsl:variable name="hmipath" select="@value"/>
   222         <xsl:variable name="hmipath" select="@value"/>
   219         <xsl:variable name="hmitree_match" select="$indexed_hmitree/*[@hmipath = $hmipath]"/>
   223         <xsl:variable name="hmitree_match" select="$indexed_hmitree/*[@hmipath = $hmipath]"/>
   220         <xsl:if test="count($hmitree_match) = 0">
   224         <xsl:if test="count($hmitree_match) = 0">
   243     </xsl:for-each>
   247     </xsl:for-each>
   244     <xsl:text>}
   248     <xsl:text>}
   245 </xsl:text>
   249 </xsl:text>
   246     <xsl:text>
   250     <xsl:text>
   247 </xsl:text>
   251 </xsl:text>
   248     <xsl:text>var hmi_index = [
   252     <xsl:text>var hmitree_types = [
   249 </xsl:text>
   253 </xsl:text>
   250     <xsl:variable name="svg" select="/"/>
       
   251     <xsl:for-each select="$indexed_hmitree/*">
   254     <xsl:for-each select="$indexed_hmitree/*">
   252       <xsl:text>{   /* </xsl:text>
   255       <xsl:text>/* </xsl:text>
   253       <xsl:value-of select="@index"/>
   256       <xsl:value-of select="@index"/>
   254       <xsl:text>  </xsl:text>
   257       <xsl:text>  </xsl:text>
   255       <xsl:value-of select="@hmipath"/>
   258       <xsl:value-of select="@hmipath"/>
   256       <xsl:text> */
   259       <xsl:text> */ "</xsl:text>
   257 </xsl:text>
   260       <xsl:value-of select="substring(local-name(), 5)"/>
   258       <xsl:text>    type: "</xsl:text>
   261       <xsl:text>"</xsl:text>
   259       <xsl:value-of select="local-name()"/>
       
   260       <xsl:text>",
       
   261 </xsl:text>
       
   262       <xsl:text>    ids: [
       
   263 </xsl:text>
       
   264       <xsl:variable name="hmipath" select="@hmipath"/>
       
   265       <xsl:for-each select="$svg//*[substring-after(@inkscape:label,'@') = $hmipath]">
       
   266         <xsl:text>        hmi_widgets["</xsl:text>
       
   267         <xsl:value-of select="@id"/>
       
   268         <xsl:text>"]</xsl:text>
       
   269         <xsl:if test="position()!=last()">
       
   270           <xsl:text>,</xsl:text>
       
   271         </xsl:if>
       
   272         <xsl:text>
       
   273 </xsl:text>
       
   274       </xsl:for-each>
       
   275       <xsl:text>    ]
       
   276 </xsl:text>
       
   277       <xsl:text>}</xsl:text>
       
   278       <xsl:if test="position()!=last()">
   262       <xsl:if test="position()!=last()">
   279         <xsl:text>,</xsl:text>
   263         <xsl:text>,</xsl:text>
   280       </xsl:if>
   264       </xsl:if>
   281       <xsl:text>
   265       <xsl:text>
   282 </xsl:text>
   266 </xsl:text>
   302       <xsl:text>",
   286       <xsl:text>",
   303 </xsl:text>
   287 </xsl:text>
   304       <xsl:text>        widgets: [
   288       <xsl:text>        widgets: [
   305 </xsl:text>
   289 </xsl:text>
   306       <xsl:for-each select="$page_ids">
   290       <xsl:for-each select="$page_ids">
   307         <xsl:text>            "</xsl:text>
   291         <xsl:text>            hmi_widgets.</xsl:text>
   308         <xsl:value-of select="."/>
   292         <xsl:value-of select="."/>
   309         <xsl:text>"</xsl:text>
       
   310         <xsl:if test="position()!=last()">
   293         <xsl:if test="position()!=last()">
   311           <xsl:text>,</xsl:text>
   294           <xsl:text>,</xsl:text>
   312         </xsl:if>
   295         </xsl:if>
   313         <xsl:text>
   296         <xsl:text>
   314 </xsl:text>
   297 </xsl:text>
   315       </xsl:for-each>
       
   316       <xsl:text>        ]
       
   317 </xsl:text>
       
   318       <xsl:text>        subscriptions: [
       
   319 </xsl:text>
       
   320       <xsl:for-each select="$page_elements">
       
   321         <xsl:variable name="hmipaths" select="func:parselabel(@inkscape:label)/widget/path/@value"/>
       
   322         <xsl:variable name="notlast" select="position()!=last()"/>
       
   323         <xsl:for-each select="$hmipaths">
       
   324           <xsl:variable name="hmipath" select="."/>
       
   325           <xsl:text>            </xsl:text>
       
   326           <xsl:value-of select="$indexed_hmitree/*[@hmipath = $hmipath]/@index"/>
       
   327           <xsl:if test="$notlast or position()!=last()">
       
   328             <xsl:text>,</xsl:text>
       
   329           </xsl:if>
       
   330           <xsl:text>
       
   331 </xsl:text>
       
   332         </xsl:for-each>
       
   333       </xsl:for-each>
   298       </xsl:for-each>
   334       <xsl:text>        ]
   299       <xsl:text>        ]
   335 </xsl:text>
   300 </xsl:text>
   336       <xsl:text>    }</xsl:text>
   301       <xsl:text>    }</xsl:text>
   337       <xsl:if test="position()!=last()">
   302       <xsl:if test="position()!=last()">
   350 </xsl:text>
   315 </xsl:text>
   351     <xsl:text>// svghmi.js
   316     <xsl:text>// svghmi.js
   352 </xsl:text>
   317 </xsl:text>
   353     <xsl:text>
   318     <xsl:text>
   354 </xsl:text>
   319 </xsl:text>
   355     <xsl:text>(function(){
   320     <xsl:text>function dispatch_value(index, value) {
   356 </xsl:text>
   321 </xsl:text>
   357     <xsl:text>    // Open WebSocket to relative "/ws" address
   322     <xsl:text>    console.log("dispatch_value("+index+value+")");
   358 </xsl:text>
   323 </xsl:text>
   359     <xsl:text>    var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
   324     <xsl:text>};
   360 </xsl:text>
   325 </xsl:text>
   361     <xsl:text>
   326     <xsl:text>
   362 </xsl:text>
   327 </xsl:text>
   363     <xsl:text>    // Register message reception handler 
   328     <xsl:text>// Open WebSocket to relative "/ws" address
   364 </xsl:text>
   329 </xsl:text>
   365     <xsl:text>    ws.onmessage = function (evt) {
   330     <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
   366 </xsl:text>
   331 </xsl:text>
   367     <xsl:text>        // TODO : dispatch and cache hmi tree updates
   332     <xsl:text>ws.binaryType = 'arraybuffer';
   368 </xsl:text>
   333 </xsl:text>
   369     <xsl:text>
   334     <xsl:text>
   370 </xsl:text>
   335 </xsl:text>
   371     <xsl:text>        var received_msg = evt.data;
   336     <xsl:text>const dvgetters = {
   372 </xsl:text>
   337 </xsl:text>
   373     <xsl:text>        // TODO : check for hmitree hash header
   338     <xsl:text>    INT: [DataView.prototype.getInt16, 2],
   374 </xsl:text>
   339 </xsl:text>
   375     <xsl:text>        //        if not matching, reload page
   340     <xsl:text>    BOOL: [DataView.prototype.getInt8, 1]
   376 </xsl:text>
   341 </xsl:text>
   377     <xsl:text>        alert("Message is received..."+received_msg); 
   342     <xsl:text>    /* TODO */
       
   343 </xsl:text>
       
   344     <xsl:text>};
       
   345 </xsl:text>
       
   346     <xsl:text>
       
   347 </xsl:text>
       
   348     <xsl:text>// Register message reception handler 
       
   349 </xsl:text>
       
   350     <xsl:text>ws.onmessage = function (evt) {
       
   351 </xsl:text>
       
   352     <xsl:text>
       
   353 </xsl:text>
       
   354     <xsl:text>    let data = evt.data;
       
   355 </xsl:text>
       
   356     <xsl:text>    let dv = new DataView(data);
       
   357 </xsl:text>
       
   358     <xsl:text>    let i = 0;
       
   359 </xsl:text>
       
   360     <xsl:text>    for(let hash_int of hmi_hash) {
       
   361 </xsl:text>
       
   362     <xsl:text>        if(hash_int != dv.getUint8(i)){
       
   363 </xsl:text>
       
   364     <xsl:text>            console.log("Recv non maching hash. Reload.");
       
   365 </xsl:text>
       
   366     <xsl:text>
       
   367 </xsl:text>
       
   368     <xsl:text>            // 1003 is for "Unsupported Data"
       
   369 </xsl:text>
       
   370     <xsl:text>            ws.close(1003,"Hash doesn't match");
       
   371 </xsl:text>
       
   372     <xsl:text>
       
   373 </xsl:text>
       
   374     <xsl:text>            // TODO : remove debug alert ?
       
   375 </xsl:text>
       
   376     <xsl:text>            alert("HMI will be reloaded.");
       
   377 </xsl:text>
       
   378     <xsl:text>
       
   379 </xsl:text>
       
   380     <xsl:text>            // force reload ignoring cache
       
   381 </xsl:text>
       
   382     <xsl:text>            location.reload(true);
       
   383 </xsl:text>
       
   384     <xsl:text>        };
       
   385 </xsl:text>
       
   386     <xsl:text>        i++;
   378 </xsl:text>
   387 </xsl:text>
   379     <xsl:text>    };
   388     <xsl:text>    };
   380 </xsl:text>
   389 </xsl:text>
   381     <xsl:text>
   390     <xsl:text>
   382 </xsl:text>
   391 </xsl:text>
   383     <xsl:text>    // Once connection established
   392     <xsl:text>    while(i &lt; data.length){
   384 </xsl:text>
   393 </xsl:text>
   385     <xsl:text>    ws.onopen = function (evt) {
   394     <xsl:text>        let index = dv.getUint32(i);
   386 </xsl:text>
   395 </xsl:text>
   387     <xsl:text>        // TODO : enable the HMI (was previously offline, or just starts)
   396     <xsl:text>        i += 4;
   388 </xsl:text>
   397 </xsl:text>
   389     <xsl:text>        //        show main page
   398     <xsl:text>        let iectype = hmitree_types[index];
   390 </xsl:text>
   399 </xsl:text>
   391     <xsl:text>
   400     <xsl:text>        let [dvgetter, bytesize] = dvgetters[iectypes];
   392 </xsl:text>
   401 </xsl:text>
   393     <xsl:text>
   402     <xsl:text>        value = dvgetter.call(dv,i);
   394 </xsl:text>
   403 </xsl:text>
   395     <xsl:text>        // TODO : prefix with hmitree hash header
   404     <xsl:text>        dispatch_value(index, value);
   396 </xsl:text>
   405 </xsl:text>
   397     <xsl:text>        ws.send("test");
   406     <xsl:text>        i += bytesize;
   398 </xsl:text>
   407 </xsl:text>
   399     <xsl:text>    };
   408     <xsl:text>    };
   400 </xsl:text>
   409 </xsl:text>
   401     <xsl:text>
   410     <xsl:text>};
   402 </xsl:text>
   411 </xsl:text>
   403     <xsl:text>    var pending_updates = {};
   412     <xsl:text>
   404 </xsl:text>
   413 </xsl:text>
   405     <xsl:text>    
   414     <xsl:text>
   406 </xsl:text>
   415 </xsl:text>
   407     <xsl:text>    // subscription state, as it should be in hmi server
   416     <xsl:text>function send_blob(data) {
   408 </xsl:text>
   417 </xsl:text>
   409     <xsl:text>    // expected {index:period}
   418     <xsl:text>    if(data.length &gt; 0) {
   410 </xsl:text>
   419 </xsl:text>
   411     <xsl:text>    const subscriptions = new Map();
   420     <xsl:text>        ws.send(new Blob([
   412 </xsl:text>
   421 </xsl:text>
   413     <xsl:text>
   422     <xsl:text>            new Uint8Array(hmi_hash), 
   414 </xsl:text>
   423 </xsl:text>
   415     <xsl:text>    // subscription state as needed by widget now
   424     <xsl:text>            data]));
   416 </xsl:text>
   425 </xsl:text>
   417     <xsl:text>    // expected {index:[widgets]};
   426     <xsl:text>    };
   418 </xsl:text>
   427 </xsl:text>
   419     <xsl:text>    var subscribers = new Map();
   428     <xsl:text>};
   420 </xsl:text>
   429 </xsl:text>
   421     <xsl:text>
   430     <xsl:text>
   422 </xsl:text>
   431 </xsl:text>
   423     <xsl:text>    // return the diff in between curently subscribed and subscription
   432     <xsl:text>const typedarray_types = {
   424 </xsl:text>
   433 </xsl:text>
   425     <xsl:text>    function update_subscriptions() {
   434     <xsl:text>    INT: Int16Array,
   426 </xsl:text>
   435 </xsl:text>
   427     <xsl:text>        let delta = [];
   436     <xsl:text>    BOOL: Uint8Array
   428 </xsl:text>
   437 </xsl:text>
   429     <xsl:text>        for(let [index, widgets] in subscribers.entries()){
   438     <xsl:text>    /* TODO */
   430 </xsl:text>
   439 </xsl:text>
   431     <xsl:text>
   440     <xsl:text>};
   432 </xsl:text>
   441 </xsl:text>
   433     <xsl:text>            // periods are in ms
   442     <xsl:text>
   434 </xsl:text>
   443 </xsl:text>
   435     <xsl:text>            let previous_period = subscriptions.get(index);
   444     <xsl:text>function send_reset() {
   436 </xsl:text>
   445 </xsl:text>
   437     <xsl:text>
   446     <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
   438 </xsl:text>
   447 </xsl:text>
   439     <xsl:text>            let new_period = Math.min(...widgets.map(widget =&gt; 1000/widget.frequency));
   448     <xsl:text>};
   440 </xsl:text>
   449 </xsl:text>
   441     <xsl:text>
   450     <xsl:text>
   442 </xsl:text>
   451 </xsl:text>
   443     <xsl:text>            if(previous_period != new_period) {
   452     <xsl:text>// subscription state, as it should be in hmi server
   444 </xsl:text>
   453 </xsl:text>
   445     <xsl:text>                subscriptions.set(index, new_period);
   454     <xsl:text>// hmitree indexed array of integers
   446 </xsl:text>
   455 </xsl:text>
   447     <xsl:text>                delta.push({index: index, period: new_period});
   456     <xsl:text>var subscriptions =  hmitree_types.map(_ignored =&gt; 0);
   448 </xsl:text>
   457 </xsl:text>
   449     <xsl:text>            }
   458     <xsl:text>
   450 </xsl:text>
   459 </xsl:text>
   451     <xsl:text>            
   460     <xsl:text>// subscription state as needed by widget now
   452 </xsl:text>
   461 </xsl:text>
   453     <xsl:text>        })
   462     <xsl:text>// hmitree indexed array of Sets of widgets objects
   454 </xsl:text>
   463 </xsl:text>
   455     <xsl:text>        return result;
   464     <xsl:text>var subscribers = hmitree_types.map(_ignored =&gt; new Set());
       
   465 </xsl:text>
       
   466     <xsl:text>
       
   467 </xsl:text>
       
   468     <xsl:text>function update_subscriptions() {
       
   469 </xsl:text>
       
   470     <xsl:text>    let delta = [];
       
   471 </xsl:text>
       
   472     <xsl:text>    for(let index = 0; index &lt; subscribers.length; index++){
       
   473 </xsl:text>
       
   474     <xsl:text>        let widgets = subscribers[index];
       
   475 </xsl:text>
       
   476     <xsl:text>
       
   477 </xsl:text>
       
   478     <xsl:text>        // periods are in ms
       
   479 </xsl:text>
       
   480     <xsl:text>        let previous_period = subscriptions[index];
       
   481 </xsl:text>
       
   482     <xsl:text>
       
   483 </xsl:text>
       
   484     <xsl:text>        let new_period;
       
   485 </xsl:text>
       
   486     <xsl:text>        if(widgets.size &gt; 0) {
       
   487 </xsl:text>
       
   488     <xsl:text>            let maxfreq = 0;
       
   489 </xsl:text>
       
   490     <xsl:text>            for(let widget of widgets)
       
   491 </xsl:text>
       
   492     <xsl:text>                if(maxfreq &lt; widgets.frequency)
       
   493 </xsl:text>
       
   494     <xsl:text>                    maxfreq = widgets.frequency;
       
   495 </xsl:text>
       
   496     <xsl:text>
       
   497 </xsl:text>
       
   498     <xsl:text>            new_period = 1000/maxfreq;
       
   499 </xsl:text>
       
   500     <xsl:text>        } else {
       
   501 </xsl:text>
       
   502     <xsl:text>            new_period = 0;
       
   503 </xsl:text>
       
   504     <xsl:text>        }
       
   505 </xsl:text>
       
   506     <xsl:text>
       
   507 </xsl:text>
       
   508     <xsl:text>        if(previous_period != new_period) {
       
   509 </xsl:text>
       
   510     <xsl:text>            subscriptions[index] = new_period;
       
   511 </xsl:text>
       
   512     <xsl:text>            delta.push([
       
   513 </xsl:text>
       
   514     <xsl:text>                new Uint8Array([2]), /* subscribe = 2 */
       
   515 </xsl:text>
       
   516     <xsl:text>                new Uint32Array([index]), 
       
   517 </xsl:text>
       
   518     <xsl:text>                new Uint16Array([new_period])]);
       
   519 </xsl:text>
       
   520     <xsl:text>        }
       
   521 </xsl:text>
       
   522     <xsl:text>        
   456 </xsl:text>
   523 </xsl:text>
   457     <xsl:text>    }
   524     <xsl:text>    }
   458 </xsl:text>
   525 </xsl:text>
   459     <xsl:text>
   526     <xsl:text>    send_blob(delta);
   460 </xsl:text>
   527 </xsl:text>
   461     <xsl:text>
   528     <xsl:text>};
   462 </xsl:text>
   529 </xsl:text>
   463     <xsl:text>    function update_value(index, value) {
   530     <xsl:text>
   464 </xsl:text>
   531 </xsl:text>
   465     <xsl:text>
   532     <xsl:text>function update_value(index, value) {
   466 </xsl:text>
   533 </xsl:text>
   467     <xsl:text>    };
   534     <xsl:text>    iectype = hmitree_types[index];
   468 </xsl:text>
   535 </xsl:text>
   469     <xsl:text>
   536     <xsl:text>    jstype = typedarray_types[iectypes];
   470 </xsl:text>
   537 </xsl:text>
   471     <xsl:text>    var current_page = default_page;
   538     <xsl:text>    send_blob([
   472 </xsl:text>
   539 </xsl:text>
   473     <xsl:text>
   540     <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
   474 </xsl:text>
   541 </xsl:text>
   475     <xsl:text>    function switch_page(page_name) {
   542     <xsl:text>        new jstype([value])
   476 </xsl:text>
   543 </xsl:text>
   477     <xsl:text>        let new_desc = page_desc[page_name];
   544     <xsl:text>        ]);
   478 </xsl:text>
   545 </xsl:text>
   479     <xsl:text>        let old_desc = page_desc[page_name];
   546     <xsl:text>
   480 </xsl:text>
   547 </xsl:text>
   481     <xsl:text>        /* TODO hide / show widgets */
   548     <xsl:text>};
   482 </xsl:text>
   549 </xsl:text>
   483     <xsl:text>        /* TODO move viewport */
   550     <xsl:text>
   484 </xsl:text>
   551 </xsl:text>
   485     <xsl:text>
   552     <xsl:text>var current_page;
   486 </xsl:text>
   553 </xsl:text>
   487     <xsl:text>        /* Update subscribers */
   554     <xsl:text>
   488 </xsl:text>
   555 </xsl:text>
   489     <xsl:text>        /* Update subscriptions */
   556     <xsl:text>function switch_page(page_name) {
   490 </xsl:text>
   557 </xsl:text>
   491     <xsl:text>
   558     <xsl:text>    let old_desc = page_desc[current_page];
   492 </xsl:text>
   559 </xsl:text>
   493     <xsl:text>
   560     <xsl:text>    let new_desc = page_desc[page_name];
   494 </xsl:text>
   561 </xsl:text>
   495     <xsl:text>    };
   562     <xsl:text>    /* TODO hide / show widgets */
       
   563 </xsl:text>
       
   564     <xsl:text>    /* TODO move viewport */
       
   565 </xsl:text>
       
   566     <xsl:text>
       
   567 </xsl:text>
       
   568     <xsl:text>    /* remove subsribers of previous page if any */
       
   569 </xsl:text>
       
   570     <xsl:text>    if(old_desc) for(let widget of old_desc.widgets){
       
   571 </xsl:text>
       
   572     <xsl:text>        for(let index of widget.indexes){
       
   573 </xsl:text>
       
   574     <xsl:text>            subscribers[index].delete(widget);
       
   575 </xsl:text>
       
   576     <xsl:text>        }
       
   577 </xsl:text>
       
   578     <xsl:text>    }
       
   579 </xsl:text>
       
   580     <xsl:text>    /* add new subsribers if any */
       
   581 </xsl:text>
       
   582     <xsl:text>    if(new_desc) for(let widget of new_desc.widgets){
       
   583 </xsl:text>
       
   584     <xsl:text>        for(let index of widget.indexes){
       
   585 </xsl:text>
       
   586     <xsl:text>            subscribers[index].add(widget);
       
   587 </xsl:text>
       
   588     <xsl:text>        }
       
   589 </xsl:text>
       
   590     <xsl:text>    }
       
   591 </xsl:text>
       
   592     <xsl:text>
       
   593 </xsl:text>
       
   594     <xsl:text>    current_page = page_name;
       
   595 </xsl:text>
       
   596     <xsl:text>
       
   597 </xsl:text>
       
   598     <xsl:text>    update_subscriptions();
       
   599 </xsl:text>
       
   600     <xsl:text>};
       
   601 </xsl:text>
       
   602     <xsl:text>
       
   603 </xsl:text>
       
   604     <xsl:text>
       
   605 </xsl:text>
       
   606     <xsl:text>// Once connection established
       
   607 </xsl:text>
       
   608     <xsl:text>ws.onopen = function (evt) {
       
   609 </xsl:text>
       
   610     <xsl:text>    send_reset();
       
   611 </xsl:text>
       
   612     <xsl:text>    // show main page
       
   613 </xsl:text>
       
   614     <xsl:text>    switch_page(default_page);
       
   615 </xsl:text>
       
   616     <xsl:text>
       
   617 </xsl:text>
       
   618     <xsl:text>};
   496 </xsl:text>
   619 </xsl:text>
   497     <xsl:text>
   620     <xsl:text>
   498 </xsl:text>
   621 </xsl:text>
   499     <xsl:text>})();
   622     <xsl:text>})();
   500 </xsl:text>
   623 </xsl:text>