11089 </xsl:text> |
11333 </xsl:text> |
11090 <xsl:text>}; |
11334 <xsl:text>}; |
11091 </xsl:text> |
11335 </xsl:text> |
11092 <xsl:text> |
11336 <xsl:text> |
11093 </xsl:text> |
11337 </xsl:text> |
11094 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
11338 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
11095 </xsl:text> |
11339 </xsl:text> |
11096 <xsl:text>function apply_updates() { |
11340 <xsl:text>var requestAnimationFrameID = null; |
11097 </xsl:text> |
11341 </xsl:text> |
11098 <xsl:text> updates.forEach((value, index) => { |
11342 <xsl:text>function animate() { |
|
11343 </xsl:text> |
|
11344 <xsl:text> let rearm = true; |
|
11345 </xsl:text> |
|
11346 <xsl:text> do{ |
|
11347 </xsl:text> |
|
11348 <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ |
|
11349 </xsl:text> |
|
11350 <xsl:text> if(page_fading == "pending") |
|
11351 </xsl:text> |
|
11352 <xsl:text> svg_root.classList.add("fade-out-page"); |
|
11353 </xsl:text> |
|
11354 <xsl:text> page_fading = "in_progress"; |
|
11355 </xsl:text> |
|
11356 <xsl:text> if(page_fading_args.length) |
|
11357 </xsl:text> |
|
11358 <xsl:text> setTimeout(function(){ |
|
11359 </xsl:text> |
|
11360 <xsl:text> switch_page(...page_fading_args); |
|
11361 </xsl:text> |
|
11362 <xsl:text> },1); |
|
11363 </xsl:text> |
|
11364 <xsl:text> break; |
|
11365 </xsl:text> |
|
11366 <xsl:text> } |
|
11367 </xsl:text> |
|
11368 <xsl:text> |
|
11369 </xsl:text> |
|
11370 <xsl:text> // Do the page swith if pending |
|
11371 </xsl:text> |
|
11372 <xsl:text> if(page_switch_in_progress){ |
|
11373 </xsl:text> |
|
11374 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
11375 </xsl:text> |
|
11376 <xsl:text> switch_visible_page(current_subscribed_page); |
|
11377 </xsl:text> |
|
11378 <xsl:text> } |
|
11379 </xsl:text> |
|
11380 <xsl:text> |
|
11381 </xsl:text> |
|
11382 <xsl:text> page_switch_in_progress = false; |
|
11383 </xsl:text> |
|
11384 <xsl:text> |
|
11385 </xsl:text> |
|
11386 <xsl:text> if(page_fading == "in_progress"){ |
|
11387 </xsl:text> |
|
11388 <xsl:text> svg_root.classList.remove("fade-out-page"); |
|
11389 </xsl:text> |
|
11390 <xsl:text> page_fading = "off"; |
|
11391 </xsl:text> |
|
11392 <xsl:text> } |
|
11393 </xsl:text> |
|
11394 <xsl:text> } |
|
11395 </xsl:text> |
|
11396 <xsl:text> |
|
11397 </xsl:text> |
|
11398 <xsl:text> if(jumps_need_update) update_jumps(); |
|
11399 </xsl:text> |
|
11400 <xsl:text> |
|
11401 </xsl:text> |
|
11402 <xsl:text> |
|
11403 </xsl:text> |
|
11404 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
|
11405 </xsl:text> |
|
11406 <xsl:text> pending_widget_animates = []; |
|
11407 </xsl:text> |
|
11408 <xsl:text> rearm = false; |
|
11409 </xsl:text> |
|
11410 <xsl:text> } while(0); |
|
11411 </xsl:text> |
|
11412 <xsl:text> |
|
11413 </xsl:text> |
|
11414 <xsl:text> requestAnimationFrameID = null; |
|
11415 </xsl:text> |
|
11416 <xsl:text> |
|
11417 </xsl:text> |
|
11418 <xsl:text> if(rearm) requestHMIAnimation(); |
|
11419 </xsl:text> |
|
11420 <xsl:text>} |
|
11421 </xsl:text> |
|
11422 <xsl:text> |
|
11423 </xsl:text> |
|
11424 <xsl:text>function requestHMIAnimation() { |
|
11425 </xsl:text> |
|
11426 <xsl:text> if(requestAnimationFrameID == null){ |
|
11427 </xsl:text> |
|
11428 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
11429 </xsl:text> |
|
11430 <xsl:text> } |
|
11431 </xsl:text> |
|
11432 <xsl:text>} |
|
11433 </xsl:text> |
|
11434 <xsl:text> |
|
11435 </xsl:text> |
|
11436 <xsl:text>// Message reception handler |
|
11437 </xsl:text> |
|
11438 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
11439 </xsl:text> |
|
11440 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
11441 </xsl:text> |
|
11442 <xsl:text>ws.onmessage = function (evt) { |
|
11443 </xsl:text> |
|
11444 <xsl:text> |
|
11445 </xsl:text> |
|
11446 <xsl:text> let data = evt.data; |
|
11447 </xsl:text> |
|
11448 <xsl:text> let dv = new DataView(data); |
|
11449 </xsl:text> |
|
11450 <xsl:text> let i = 0; |
|
11451 </xsl:text> |
|
11452 <xsl:text> try { |
|
11453 </xsl:text> |
|
11454 <xsl:text> for(let hash_int of hmi_hash) { |
|
11455 </xsl:text> |
|
11456 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
11457 </xsl:text> |
|
11458 <xsl:text> throw new Error("Hash doesn't match"); |
|
11459 </xsl:text> |
|
11460 <xsl:text> }; |
|
11461 </xsl:text> |
|
11462 <xsl:text> i++; |
|
11463 </xsl:text> |
|
11464 <xsl:text> }; |
|
11465 </xsl:text> |
|
11466 <xsl:text> |
|
11467 </xsl:text> |
|
11468 <xsl:text> while(i < data.byteLength){ |
|
11469 </xsl:text> |
|
11470 <xsl:text> let index = dv.getUint32(i, true); |
|
11471 </xsl:text> |
|
11472 <xsl:text> i += 4; |
|
11473 </xsl:text> |
|
11474 <xsl:text> let iectype = hmitree_types[index]; |
|
11475 </xsl:text> |
|
11476 <xsl:text> if(iectype != undefined){ |
|
11477 </xsl:text> |
|
11478 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
11479 </xsl:text> |
|
11480 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
11481 </xsl:text> |
|
11482 <xsl:text> dispatch_value(index, value); |
|
11483 </xsl:text> |
|
11484 <xsl:text> i += bytesize; |
|
11485 </xsl:text> |
|
11486 <xsl:text> } else { |
|
11487 </xsl:text> |
|
11488 <xsl:text> throw new Error("Unknown index "+index); |
|
11489 </xsl:text> |
|
11490 <xsl:text> } |
|
11491 </xsl:text> |
|
11492 <xsl:text> }; |
|
11493 </xsl:text> |
|
11494 <xsl:text> |
|
11495 </xsl:text> |
|
11496 <xsl:text> // register for rendering on next frame, since there are updates |
|
11497 </xsl:text> |
|
11498 <xsl:text> } catch(err) { |
|
11499 </xsl:text> |
|
11500 <xsl:text> // 1003 is for "Unsupported Data" |
|
11501 </xsl:text> |
|
11502 <xsl:text> // ws.close(1003, err.message); |
|
11503 </xsl:text> |
|
11504 <xsl:text> |
|
11505 </xsl:text> |
|
11506 <xsl:text> // TODO : remove debug alert ? |
|
11507 </xsl:text> |
|
11508 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
11509 </xsl:text> |
|
11510 <xsl:text> |
|
11511 </xsl:text> |
|
11512 <xsl:text> // force reload ignoring cache |
|
11513 </xsl:text> |
|
11514 <xsl:text> location.reload(true); |
|
11515 </xsl:text> |
|
11516 <xsl:text> } |
|
11517 </xsl:text> |
|
11518 <xsl:text>}; |
|
11519 </xsl:text> |
|
11520 <xsl:text> |
|
11521 </xsl:text> |
|
11522 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
|
11523 </xsl:text> |
|
11524 <xsl:text> |
|
11525 </xsl:text> |
|
11526 <xsl:text>function send_blob(data) { |
|
11527 </xsl:text> |
|
11528 <xsl:text> if(data.length > 0) { |
|
11529 </xsl:text> |
|
11530 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
|
11531 </xsl:text> |
|
11532 <xsl:text> }; |
|
11533 </xsl:text> |
|
11534 <xsl:text>}; |
|
11535 </xsl:text> |
|
11536 <xsl:text> |
|
11537 </xsl:text> |
|
11538 <xsl:text>const typedarray_types = { |
|
11539 </xsl:text> |
|
11540 <xsl:text> INT: (number) => new Int16Array([number]), |
|
11541 </xsl:text> |
|
11542 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
11543 </xsl:text> |
|
11544 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
11545 </xsl:text> |
|
11546 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
11547 </xsl:text> |
|
11548 <xsl:text> STRING: (str) => { |
|
11549 </xsl:text> |
|
11550 <xsl:text> // beremiz default string max size is 128 |
|
11551 </xsl:text> |
|
11552 <xsl:text> str = str.slice(0,128); |
|
11553 </xsl:text> |
|
11554 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
11555 </xsl:text> |
|
11556 <xsl:text> binary[0] = str.length; |
|
11557 </xsl:text> |
|
11558 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
11559 </xsl:text> |
|
11560 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
11561 </xsl:text> |
|
11562 <xsl:text> } |
|
11563 </xsl:text> |
|
11564 <xsl:text> return binary; |
|
11565 </xsl:text> |
|
11566 <xsl:text> } |
|
11567 </xsl:text> |
|
11568 <xsl:text> /* TODO */ |
|
11569 </xsl:text> |
|
11570 <xsl:text>}; |
|
11571 </xsl:text> |
|
11572 <xsl:text> |
|
11573 </xsl:text> |
|
11574 <xsl:text>function send_reset() { |
|
11575 </xsl:text> |
|
11576 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
11577 </xsl:text> |
|
11578 <xsl:text>}; |
|
11579 </xsl:text> |
|
11580 <xsl:text> |
|
11581 </xsl:text> |
|
11582 <xsl:text>var subscriptions = []; |
|
11583 </xsl:text> |
|
11584 <xsl:text> |
|
11585 </xsl:text> |
|
11586 <xsl:text>function subscribers(index) { |
|
11587 </xsl:text> |
|
11588 <xsl:text> let entry = subscriptions[index]; |
|
11589 </xsl:text> |
|
11590 <xsl:text> let res; |
|
11591 </xsl:text> |
|
11592 <xsl:text> if(entry == undefined){ |
|
11593 </xsl:text> |
|
11594 <xsl:text> res = new Set(); |
|
11595 </xsl:text> |
|
11596 <xsl:text> subscriptions[index] = [res,0]; |
|
11597 </xsl:text> |
|
11598 <xsl:text> }else{ |
|
11599 </xsl:text> |
|
11600 <xsl:text> [res, _ign] = entry; |
|
11601 </xsl:text> |
|
11602 <xsl:text> } |
|
11603 </xsl:text> |
|
11604 <xsl:text> return res |
|
11605 </xsl:text> |
|
11606 <xsl:text>} |
|
11607 </xsl:text> |
|
11608 <xsl:text> |
|
11609 </xsl:text> |
|
11610 <xsl:text>function get_subscription_period(index) { |
|
11611 </xsl:text> |
|
11612 <xsl:text> let entry = subscriptions[index]; |
|
11613 </xsl:text> |
|
11614 <xsl:text> if(entry == undefined) |
|
11615 </xsl:text> |
|
11616 <xsl:text> return 0; |
|
11617 </xsl:text> |
|
11618 <xsl:text> let [_ign, period] = entry; |
|
11619 </xsl:text> |
|
11620 <xsl:text> return period; |
|
11621 </xsl:text> |
|
11622 <xsl:text>} |
|
11623 </xsl:text> |
|
11624 <xsl:text> |
|
11625 </xsl:text> |
|
11626 <xsl:text>function set_subscription_period(index, period) { |
|
11627 </xsl:text> |
|
11628 <xsl:text> let entry = subscriptions[index]; |
|
11629 </xsl:text> |
|
11630 <xsl:text> if(entry == undefined){ |
|
11631 </xsl:text> |
|
11632 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
11633 </xsl:text> |
|
11634 <xsl:text> } else { |
|
11635 </xsl:text> |
|
11636 <xsl:text> entry[1] = period; |
|
11637 </xsl:text> |
|
11638 <xsl:text> } |
|
11639 </xsl:text> |
|
11640 <xsl:text>} |
|
11641 </xsl:text> |
|
11642 <xsl:text> |
|
11643 </xsl:text> |
|
11644 <xsl:text>if(has_watchdog){ |
|
11645 </xsl:text> |
|
11646 <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
11647 </xsl:text> |
|
11648 <xsl:text> // Since dispatch directly calls change_hmi_value, |
|
11649 </xsl:text> |
|
11650 <xsl:text> // PLC will periodically send variable at given frequency |
|
11651 </xsl:text> |
|
11652 <xsl:text> subscribers(heartbeat_index).add({ |
|
11653 </xsl:text> |
|
11654 <xsl:text> /* type: "Watchdog", */ |
|
11655 </xsl:text> |
|
11656 <xsl:text> frequency: 1, |
|
11657 </xsl:text> |
|
11658 <xsl:text> indexes: [heartbeat_index], |
|
11659 </xsl:text> |
|
11660 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11661 </xsl:text> |
|
11662 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
11663 </xsl:text> |
|
11664 <xsl:text> } |
|
11665 </xsl:text> |
|
11666 <xsl:text> }); |
|
11667 </xsl:text> |
|
11668 <xsl:text>} |
|
11669 </xsl:text> |
|
11670 <xsl:text> |
|
11671 </xsl:text> |
|
11672 <xsl:text> |
|
11673 </xsl:text> |
|
11674 <xsl:text>var page_fading = "off"; |
|
11675 </xsl:text> |
|
11676 <xsl:text>var page_fading_args = "off"; |
|
11677 </xsl:text> |
|
11678 <xsl:text>function fading_page_switch(...args){ |
|
11679 </xsl:text> |
|
11680 <xsl:text> if(page_fading == "in_progress") |
|
11681 </xsl:text> |
|
11682 <xsl:text> page_fading = "forced"; |
|
11683 </xsl:text> |
|
11684 <xsl:text> else |
|
11685 </xsl:text> |
|
11686 <xsl:text> page_fading = "pending"; |
|
11687 </xsl:text> |
|
11688 <xsl:text> page_fading_args = args; |
|
11689 </xsl:text> |
|
11690 <xsl:text> |
|
11691 </xsl:text> |
|
11692 <xsl:text> requestHMIAnimation(); |
|
11693 </xsl:text> |
|
11694 <xsl:text> |
|
11695 </xsl:text> |
|
11696 <xsl:text>} |
|
11697 </xsl:text> |
|
11698 <xsl:text>document.body.style.backgroundColor = "black"; |
|
11699 </xsl:text> |
|
11700 <xsl:text> |
|
11701 </xsl:text> |
|
11702 <xsl:text>// subscribe to per instance current page hmi variable |
|
11703 </xsl:text> |
|
11704 <xsl:text>// PLC must prefix page name with "!" for page switch to happen |
|
11705 </xsl:text> |
|
11706 <xsl:text>subscribers(current_page_var_index).add({ |
|
11707 </xsl:text> |
|
11708 <xsl:text> frequency: 1, |
|
11709 </xsl:text> |
|
11710 <xsl:text> indexes: [current_page_var_index], |
|
11711 </xsl:text> |
|
11712 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11713 </xsl:text> |
|
11714 <xsl:text> if(value.startsWith("!")) |
|
11715 </xsl:text> |
|
11716 <xsl:text> fading_page_switch(value.slice(1)); |
|
11717 </xsl:text> |
|
11718 <xsl:text> } |
|
11719 </xsl:text> |
|
11720 <xsl:text>}); |
|
11721 </xsl:text> |
|
11722 <xsl:text> |
|
11723 </xsl:text> |
|
11724 <xsl:text>function svg_text_to_multiline(elt) { |
|
11725 </xsl:text> |
|
11726 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
|
11727 </xsl:text> |
|
11728 <xsl:text>} |
|
11729 </xsl:text> |
|
11730 <xsl:text> |
|
11731 </xsl:text> |
|
11732 <xsl:text>function multiline_to_svg_text(elt, str, blank) { |
|
11733 </xsl:text> |
|
11734 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); |
|
11735 </xsl:text> |
|
11736 <xsl:text>} |
|
11737 </xsl:text> |
|
11738 <xsl:text> |
|
11739 </xsl:text> |
|
11740 <xsl:text>function switch_langnum(langnum) { |
|
11741 </xsl:text> |
|
11742 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
|
11743 </xsl:text> |
|
11744 <xsl:text> |
|
11745 </xsl:text> |
|
11746 <xsl:text> for (let translation of translations) { |
|
11747 </xsl:text> |
|
11748 <xsl:text> let [objs, msgs] = translation; |
|
11749 </xsl:text> |
|
11750 <xsl:text> let msg = msgs[langnum]; |
|
11751 </xsl:text> |
|
11752 <xsl:text> for (let obj of objs) { |
|
11753 </xsl:text> |
|
11754 <xsl:text> multiline_to_svg_text(obj, msg); |
|
11755 </xsl:text> |
|
11756 <xsl:text> obj.setAttribute("lang",langnum); |
|
11757 </xsl:text> |
|
11758 <xsl:text> } |
|
11759 </xsl:text> |
|
11760 <xsl:text> } |
|
11761 </xsl:text> |
|
11762 <xsl:text> return langnum; |
|
11763 </xsl:text> |
|
11764 <xsl:text>} |
|
11765 </xsl:text> |
|
11766 <xsl:text> |
|
11767 </xsl:text> |
|
11768 <xsl:text>// backup original texts |
|
11769 </xsl:text> |
|
11770 <xsl:text>for (let translation of translations) { |
|
11771 </xsl:text> |
|
11772 <xsl:text> let [objs, msgs] = translation; |
|
11773 </xsl:text> |
|
11774 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
11775 </xsl:text> |
|
11776 <xsl:text>} |
|
11777 </xsl:text> |
|
11778 <xsl:text> |
|
11779 </xsl:text> |
|
11780 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
11781 </xsl:text> |
|
11782 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
11783 </xsl:text> |
|
11784 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
11785 </xsl:text> |
|
11786 <xsl:text>subscribers(lang_local_index).add({ |
|
11787 </xsl:text> |
|
11788 <xsl:text> indexes: [lang_local_index], |
|
11789 </xsl:text> |
|
11790 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11791 </xsl:text> |
|
11792 <xsl:text> let current_lang = switch_langnum(value); |
|
11793 </xsl:text> |
|
11794 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
11795 </xsl:text> |
|
11796 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
11797 </xsl:text> |
|
11798 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
11799 </xsl:text> |
|
11800 <xsl:text> switch_page(); |
|
11801 </xsl:text> |
|
11802 <xsl:text> } |
|
11803 </xsl:text> |
|
11804 <xsl:text>}); |
|
11805 </xsl:text> |
|
11806 <xsl:text> |
|
11807 </xsl:text> |
|
11808 <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language |
|
11809 </xsl:text> |
|
11810 <xsl:text>function get_current_lang_code(){ |
|
11811 </xsl:text> |
|
11812 <xsl:text> return cache[langcode_local_index]; |
|
11813 </xsl:text> |
|
11814 <xsl:text>} |
|
11815 </xsl:text> |
|
11816 <xsl:text> |
|
11817 </xsl:text> |
|
11818 <xsl:text>function setup_lang(){ |
|
11819 </xsl:text> |
|
11820 <xsl:text> let current_lang = cache[lang_local_index]; |
|
11821 </xsl:text> |
|
11822 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
11823 </xsl:text> |
|
11824 <xsl:text> if(current_lang != new_lang){ |
|
11825 </xsl:text> |
|
11826 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
11827 </xsl:text> |
|
11828 <xsl:text> } |
|
11829 </xsl:text> |
|
11830 <xsl:text>} |
|
11831 </xsl:text> |
|
11832 <xsl:text> |
|
11833 </xsl:text> |
|
11834 <xsl:text>setup_lang(); |
|
11835 </xsl:text> |
|
11836 <xsl:text> |
|
11837 </xsl:text> |
|
11838 <xsl:text>function update_subscriptions() { |
|
11839 </xsl:text> |
|
11840 <xsl:text> let delta = []; |
|
11841 </xsl:text> |
|
11842 <xsl:text> for(let index in subscriptions){ |
|
11843 </xsl:text> |
|
11844 <xsl:text> let widgets = subscribers(index); |
|
11845 </xsl:text> |
|
11846 <xsl:text> |
|
11847 </xsl:text> |
|
11848 <xsl:text> // periods are in ms |
|
11849 </xsl:text> |
|
11850 <xsl:text> let previous_period = get_subscription_period(index); |
|
11851 </xsl:text> |
|
11852 <xsl:text> |
|
11853 </xsl:text> |
|
11854 <xsl:text> // subscribing with a zero period is unsubscribing |
|
11855 </xsl:text> |
|
11856 <xsl:text> let new_period = 0; |
|
11857 </xsl:text> |
|
11858 <xsl:text> if(widgets.size > 0) { |
|
11859 </xsl:text> |
|
11860 <xsl:text> let maxfreq = 0; |
|
11861 </xsl:text> |
|
11862 <xsl:text> for(let widget of widgets){ |
|
11863 </xsl:text> |
|
11864 <xsl:text> let wf = widget.frequency; |
|
11865 </xsl:text> |
|
11866 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
11867 </xsl:text> |
|
11868 <xsl:text> maxfreq = wf; |
|
11869 </xsl:text> |
|
11870 <xsl:text> } |
|
11871 </xsl:text> |
|
11872 <xsl:text> |
|
11873 </xsl:text> |
|
11874 <xsl:text> if(maxfreq != 0) |
|
11875 </xsl:text> |
|
11876 <xsl:text> new_period = 1000/maxfreq; |
|
11877 </xsl:text> |
|
11878 <xsl:text> } |
|
11879 </xsl:text> |
|
11880 <xsl:text> |
|
11881 </xsl:text> |
|
11882 <xsl:text> if(previous_period != new_period) { |
|
11883 </xsl:text> |
|
11884 <xsl:text> set_subscription_period(index, new_period); |
|
11885 </xsl:text> |
|
11886 <xsl:text> if(index <= last_remote_index){ |
|
11887 </xsl:text> |
|
11888 <xsl:text> delta.push( |
|
11889 </xsl:text> |
|
11890 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
11891 </xsl:text> |
|
11892 <xsl:text> new Uint32Array([index]), |
|
11893 </xsl:text> |
|
11894 <xsl:text> new Uint16Array([new_period])); |
|
11895 </xsl:text> |
|
11896 <xsl:text> } |
|
11897 </xsl:text> |
|
11898 <xsl:text> } |
|
11899 </xsl:text> |
|
11900 <xsl:text> } |
|
11901 </xsl:text> |
|
11902 <xsl:text> send_blob(delta); |
|
11903 </xsl:text> |
|
11904 <xsl:text>}; |
|
11905 </xsl:text> |
|
11906 <xsl:text> |
|
11907 </xsl:text> |
|
11908 <xsl:text>function send_hmi_value(index, value) { |
|
11909 </xsl:text> |
|
11910 <xsl:text> if(index > last_remote_index){ |
11099 </xsl:text> |
11911 </xsl:text> |
11100 <xsl:text> dispatch_value(index, value); |
11912 <xsl:text> dispatch_value(index, value); |
11101 </xsl:text> |
11913 </xsl:text> |
|
11914 <xsl:text> |
|
11915 </xsl:text> |
|
11916 <xsl:text> if(persistent_indexes.has(index)){ |
|
11917 </xsl:text> |
|
11918 <xsl:text> let varname = persistent_indexes.get(index); |
|
11919 </xsl:text> |
|
11920 <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; |
|
11921 </xsl:text> |
|
11922 <xsl:text> } |
|
11923 </xsl:text> |
|
11924 <xsl:text> |
|
11925 </xsl:text> |
|
11926 <xsl:text> return; |
|
11927 </xsl:text> |
|
11928 <xsl:text> } |
|
11929 </xsl:text> |
|
11930 <xsl:text> |
|
11931 </xsl:text> |
|
11932 <xsl:text> let iectype = hmitree_types[index]; |
|
11933 </xsl:text> |
|
11934 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
11935 </xsl:text> |
|
11936 <xsl:text> send_blob([ |
|
11937 </xsl:text> |
|
11938 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
11939 </xsl:text> |
|
11940 <xsl:text> new Uint32Array([index]), |
|
11941 </xsl:text> |
|
11942 <xsl:text> tobinary(value)]); |
|
11943 </xsl:text> |
|
11944 <xsl:text> |
|
11945 </xsl:text> |
|
11946 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
11947 </xsl:text> |
|
11948 <xsl:text> // cache[index] = value; |
|
11949 </xsl:text> |
|
11950 <xsl:text>}; |
|
11951 </xsl:text> |
|
11952 <xsl:text> |
|
11953 </xsl:text> |
|
11954 <xsl:text>function apply_hmi_value(index, new_val) { |
|
11955 </xsl:text> |
|
11956 <xsl:text> // Similarly to previous comment, taking decision to update based |
|
11957 </xsl:text> |
|
11958 <xsl:text> // on cache content is bad and can lead to inconsistency |
|
11959 </xsl:text> |
|
11960 <xsl:text> /*let old_val = cache[index];*/ |
|
11961 </xsl:text> |
|
11962 <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) |
|
11963 </xsl:text> |
|
11964 <xsl:text> send_hmi_value(index, new_val); |
|
11965 </xsl:text> |
|
11966 <xsl:text> return new_val; |
|
11967 </xsl:text> |
|
11968 <xsl:text>} |
|
11969 </xsl:text> |
|
11970 <xsl:text> |
|
11971 </xsl:text> |
|
11972 <xsl:text>const quotes = {"'":null, '"':null}; |
|
11973 </xsl:text> |
|
11974 <xsl:text> |
|
11975 </xsl:text> |
|
11976 <xsl:text>function eval_operation_string(old_val, opstr) { |
|
11977 </xsl:text> |
|
11978 <xsl:text> let op = opstr[0]; |
|
11979 </xsl:text> |
|
11980 <xsl:text> let given_val; |
|
11981 </xsl:text> |
|
11982 <xsl:text> if(opstr.length < 2) |
|
11983 </xsl:text> |
|
11984 <xsl:text> return undefined; |
|
11985 </xsl:text> |
|
11986 <xsl:text> if(opstr[1] in quotes){ |
|
11987 </xsl:text> |
|
11988 <xsl:text> if(opstr.length < 3) |
|
11989 </xsl:text> |
|
11990 <xsl:text> return undefined; |
|
11991 </xsl:text> |
|
11992 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
11993 </xsl:text> |
|
11994 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
|
11995 </xsl:text> |
|
11996 <xsl:text> } |
|
11997 </xsl:text> |
|
11998 <xsl:text> } else { |
|
11999 </xsl:text> |
|
12000 <xsl:text> given_val = Number(opstr.slice(1)); |
|
12001 </xsl:text> |
|
12002 <xsl:text> } |
|
12003 </xsl:text> |
|
12004 <xsl:text> let new_val; |
|
12005 </xsl:text> |
|
12006 <xsl:text> switch(op){ |
|
12007 </xsl:text> |
|
12008 <xsl:text> case "=": |
|
12009 </xsl:text> |
|
12010 <xsl:text> new_val = given_val; |
|
12011 </xsl:text> |
|
12012 <xsl:text> break; |
|
12013 </xsl:text> |
|
12014 <xsl:text> case "+": |
|
12015 </xsl:text> |
|
12016 <xsl:text> new_val = old_val + given_val; |
|
12017 </xsl:text> |
|
12018 <xsl:text> break; |
|
12019 </xsl:text> |
|
12020 <xsl:text> case "-": |
|
12021 </xsl:text> |
|
12022 <xsl:text> new_val = old_val - given_val; |
|
12023 </xsl:text> |
|
12024 <xsl:text> break; |
|
12025 </xsl:text> |
|
12026 <xsl:text> case "*": |
|
12027 </xsl:text> |
|
12028 <xsl:text> new_val = old_val * given_val; |
|
12029 </xsl:text> |
|
12030 <xsl:text> break; |
|
12031 </xsl:text> |
|
12032 <xsl:text> case "/": |
|
12033 </xsl:text> |
|
12034 <xsl:text> new_val = old_val / given_val; |
|
12035 </xsl:text> |
|
12036 <xsl:text> break; |
|
12037 </xsl:text> |
|
12038 <xsl:text> } |
|
12039 </xsl:text> |
|
12040 <xsl:text> return new_val; |
|
12041 </xsl:text> |
|
12042 <xsl:text>} |
|
12043 </xsl:text> |
|
12044 <xsl:text> |
|
12045 </xsl:text> |
|
12046 <xsl:text>var current_visible_page; |
|
12047 </xsl:text> |
|
12048 <xsl:text>var current_subscribed_page; |
|
12049 </xsl:text> |
|
12050 <xsl:text>var current_page_index; |
|
12051 </xsl:text> |
|
12052 <xsl:text>var page_node_local_index = hmi_local_index("page_node"); |
|
12053 </xsl:text> |
|
12054 <xsl:text>var page_switch_in_progress = false; |
|
12055 </xsl:text> |
|
12056 <xsl:text> |
|
12057 </xsl:text> |
|
12058 <xsl:text>function toggleFullscreen() { |
|
12059 </xsl:text> |
|
12060 <xsl:text> let elem = document.documentElement; |
|
12061 </xsl:text> |
|
12062 <xsl:text> |
|
12063 </xsl:text> |
|
12064 <xsl:text> if (!document.fullscreenElement) { |
|
12065 </xsl:text> |
|
12066 <xsl:text> elem.requestFullscreen().catch(err => { |
|
12067 </xsl:text> |
|
12068 <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); |
|
12069 </xsl:text> |
11102 <xsl:text> }); |
12070 <xsl:text> }); |
11103 </xsl:text> |
12071 </xsl:text> |
11104 <xsl:text> updates.clear(); |
12072 <xsl:text> } else { |
|
12073 </xsl:text> |
|
12074 <xsl:text> document.exitFullscreen(); |
|
12075 </xsl:text> |
|
12076 <xsl:text> } |
11105 </xsl:text> |
12077 </xsl:text> |
11106 <xsl:text>} |
12078 <xsl:text>} |
11107 </xsl:text> |
12079 </xsl:text> |
11108 <xsl:text> |
12080 <xsl:text> |
11109 </xsl:text> |
12081 </xsl:text> |
11110 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
12082 <xsl:text>function prepare_svg() { |
11111 </xsl:text> |
12083 </xsl:text> |
11112 <xsl:text>var requestAnimationFrameID = null; |
12084 <xsl:text> // prevents context menu from appearing on right click and long touch |
11113 </xsl:text> |
12085 </xsl:text> |
11114 <xsl:text>function animate() { |
12086 <xsl:text> document.body.addEventListener('contextmenu', e => { |
11115 </xsl:text> |
12087 </xsl:text> |
11116 <xsl:text> let rearm = true; |
12088 <xsl:text> toggleFullscreen(); |
11117 </xsl:text> |
12089 </xsl:text> |
11118 <xsl:text> do{ |
12090 <xsl:text> e.preventDefault(); |
11119 </xsl:text> |
12091 </xsl:text> |
11120 <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ |
12092 <xsl:text> }); |
11121 </xsl:text> |
12093 </xsl:text> |
11122 <xsl:text> if(page_fading == "pending") |
12094 <xsl:text> |
11123 </xsl:text> |
12095 </xsl:text> |
11124 <xsl:text> svg_root.classList.add("fade-out-page"); |
12096 <xsl:text> for(let eltid in detachable_elements){ |
11125 </xsl:text> |
12097 </xsl:text> |
11126 <xsl:text> page_fading = "in_progress"; |
12098 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
11127 </xsl:text> |
12099 </xsl:text> |
11128 <xsl:text> if(page_fading_args.length) |
12100 <xsl:text> parent.removeChild(element); |
11129 </xsl:text> |
12101 </xsl:text> |
11130 <xsl:text> setTimeout(function(){ |
12102 <xsl:text> } |
11131 </xsl:text> |
12103 </xsl:text> |
11132 <xsl:text> switch_page(...page_fading_args); |
12104 <xsl:text>}; |
11133 </xsl:text> |
12105 </xsl:text> |
11134 <xsl:text> },1); |
12106 <xsl:text> |
11135 </xsl:text> |
12107 </xsl:text> |
11136 <xsl:text> break; |
12108 <xsl:text>function switch_page(page_name, page_index) { |
|
12109 </xsl:text> |
|
12110 <xsl:text> if(page_switch_in_progress){ |
|
12111 </xsl:text> |
|
12112 <xsl:text> /* page switch already going */ |
|
12113 </xsl:text> |
|
12114 <xsl:text> /* TODO LOG ERROR */ |
|
12115 </xsl:text> |
|
12116 <xsl:text> return false; |
|
12117 </xsl:text> |
|
12118 <xsl:text> } |
|
12119 </xsl:text> |
|
12120 <xsl:text> page_switch_in_progress = true; |
|
12121 </xsl:text> |
|
12122 <xsl:text> |
|
12123 </xsl:text> |
|
12124 <xsl:text> if(page_name == undefined) |
|
12125 </xsl:text> |
|
12126 <xsl:text> page_name = current_subscribed_page; |
|
12127 </xsl:text> |
|
12128 <xsl:text> else if(page_index == undefined){ |
|
12129 </xsl:text> |
|
12130 <xsl:text> [page_name, page_index] = page_name.split('@') |
|
12131 </xsl:text> |
|
12132 <xsl:text> } |
|
12133 </xsl:text> |
|
12134 <xsl:text> |
|
12135 </xsl:text> |
|
12136 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
12137 </xsl:text> |
|
12138 <xsl:text> let new_desc = page_desc[page_name]; |
|
12139 </xsl:text> |
|
12140 <xsl:text> |
|
12141 </xsl:text> |
|
12142 <xsl:text> if(new_desc == undefined){ |
|
12143 </xsl:text> |
|
12144 <xsl:text> /* TODO LOG ERROR */ |
|
12145 </xsl:text> |
|
12146 <xsl:text> return false; |
|
12147 </xsl:text> |
|
12148 <xsl:text> } |
|
12149 </xsl:text> |
|
12150 <xsl:text> |
|
12151 </xsl:text> |
|
12152 <xsl:text> if(page_index == undefined) |
|
12153 </xsl:text> |
|
12154 <xsl:text> page_index = new_desc.page_index; |
|
12155 </xsl:text> |
|
12156 <xsl:text> else if(typeof(page_index) == "string") { |
|
12157 </xsl:text> |
|
12158 <xsl:text> let hmitree_node = hmitree_nodes[page_index]; |
|
12159 </xsl:text> |
|
12160 <xsl:text> if(hmitree_node !== undefined){ |
|
12161 </xsl:text> |
|
12162 <xsl:text> let [int_index, hmiclass] = hmitree_node; |
|
12163 </xsl:text> |
|
12164 <xsl:text> if(hmiclass == new_desc.page_class) |
|
12165 </xsl:text> |
|
12166 <xsl:text> page_index = int_index; |
|
12167 </xsl:text> |
|
12168 <xsl:text> else |
|
12169 </xsl:text> |
|
12170 <xsl:text> page_index = new_desc.page_index; |
|
12171 </xsl:text> |
|
12172 <xsl:text> } else { |
|
12173 </xsl:text> |
|
12174 <xsl:text> page_index = new_desc.page_index; |
11137 </xsl:text> |
12175 </xsl:text> |
11138 <xsl:text> } |
12176 <xsl:text> } |
11139 </xsl:text> |
12177 </xsl:text> |
11140 <xsl:text> |
12178 <xsl:text> } |
11141 </xsl:text> |
12179 </xsl:text> |
11142 <xsl:text> // Do the page swith if pending |
12180 <xsl:text> |
11143 </xsl:text> |
12181 </xsl:text> |
11144 <xsl:text> if(page_switch_in_progress){ |
12182 <xsl:text> if(old_desc){ |
11145 </xsl:text> |
12183 </xsl:text> |
11146 <xsl:text> if(current_subscribed_page != current_visible_page){ |
12184 <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); |
11147 </xsl:text> |
12185 </xsl:text> |
11148 <xsl:text> switch_visible_page(current_subscribed_page); |
12186 <xsl:text> } |
|
12187 </xsl:text> |
|
12188 <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
12189 </xsl:text> |
|
12190 <xsl:text> |
|
12191 </xsl:text> |
|
12192 <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); |
|
12193 </xsl:text> |
|
12194 <xsl:text> |
|
12195 </xsl:text> |
|
12196 <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); |
|
12197 </xsl:text> |
|
12198 <xsl:text> |
|
12199 </xsl:text> |
|
12200 <xsl:text> update_subscriptions(); |
|
12201 </xsl:text> |
|
12202 <xsl:text> |
|
12203 </xsl:text> |
|
12204 <xsl:text> current_subscribed_page = page_name; |
|
12205 </xsl:text> |
|
12206 <xsl:text> current_page_index = page_index; |
|
12207 </xsl:text> |
|
12208 <xsl:text> let page_node; |
|
12209 </xsl:text> |
|
12210 <xsl:text> if(page_index != undefined){ |
|
12211 </xsl:text> |
|
12212 <xsl:text> page_node = hmitree_paths[page_index]; |
|
12213 </xsl:text> |
|
12214 <xsl:text> }else{ |
|
12215 </xsl:text> |
|
12216 <xsl:text> page_node = ""; |
|
12217 </xsl:text> |
|
12218 <xsl:text> } |
|
12219 </xsl:text> |
|
12220 <xsl:text> apply_hmi_value(page_node_local_index, page_node); |
|
12221 </xsl:text> |
|
12222 <xsl:text> |
|
12223 </xsl:text> |
|
12224 <xsl:text> jumps_need_update = true; |
|
12225 </xsl:text> |
|
12226 <xsl:text> |
|
12227 </xsl:text> |
|
12228 <xsl:text> requestHMIAnimation(); |
|
12229 </xsl:text> |
|
12230 <xsl:text> jump_history.push([page_name, page_index]); |
|
12231 </xsl:text> |
|
12232 <xsl:text> if(jump_history.length > 42) |
|
12233 </xsl:text> |
|
12234 <xsl:text> jump_history.shift(); |
|
12235 </xsl:text> |
|
12236 <xsl:text> |
|
12237 </xsl:text> |
|
12238 <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined |
|
12239 </xsl:text> |
|
12240 <xsl:text> ? page_name |
|
12241 </xsl:text> |
|
12242 <xsl:text> : page_name + "@" + hmitree_paths[page_index]); |
|
12243 </xsl:text> |
|
12244 <xsl:text> |
|
12245 </xsl:text> |
|
12246 <xsl:text> return true; |
|
12247 </xsl:text> |
|
12248 <xsl:text>}; |
|
12249 </xsl:text> |
|
12250 <xsl:text> |
|
12251 </xsl:text> |
|
12252 <xsl:text>function switch_visible_page(page_name) { |
|
12253 </xsl:text> |
|
12254 <xsl:text> |
|
12255 </xsl:text> |
|
12256 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
12257 </xsl:text> |
|
12258 <xsl:text> let new_desc = page_desc[page_name]; |
|
12259 </xsl:text> |
|
12260 <xsl:text> |
|
12261 </xsl:text> |
|
12262 <xsl:text> if(old_desc){ |
|
12263 </xsl:text> |
|
12264 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
12265 </xsl:text> |
|
12266 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
12267 </xsl:text> |
|
12268 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
12269 </xsl:text> |
|
12270 <xsl:text> parent.removeChild(element); |
11149 </xsl:text> |
12271 </xsl:text> |
11150 <xsl:text> } |
12272 <xsl:text> } |
11151 </xsl:text> |
12273 </xsl:text> |
11152 <xsl:text> |
12274 <xsl:text> } |
11153 </xsl:text> |
12275 </xsl:text> |
11154 <xsl:text> page_switch_in_progress = false; |
12276 <xsl:text> for(let eltid in new_desc.required_detachables){ |
11155 </xsl:text> |
12277 </xsl:text> |
11156 <xsl:text> |
12278 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
11157 </xsl:text> |
12279 </xsl:text> |
11158 <xsl:text> if(page_fading == "in_progress"){ |
12280 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
11159 </xsl:text> |
12281 </xsl:text> |
11160 <xsl:text> svg_root.classList.remove("fade-out-page"); |
12282 <xsl:text> parent.appendChild(element); |
11161 </xsl:text> |
|
11162 <xsl:text> page_fading = "off"; |
|
11163 </xsl:text> |
12283 </xsl:text> |
11164 <xsl:text> } |
12284 <xsl:text> } |
11165 </xsl:text> |
12285 </xsl:text> |
11166 <xsl:text> } |
12286 <xsl:text> } |
11167 </xsl:text> |
12287 </xsl:text> |
11168 <xsl:text> |
12288 <xsl:text> }else{ |
11169 </xsl:text> |
12289 </xsl:text> |
11170 <xsl:text> if(jumps_need_update) update_jumps(); |
12290 <xsl:text> for(let eltid in new_desc.required_detachables){ |
11171 </xsl:text> |
12291 </xsl:text> |
11172 <xsl:text> |
12292 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
11173 </xsl:text> |
12293 </xsl:text> |
11174 <xsl:text> |
12294 <xsl:text> parent.appendChild(element); |
11175 </xsl:text> |
12295 </xsl:text> |
11176 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
12296 <xsl:text> } |
11177 </xsl:text> |
12297 </xsl:text> |
11178 <xsl:text> pending_widget_animates = []; |
12298 <xsl:text> } |
11179 </xsl:text> |
12299 </xsl:text> |
11180 <xsl:text> rearm = false; |
12300 <xsl:text> |
11181 </xsl:text> |
12301 </xsl:text> |
11182 <xsl:text> } while(0); |
12302 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
11183 </xsl:text> |
12303 </xsl:text> |
11184 <xsl:text> |
12304 <xsl:text> current_visible_page = page_name; |
11185 </xsl:text> |
12305 </xsl:text> |
11186 <xsl:text> requestAnimationFrameID = null; |
12306 <xsl:text>}; |
11187 </xsl:text> |
12307 </xsl:text> |
11188 <xsl:text> |
12308 <xsl:text> |
11189 </xsl:text> |
12309 </xsl:text> |
11190 <xsl:text> if(rearm) requestHMIAnimation(); |
12310 <xsl:text>/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ |
|
12311 </xsl:text> |
|
12312 <xsl:text>function getAbsoluteCTM(element){ |
|
12313 </xsl:text> |
|
12314 <xsl:text> var height = svg_root.height.baseVal.value, |
|
12315 </xsl:text> |
|
12316 <xsl:text> width = svg_root.width.baseVal.value, |
|
12317 </xsl:text> |
|
12318 <xsl:text> viewBoxRect = svg_root.viewBox.baseVal, |
|
12319 </xsl:text> |
|
12320 <xsl:text> vHeight = viewBoxRect.height, |
|
12321 </xsl:text> |
|
12322 <xsl:text> vWidth = viewBoxRect.width; |
|
12323 </xsl:text> |
|
12324 <xsl:text> if(!vWidth || !vHeight){ |
|
12325 </xsl:text> |
|
12326 <xsl:text> return element.getCTM(); |
|
12327 </xsl:text> |
|
12328 <xsl:text> } |
|
12329 </xsl:text> |
|
12330 <xsl:text> var sH = height/vHeight, |
|
12331 </xsl:text> |
|
12332 <xsl:text> sW = width/vWidth, |
|
12333 </xsl:text> |
|
12334 <xsl:text> matrix = svg_root.createSVGMatrix(); |
|
12335 </xsl:text> |
|
12336 <xsl:text> matrix.a = sW; |
|
12337 </xsl:text> |
|
12338 <xsl:text> matrix.d = sH |
|
12339 </xsl:text> |
|
12340 <xsl:text> var realCTM = element.getCTM().multiply(matrix.inverse()); |
|
12341 </xsl:text> |
|
12342 <xsl:text> realCTM.e = realCTM.e/sW + viewBoxRect.x; |
|
12343 </xsl:text> |
|
12344 <xsl:text> realCTM.f = realCTM.f/sH + viewBoxRect.y; |
|
12345 </xsl:text> |
|
12346 <xsl:text> return realCTM; |
11191 </xsl:text> |
12347 </xsl:text> |
11192 <xsl:text>} |
12348 <xsl:text>} |
11193 </xsl:text> |
12349 </xsl:text> |
11194 <xsl:text> |
12350 <xsl:text> |
11195 </xsl:text> |
12351 </xsl:text> |
11196 <xsl:text>function requestHMIAnimation() { |
12352 <xsl:text>function apply_reference_frames(){ |
11197 </xsl:text> |
12353 </xsl:text> |
11198 <xsl:text> if(requestAnimationFrameID == null){ |
12354 <xsl:text> const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); |
11199 </xsl:text> |
12355 </xsl:text> |
11200 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
12356 <xsl:text> matches.forEach((group) => { |
11201 </xsl:text> |
12357 </xsl:text> |
11202 <xsl:text> } |
12358 <xsl:text> let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); |
|
12359 </xsl:text> |
|
12360 <xsl:text> let ctm = getAbsoluteCTM(group); |
|
12361 </xsl:text> |
|
12362 <xsl:text> // zero translation part of CTM |
|
12363 </xsl:text> |
|
12364 <xsl:text> // to only apply rotation/skewing to offset vector |
|
12365 </xsl:text> |
|
12366 <xsl:text> ctm.e = 0; |
|
12367 </xsl:text> |
|
12368 <xsl:text> ctm.f = 0; |
|
12369 </xsl:text> |
|
12370 <xsl:text> let invctm = ctm.inverse(); |
|
12371 </xsl:text> |
|
12372 <xsl:text> let vect = new DOMPoint(x, y); |
|
12373 </xsl:text> |
|
12374 <xsl:text> let newvect = vect.matrixTransform(invctm); |
|
12375 </xsl:text> |
|
12376 <xsl:text> let transform = svg_root.createSVGTransform(); |
|
12377 </xsl:text> |
|
12378 <xsl:text> transform.setTranslate(newvect.x, newvect.y); |
|
12379 </xsl:text> |
|
12380 <xsl:text> group.transform.baseVal.appendItem(transform); |
|
12381 </xsl:text> |
|
12382 <xsl:text> ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); |
|
12383 </xsl:text> |
|
12384 <xsl:text> }); |
11203 </xsl:text> |
12385 </xsl:text> |
11204 <xsl:text>} |
12386 <xsl:text>} |
11205 </xsl:text> |
12387 </xsl:text> |
11206 <xsl:text> |
12388 <xsl:text> |
11207 </xsl:text> |
12389 </xsl:text> |
11208 <xsl:text>// Message reception handler |
|
11209 </xsl:text> |
|
11210 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
11211 </xsl:text> |
|
11212 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
11213 </xsl:text> |
|
11214 <xsl:text>ws.onmessage = function (evt) { |
|
11215 </xsl:text> |
|
11216 <xsl:text> |
|
11217 </xsl:text> |
|
11218 <xsl:text> let data = evt.data; |
|
11219 </xsl:text> |
|
11220 <xsl:text> let dv = new DataView(data); |
|
11221 </xsl:text> |
|
11222 <xsl:text> let i = 0; |
|
11223 </xsl:text> |
|
11224 <xsl:text> try { |
|
11225 </xsl:text> |
|
11226 <xsl:text> for(let hash_int of hmi_hash) { |
|
11227 </xsl:text> |
|
11228 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
11229 </xsl:text> |
|
11230 <xsl:text> throw new Error("Hash doesn't match"); |
|
11231 </xsl:text> |
|
11232 <xsl:text> }; |
|
11233 </xsl:text> |
|
11234 <xsl:text> i++; |
|
11235 </xsl:text> |
|
11236 <xsl:text> }; |
|
11237 </xsl:text> |
|
11238 <xsl:text> |
|
11239 </xsl:text> |
|
11240 <xsl:text> while(i < data.byteLength){ |
|
11241 </xsl:text> |
|
11242 <xsl:text> let index = dv.getUint32(i, true); |
|
11243 </xsl:text> |
|
11244 <xsl:text> i += 4; |
|
11245 </xsl:text> |
|
11246 <xsl:text> let iectype = hmitree_types[index]; |
|
11247 </xsl:text> |
|
11248 <xsl:text> if(iectype != undefined){ |
|
11249 </xsl:text> |
|
11250 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
11251 </xsl:text> |
|
11252 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
11253 </xsl:text> |
|
11254 <xsl:text> updates.set(index, value); |
|
11255 </xsl:text> |
|
11256 <xsl:text> i += bytesize; |
|
11257 </xsl:text> |
|
11258 <xsl:text> } else { |
|
11259 </xsl:text> |
|
11260 <xsl:text> throw new Error("Unknown index "+index); |
|
11261 </xsl:text> |
|
11262 <xsl:text> } |
|
11263 </xsl:text> |
|
11264 <xsl:text> }; |
|
11265 </xsl:text> |
|
11266 <xsl:text> |
|
11267 </xsl:text> |
|
11268 <xsl:text> apply_updates(); |
|
11269 </xsl:text> |
|
11270 <xsl:text> // register for rendering on next frame, since there are updates |
|
11271 </xsl:text> |
|
11272 <xsl:text> } catch(err) { |
|
11273 </xsl:text> |
|
11274 <xsl:text> // 1003 is for "Unsupported Data" |
|
11275 </xsl:text> |
|
11276 <xsl:text> // ws.close(1003, err.message); |
|
11277 </xsl:text> |
|
11278 <xsl:text> |
|
11279 </xsl:text> |
|
11280 <xsl:text> // TODO : remove debug alert ? |
|
11281 </xsl:text> |
|
11282 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
11283 </xsl:text> |
|
11284 <xsl:text> |
|
11285 </xsl:text> |
|
11286 <xsl:text> // force reload ignoring cache |
|
11287 </xsl:text> |
|
11288 <xsl:text> location.reload(true); |
|
11289 </xsl:text> |
|
11290 <xsl:text> } |
|
11291 </xsl:text> |
|
11292 <xsl:text>}; |
|
11293 </xsl:text> |
|
11294 <xsl:text> |
|
11295 </xsl:text> |
|
11296 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
|
11297 </xsl:text> |
|
11298 <xsl:text> |
|
11299 </xsl:text> |
|
11300 <xsl:text>function send_blob(data) { |
|
11301 </xsl:text> |
|
11302 <xsl:text> if(data.length > 0) { |
|
11303 </xsl:text> |
|
11304 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
|
11305 </xsl:text> |
|
11306 <xsl:text> }; |
|
11307 </xsl:text> |
|
11308 <xsl:text>}; |
|
11309 </xsl:text> |
|
11310 <xsl:text> |
|
11311 </xsl:text> |
|
11312 <xsl:text>const typedarray_types = { |
|
11313 </xsl:text> |
|
11314 <xsl:text> INT: (number) => new Int16Array([number]), |
|
11315 </xsl:text> |
|
11316 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
11317 </xsl:text> |
|
11318 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
11319 </xsl:text> |
|
11320 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
11321 </xsl:text> |
|
11322 <xsl:text> STRING: (str) => { |
|
11323 </xsl:text> |
|
11324 <xsl:text> // beremiz default string max size is 128 |
|
11325 </xsl:text> |
|
11326 <xsl:text> str = str.slice(0,128); |
|
11327 </xsl:text> |
|
11328 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
11329 </xsl:text> |
|
11330 <xsl:text> binary[0] = str.length; |
|
11331 </xsl:text> |
|
11332 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
11333 </xsl:text> |
|
11334 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
11335 </xsl:text> |
|
11336 <xsl:text> } |
|
11337 </xsl:text> |
|
11338 <xsl:text> return binary; |
|
11339 </xsl:text> |
|
11340 <xsl:text> } |
|
11341 </xsl:text> |
|
11342 <xsl:text> /* TODO */ |
|
11343 </xsl:text> |
|
11344 <xsl:text>}; |
|
11345 </xsl:text> |
|
11346 <xsl:text> |
|
11347 </xsl:text> |
|
11348 <xsl:text>function send_reset() { |
|
11349 </xsl:text> |
|
11350 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
11351 </xsl:text> |
|
11352 <xsl:text>}; |
|
11353 </xsl:text> |
|
11354 <xsl:text> |
|
11355 </xsl:text> |
|
11356 <xsl:text>var subscriptions = []; |
|
11357 </xsl:text> |
|
11358 <xsl:text> |
|
11359 </xsl:text> |
|
11360 <xsl:text>function subscribers(index) { |
|
11361 </xsl:text> |
|
11362 <xsl:text> let entry = subscriptions[index]; |
|
11363 </xsl:text> |
|
11364 <xsl:text> let res; |
|
11365 </xsl:text> |
|
11366 <xsl:text> if(entry == undefined){ |
|
11367 </xsl:text> |
|
11368 <xsl:text> res = new Set(); |
|
11369 </xsl:text> |
|
11370 <xsl:text> subscriptions[index] = [res,0]; |
|
11371 </xsl:text> |
|
11372 <xsl:text> }else{ |
|
11373 </xsl:text> |
|
11374 <xsl:text> [res, _ign] = entry; |
|
11375 </xsl:text> |
|
11376 <xsl:text> } |
|
11377 </xsl:text> |
|
11378 <xsl:text> return res |
|
11379 </xsl:text> |
|
11380 <xsl:text>} |
|
11381 </xsl:text> |
|
11382 <xsl:text> |
|
11383 </xsl:text> |
|
11384 <xsl:text>function get_subscription_period(index) { |
|
11385 </xsl:text> |
|
11386 <xsl:text> let entry = subscriptions[index]; |
|
11387 </xsl:text> |
|
11388 <xsl:text> if(entry == undefined) |
|
11389 </xsl:text> |
|
11390 <xsl:text> return 0; |
|
11391 </xsl:text> |
|
11392 <xsl:text> let [_ign, period] = entry; |
|
11393 </xsl:text> |
|
11394 <xsl:text> return period; |
|
11395 </xsl:text> |
|
11396 <xsl:text>} |
|
11397 </xsl:text> |
|
11398 <xsl:text> |
|
11399 </xsl:text> |
|
11400 <xsl:text>function set_subscription_period(index, period) { |
|
11401 </xsl:text> |
|
11402 <xsl:text> let entry = subscriptions[index]; |
|
11403 </xsl:text> |
|
11404 <xsl:text> if(entry == undefined){ |
|
11405 </xsl:text> |
|
11406 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
11407 </xsl:text> |
|
11408 <xsl:text> } else { |
|
11409 </xsl:text> |
|
11410 <xsl:text> entry[1] = period; |
|
11411 </xsl:text> |
|
11412 <xsl:text> } |
|
11413 </xsl:text> |
|
11414 <xsl:text>} |
|
11415 </xsl:text> |
|
11416 <xsl:text> |
|
11417 </xsl:text> |
|
11418 <xsl:text>if(has_watchdog){ |
|
11419 </xsl:text> |
|
11420 <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
11421 </xsl:text> |
|
11422 <xsl:text> // Since dispatch directly calls change_hmi_value, |
|
11423 </xsl:text> |
|
11424 <xsl:text> // PLC will periodically send variable at given frequency |
|
11425 </xsl:text> |
|
11426 <xsl:text> subscribers(heartbeat_index).add({ |
|
11427 </xsl:text> |
|
11428 <xsl:text> /* type: "Watchdog", */ |
|
11429 </xsl:text> |
|
11430 <xsl:text> frequency: 1, |
|
11431 </xsl:text> |
|
11432 <xsl:text> indexes: [heartbeat_index], |
|
11433 </xsl:text> |
|
11434 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11435 </xsl:text> |
|
11436 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
11437 </xsl:text> |
|
11438 <xsl:text> } |
|
11439 </xsl:text> |
|
11440 <xsl:text> }); |
|
11441 </xsl:text> |
|
11442 <xsl:text>} |
|
11443 </xsl:text> |
|
11444 <xsl:text> |
|
11445 </xsl:text> |
|
11446 <xsl:text> |
|
11447 </xsl:text> |
|
11448 <xsl:text>var page_fading = "off"; |
|
11449 </xsl:text> |
|
11450 <xsl:text>var page_fading_args = "off"; |
|
11451 </xsl:text> |
|
11452 <xsl:text>function fading_page_switch(...args){ |
|
11453 </xsl:text> |
|
11454 <xsl:text> if(page_fading == "in_progress") |
|
11455 </xsl:text> |
|
11456 <xsl:text> page_fading = "forced"; |
|
11457 </xsl:text> |
|
11458 <xsl:text> else |
|
11459 </xsl:text> |
|
11460 <xsl:text> page_fading = "pending"; |
|
11461 </xsl:text> |
|
11462 <xsl:text> page_fading_args = args; |
|
11463 </xsl:text> |
|
11464 <xsl:text> |
|
11465 </xsl:text> |
|
11466 <xsl:text> requestHMIAnimation(); |
|
11467 </xsl:text> |
|
11468 <xsl:text> |
|
11469 </xsl:text> |
|
11470 <xsl:text>} |
|
11471 </xsl:text> |
|
11472 <xsl:text>document.body.style.backgroundColor = "black"; |
|
11473 </xsl:text> |
|
11474 <xsl:text> |
|
11475 </xsl:text> |
|
11476 <xsl:text>// subscribe to per instance current page hmi variable |
|
11477 </xsl:text> |
|
11478 <xsl:text>// PLC must prefix page name with "!" for page switch to happen |
|
11479 </xsl:text> |
|
11480 <xsl:text>subscribers(current_page_var_index).add({ |
|
11481 </xsl:text> |
|
11482 <xsl:text> frequency: 1, |
|
11483 </xsl:text> |
|
11484 <xsl:text> indexes: [current_page_var_index], |
|
11485 </xsl:text> |
|
11486 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11487 </xsl:text> |
|
11488 <xsl:text> if(value.startsWith("!")) |
|
11489 </xsl:text> |
|
11490 <xsl:text> fading_page_switch(value.slice(1)); |
|
11491 </xsl:text> |
|
11492 <xsl:text> } |
|
11493 </xsl:text> |
|
11494 <xsl:text>}); |
|
11495 </xsl:text> |
|
11496 <xsl:text> |
|
11497 </xsl:text> |
|
11498 <xsl:text>function svg_text_to_multiline(elt) { |
|
11499 </xsl:text> |
|
11500 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
|
11501 </xsl:text> |
|
11502 <xsl:text>} |
|
11503 </xsl:text> |
|
11504 <xsl:text> |
|
11505 </xsl:text> |
|
11506 <xsl:text>function multiline_to_svg_text(elt, str, blank) { |
|
11507 </xsl:text> |
|
11508 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); |
|
11509 </xsl:text> |
|
11510 <xsl:text>} |
|
11511 </xsl:text> |
|
11512 <xsl:text> |
|
11513 </xsl:text> |
|
11514 <xsl:text>function switch_langnum(langnum) { |
|
11515 </xsl:text> |
|
11516 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
|
11517 </xsl:text> |
|
11518 <xsl:text> |
|
11519 </xsl:text> |
|
11520 <xsl:text> for (let translation of translations) { |
|
11521 </xsl:text> |
|
11522 <xsl:text> let [objs, msgs] = translation; |
|
11523 </xsl:text> |
|
11524 <xsl:text> let msg = msgs[langnum]; |
|
11525 </xsl:text> |
|
11526 <xsl:text> for (let obj of objs) { |
|
11527 </xsl:text> |
|
11528 <xsl:text> multiline_to_svg_text(obj, msg); |
|
11529 </xsl:text> |
|
11530 <xsl:text> obj.setAttribute("lang",langnum); |
|
11531 </xsl:text> |
|
11532 <xsl:text> } |
|
11533 </xsl:text> |
|
11534 <xsl:text> } |
|
11535 </xsl:text> |
|
11536 <xsl:text> return langnum; |
|
11537 </xsl:text> |
|
11538 <xsl:text>} |
|
11539 </xsl:text> |
|
11540 <xsl:text> |
|
11541 </xsl:text> |
|
11542 <xsl:text>// backup original texts |
|
11543 </xsl:text> |
|
11544 <xsl:text>for (let translation of translations) { |
|
11545 </xsl:text> |
|
11546 <xsl:text> let [objs, msgs] = translation; |
|
11547 </xsl:text> |
|
11548 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
11549 </xsl:text> |
|
11550 <xsl:text>} |
|
11551 </xsl:text> |
|
11552 <xsl:text> |
|
11553 </xsl:text> |
|
11554 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
11555 </xsl:text> |
|
11556 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
11557 </xsl:text> |
|
11558 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
11559 </xsl:text> |
|
11560 <xsl:text>subscribers(lang_local_index).add({ |
|
11561 </xsl:text> |
|
11562 <xsl:text> indexes: [lang_local_index], |
|
11563 </xsl:text> |
|
11564 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11565 </xsl:text> |
|
11566 <xsl:text> let current_lang = switch_langnum(value); |
|
11567 </xsl:text> |
|
11568 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
11569 </xsl:text> |
|
11570 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
11571 </xsl:text> |
|
11572 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
11573 </xsl:text> |
|
11574 <xsl:text> switch_page(); |
|
11575 </xsl:text> |
|
11576 <xsl:text> } |
|
11577 </xsl:text> |
|
11578 <xsl:text>}); |
|
11579 </xsl:text> |
|
11580 <xsl:text> |
|
11581 </xsl:text> |
|
11582 <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language |
|
11583 </xsl:text> |
|
11584 <xsl:text>function get_current_lang_code(){ |
|
11585 </xsl:text> |
|
11586 <xsl:text> return cache[langcode_local_index]; |
|
11587 </xsl:text> |
|
11588 <xsl:text>} |
|
11589 </xsl:text> |
|
11590 <xsl:text> |
|
11591 </xsl:text> |
|
11592 <xsl:text>function setup_lang(){ |
|
11593 </xsl:text> |
|
11594 <xsl:text> let current_lang = cache[lang_local_index]; |
|
11595 </xsl:text> |
|
11596 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
11597 </xsl:text> |
|
11598 <xsl:text> if(current_lang != new_lang){ |
|
11599 </xsl:text> |
|
11600 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
11601 </xsl:text> |
|
11602 <xsl:text> } |
|
11603 </xsl:text> |
|
11604 <xsl:text>} |
|
11605 </xsl:text> |
|
11606 <xsl:text> |
|
11607 </xsl:text> |
|
11608 <xsl:text>setup_lang(); |
|
11609 </xsl:text> |
|
11610 <xsl:text> |
|
11611 </xsl:text> |
|
11612 <xsl:text>function update_subscriptions() { |
|
11613 </xsl:text> |
|
11614 <xsl:text> let delta = []; |
|
11615 </xsl:text> |
|
11616 <xsl:text> for(let index in subscriptions){ |
|
11617 </xsl:text> |
|
11618 <xsl:text> let widgets = subscribers(index); |
|
11619 </xsl:text> |
|
11620 <xsl:text> |
|
11621 </xsl:text> |
|
11622 <xsl:text> // periods are in ms |
|
11623 </xsl:text> |
|
11624 <xsl:text> let previous_period = get_subscription_period(index); |
|
11625 </xsl:text> |
|
11626 <xsl:text> |
|
11627 </xsl:text> |
|
11628 <xsl:text> // subscribing with a zero period is unsubscribing |
|
11629 </xsl:text> |
|
11630 <xsl:text> let new_period = 0; |
|
11631 </xsl:text> |
|
11632 <xsl:text> if(widgets.size > 0) { |
|
11633 </xsl:text> |
|
11634 <xsl:text> let maxfreq = 0; |
|
11635 </xsl:text> |
|
11636 <xsl:text> for(let widget of widgets){ |
|
11637 </xsl:text> |
|
11638 <xsl:text> let wf = widget.frequency; |
|
11639 </xsl:text> |
|
11640 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
11641 </xsl:text> |
|
11642 <xsl:text> maxfreq = wf; |
|
11643 </xsl:text> |
|
11644 <xsl:text> } |
|
11645 </xsl:text> |
|
11646 <xsl:text> |
|
11647 </xsl:text> |
|
11648 <xsl:text> if(maxfreq != 0) |
|
11649 </xsl:text> |
|
11650 <xsl:text> new_period = 1000/maxfreq; |
|
11651 </xsl:text> |
|
11652 <xsl:text> } |
|
11653 </xsl:text> |
|
11654 <xsl:text> |
|
11655 </xsl:text> |
|
11656 <xsl:text> if(previous_period != new_period) { |
|
11657 </xsl:text> |
|
11658 <xsl:text> set_subscription_period(index, new_period); |
|
11659 </xsl:text> |
|
11660 <xsl:text> if(index <= last_remote_index){ |
|
11661 </xsl:text> |
|
11662 <xsl:text> delta.push( |
|
11663 </xsl:text> |
|
11664 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
11665 </xsl:text> |
|
11666 <xsl:text> new Uint32Array([index]), |
|
11667 </xsl:text> |
|
11668 <xsl:text> new Uint16Array([new_period])); |
|
11669 </xsl:text> |
|
11670 <xsl:text> } |
|
11671 </xsl:text> |
|
11672 <xsl:text> } |
|
11673 </xsl:text> |
|
11674 <xsl:text> } |
|
11675 </xsl:text> |
|
11676 <xsl:text> send_blob(delta); |
|
11677 </xsl:text> |
|
11678 <xsl:text>}; |
|
11679 </xsl:text> |
|
11680 <xsl:text> |
|
11681 </xsl:text> |
|
11682 <xsl:text>function send_hmi_value(index, value) { |
|
11683 </xsl:text> |
|
11684 <xsl:text> if(index > last_remote_index){ |
|
11685 </xsl:text> |
|
11686 <xsl:text> dispatch_value(index, value); |
|
11687 </xsl:text> |
|
11688 <xsl:text> |
|
11689 </xsl:text> |
|
11690 <xsl:text> if(persistent_indexes.has(index)){ |
|
11691 </xsl:text> |
|
11692 <xsl:text> let varname = persistent_indexes.get(index); |
|
11693 </xsl:text> |
|
11694 <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; |
|
11695 </xsl:text> |
|
11696 <xsl:text> } |
|
11697 </xsl:text> |
|
11698 <xsl:text> |
|
11699 </xsl:text> |
|
11700 <xsl:text> return; |
|
11701 </xsl:text> |
|
11702 <xsl:text> } |
|
11703 </xsl:text> |
|
11704 <xsl:text> |
|
11705 </xsl:text> |
|
11706 <xsl:text> let iectype = hmitree_types[index]; |
|
11707 </xsl:text> |
|
11708 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
11709 </xsl:text> |
|
11710 <xsl:text> send_blob([ |
|
11711 </xsl:text> |
|
11712 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
11713 </xsl:text> |
|
11714 <xsl:text> new Uint32Array([index]), |
|
11715 </xsl:text> |
|
11716 <xsl:text> tobinary(value)]); |
|
11717 </xsl:text> |
|
11718 <xsl:text> |
|
11719 </xsl:text> |
|
11720 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
11721 </xsl:text> |
|
11722 <xsl:text> // cache[index] = value; |
|
11723 </xsl:text> |
|
11724 <xsl:text>}; |
|
11725 </xsl:text> |
|
11726 <xsl:text> |
|
11727 </xsl:text> |
|
11728 <xsl:text>function apply_hmi_value(index, new_val) { |
|
11729 </xsl:text> |
|
11730 <xsl:text> // Similarly to previous comment, taking decision to update based |
|
11731 </xsl:text> |
|
11732 <xsl:text> // on cache content is bad and can lead to inconsistency |
|
11733 </xsl:text> |
|
11734 <xsl:text> /*let old_val = cache[index];*/ |
|
11735 </xsl:text> |
|
11736 <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) |
|
11737 </xsl:text> |
|
11738 <xsl:text> send_hmi_value(index, new_val); |
|
11739 </xsl:text> |
|
11740 <xsl:text> return new_val; |
|
11741 </xsl:text> |
|
11742 <xsl:text>} |
|
11743 </xsl:text> |
|
11744 <xsl:text> |
|
11745 </xsl:text> |
|
11746 <xsl:text>const quotes = {"'":null, '"':null}; |
|
11747 </xsl:text> |
|
11748 <xsl:text> |
|
11749 </xsl:text> |
|
11750 <xsl:text>function eval_operation_string(old_val, opstr) { |
|
11751 </xsl:text> |
|
11752 <xsl:text> let op = opstr[0]; |
|
11753 </xsl:text> |
|
11754 <xsl:text> let given_val; |
|
11755 </xsl:text> |
|
11756 <xsl:text> if(opstr.length < 2) |
|
11757 </xsl:text> |
|
11758 <xsl:text> return undefined; |
|
11759 </xsl:text> |
|
11760 <xsl:text> if(opstr[1] in quotes){ |
|
11761 </xsl:text> |
|
11762 <xsl:text> if(opstr.length < 3) |
|
11763 </xsl:text> |
|
11764 <xsl:text> return undefined; |
|
11765 </xsl:text> |
|
11766 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
11767 </xsl:text> |
|
11768 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
|
11769 </xsl:text> |
|
11770 <xsl:text> } |
|
11771 </xsl:text> |
|
11772 <xsl:text> } else { |
|
11773 </xsl:text> |
|
11774 <xsl:text> given_val = Number(opstr.slice(1)); |
|
11775 </xsl:text> |
|
11776 <xsl:text> } |
|
11777 </xsl:text> |
|
11778 <xsl:text> let new_val; |
|
11779 </xsl:text> |
|
11780 <xsl:text> switch(op){ |
|
11781 </xsl:text> |
|
11782 <xsl:text> case "=": |
|
11783 </xsl:text> |
|
11784 <xsl:text> new_val = given_val; |
|
11785 </xsl:text> |
|
11786 <xsl:text> break; |
|
11787 </xsl:text> |
|
11788 <xsl:text> case "+": |
|
11789 </xsl:text> |
|
11790 <xsl:text> new_val = old_val + given_val; |
|
11791 </xsl:text> |
|
11792 <xsl:text> break; |
|
11793 </xsl:text> |
|
11794 <xsl:text> case "-": |
|
11795 </xsl:text> |
|
11796 <xsl:text> new_val = old_val - given_val; |
|
11797 </xsl:text> |
|
11798 <xsl:text> break; |
|
11799 </xsl:text> |
|
11800 <xsl:text> case "*": |
|
11801 </xsl:text> |
|
11802 <xsl:text> new_val = old_val * given_val; |
|
11803 </xsl:text> |
|
11804 <xsl:text> break; |
|
11805 </xsl:text> |
|
11806 <xsl:text> case "/": |
|
11807 </xsl:text> |
|
11808 <xsl:text> new_val = old_val / given_val; |
|
11809 </xsl:text> |
|
11810 <xsl:text> break; |
|
11811 </xsl:text> |
|
11812 <xsl:text> } |
|
11813 </xsl:text> |
|
11814 <xsl:text> return new_val; |
|
11815 </xsl:text> |
|
11816 <xsl:text>} |
|
11817 </xsl:text> |
|
11818 <xsl:text> |
|
11819 </xsl:text> |
|
11820 <xsl:text>var current_visible_page; |
|
11821 </xsl:text> |
|
11822 <xsl:text>var current_subscribed_page; |
|
11823 </xsl:text> |
|
11824 <xsl:text>var current_page_index; |
|
11825 </xsl:text> |
|
11826 <xsl:text>var page_node_local_index = hmi_local_index("page_node"); |
|
11827 </xsl:text> |
|
11828 <xsl:text>var page_switch_in_progress = false; |
|
11829 </xsl:text> |
|
11830 <xsl:text> |
|
11831 </xsl:text> |
|
11832 <xsl:text>function toggleFullscreen() { |
|
11833 </xsl:text> |
|
11834 <xsl:text> let elem = document.documentElement; |
|
11835 </xsl:text> |
|
11836 <xsl:text> |
|
11837 </xsl:text> |
|
11838 <xsl:text> if (!document.fullscreenElement) { |
|
11839 </xsl:text> |
|
11840 <xsl:text> elem.requestFullscreen().catch(err => { |
|
11841 </xsl:text> |
|
11842 <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); |
|
11843 </xsl:text> |
|
11844 <xsl:text> }); |
|
11845 </xsl:text> |
|
11846 <xsl:text> } else { |
|
11847 </xsl:text> |
|
11848 <xsl:text> document.exitFullscreen(); |
|
11849 </xsl:text> |
|
11850 <xsl:text> } |
|
11851 </xsl:text> |
|
11852 <xsl:text>} |
|
11853 </xsl:text> |
|
11854 <xsl:text> |
|
11855 </xsl:text> |
|
11856 <xsl:text>function prepare_svg() { |
|
11857 </xsl:text> |
|
11858 <xsl:text> // prevents context menu from appearing on right click and long touch |
|
11859 </xsl:text> |
|
11860 <xsl:text> document.body.addEventListener('contextmenu', e => { |
|
11861 </xsl:text> |
|
11862 <xsl:text> toggleFullscreen(); |
|
11863 </xsl:text> |
|
11864 <xsl:text> e.preventDefault(); |
|
11865 </xsl:text> |
|
11866 <xsl:text> }); |
|
11867 </xsl:text> |
|
11868 <xsl:text> |
|
11869 </xsl:text> |
|
11870 <xsl:text> for(let eltid in detachable_elements){ |
|
11871 </xsl:text> |
|
11872 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
11873 </xsl:text> |
|
11874 <xsl:text> parent.removeChild(element); |
|
11875 </xsl:text> |
|
11876 <xsl:text> } |
|
11877 </xsl:text> |
|
11878 <xsl:text>}; |
|
11879 </xsl:text> |
|
11880 <xsl:text> |
|
11881 </xsl:text> |
|
11882 <xsl:text>function switch_page(page_name, page_index) { |
|
11883 </xsl:text> |
|
11884 <xsl:text> if(page_switch_in_progress){ |
|
11885 </xsl:text> |
|
11886 <xsl:text> /* page switch already going */ |
|
11887 </xsl:text> |
|
11888 <xsl:text> /* TODO LOG ERROR */ |
|
11889 </xsl:text> |
|
11890 <xsl:text> return false; |
|
11891 </xsl:text> |
|
11892 <xsl:text> } |
|
11893 </xsl:text> |
|
11894 <xsl:text> page_switch_in_progress = true; |
|
11895 </xsl:text> |
|
11896 <xsl:text> |
|
11897 </xsl:text> |
|
11898 <xsl:text> if(page_name == undefined) |
|
11899 </xsl:text> |
|
11900 <xsl:text> page_name = current_subscribed_page; |
|
11901 </xsl:text> |
|
11902 <xsl:text> else if(page_index == undefined){ |
|
11903 </xsl:text> |
|
11904 <xsl:text> [page_name, page_index] = page_name.split('@') |
|
11905 </xsl:text> |
|
11906 <xsl:text> } |
|
11907 </xsl:text> |
|
11908 <xsl:text> |
|
11909 </xsl:text> |
|
11910 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
11911 </xsl:text> |
|
11912 <xsl:text> let new_desc = page_desc[page_name]; |
|
11913 </xsl:text> |
|
11914 <xsl:text> |
|
11915 </xsl:text> |
|
11916 <xsl:text> if(new_desc == undefined){ |
|
11917 </xsl:text> |
|
11918 <xsl:text> /* TODO LOG ERROR */ |
|
11919 </xsl:text> |
|
11920 <xsl:text> return false; |
|
11921 </xsl:text> |
|
11922 <xsl:text> } |
|
11923 </xsl:text> |
|
11924 <xsl:text> |
|
11925 </xsl:text> |
|
11926 <xsl:text> if(page_index == undefined) |
|
11927 </xsl:text> |
|
11928 <xsl:text> page_index = new_desc.page_index; |
|
11929 </xsl:text> |
|
11930 <xsl:text> else if(typeof(page_index) == "string") { |
|
11931 </xsl:text> |
|
11932 <xsl:text> let hmitree_node = hmitree_nodes[page_index]; |
|
11933 </xsl:text> |
|
11934 <xsl:text> if(hmitree_node !== undefined){ |
|
11935 </xsl:text> |
|
11936 <xsl:text> let [int_index, hmiclass] = hmitree_node; |
|
11937 </xsl:text> |
|
11938 <xsl:text> if(hmiclass == new_desc.page_class) |
|
11939 </xsl:text> |
|
11940 <xsl:text> page_index = int_index; |
|
11941 </xsl:text> |
|
11942 <xsl:text> else |
|
11943 </xsl:text> |
|
11944 <xsl:text> page_index = new_desc.page_index; |
|
11945 </xsl:text> |
|
11946 <xsl:text> } else { |
|
11947 </xsl:text> |
|
11948 <xsl:text> page_index = new_desc.page_index; |
|
11949 </xsl:text> |
|
11950 <xsl:text> } |
|
11951 </xsl:text> |
|
11952 <xsl:text> } |
|
11953 </xsl:text> |
|
11954 <xsl:text> |
|
11955 </xsl:text> |
|
11956 <xsl:text> if(old_desc){ |
|
11957 </xsl:text> |
|
11958 <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); |
|
11959 </xsl:text> |
|
11960 <xsl:text> } |
|
11961 </xsl:text> |
|
11962 <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
11963 </xsl:text> |
|
11964 <xsl:text> |
|
11965 </xsl:text> |
|
11966 <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); |
|
11967 </xsl:text> |
|
11968 <xsl:text> |
|
11969 </xsl:text> |
|
11970 <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); |
|
11971 </xsl:text> |
|
11972 <xsl:text> |
|
11973 </xsl:text> |
|
11974 <xsl:text> update_subscriptions(); |
|
11975 </xsl:text> |
|
11976 <xsl:text> |
|
11977 </xsl:text> |
|
11978 <xsl:text> current_subscribed_page = page_name; |
|
11979 </xsl:text> |
|
11980 <xsl:text> current_page_index = page_index; |
|
11981 </xsl:text> |
|
11982 <xsl:text> let page_node; |
|
11983 </xsl:text> |
|
11984 <xsl:text> if(page_index != undefined){ |
|
11985 </xsl:text> |
|
11986 <xsl:text> page_node = hmitree_paths[page_index]; |
|
11987 </xsl:text> |
|
11988 <xsl:text> }else{ |
|
11989 </xsl:text> |
|
11990 <xsl:text> page_node = ""; |
|
11991 </xsl:text> |
|
11992 <xsl:text> } |
|
11993 </xsl:text> |
|
11994 <xsl:text> apply_hmi_value(page_node_local_index, page_node); |
|
11995 </xsl:text> |
|
11996 <xsl:text> |
|
11997 </xsl:text> |
|
11998 <xsl:text> jumps_need_update = true; |
|
11999 </xsl:text> |
|
12000 <xsl:text> |
|
12001 </xsl:text> |
|
12002 <xsl:text> requestHMIAnimation(); |
|
12003 </xsl:text> |
|
12004 <xsl:text> jump_history.push([page_name, page_index]); |
|
12005 </xsl:text> |
|
12006 <xsl:text> if(jump_history.length > 42) |
|
12007 </xsl:text> |
|
12008 <xsl:text> jump_history.shift(); |
|
12009 </xsl:text> |
|
12010 <xsl:text> |
|
12011 </xsl:text> |
|
12012 <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined |
|
12013 </xsl:text> |
|
12014 <xsl:text> ? page_name |
|
12015 </xsl:text> |
|
12016 <xsl:text> : page_name + "@" + hmitree_paths[page_index]); |
|
12017 </xsl:text> |
|
12018 <xsl:text> |
|
12019 </xsl:text> |
|
12020 <xsl:text> return true; |
|
12021 </xsl:text> |
|
12022 <xsl:text>}; |
|
12023 </xsl:text> |
|
12024 <xsl:text> |
|
12025 </xsl:text> |
|
12026 <xsl:text>function switch_visible_page(page_name) { |
|
12027 </xsl:text> |
|
12028 <xsl:text> |
|
12029 </xsl:text> |
|
12030 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
12031 </xsl:text> |
|
12032 <xsl:text> let new_desc = page_desc[page_name]; |
|
12033 </xsl:text> |
|
12034 <xsl:text> |
|
12035 </xsl:text> |
|
12036 <xsl:text> if(old_desc){ |
|
12037 </xsl:text> |
|
12038 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
12039 </xsl:text> |
|
12040 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
12041 </xsl:text> |
|
12042 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
12043 </xsl:text> |
|
12044 <xsl:text> parent.removeChild(element); |
|
12045 </xsl:text> |
|
12046 <xsl:text> } |
|
12047 </xsl:text> |
|
12048 <xsl:text> } |
|
12049 </xsl:text> |
|
12050 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12051 </xsl:text> |
|
12052 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
12053 </xsl:text> |
|
12054 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12055 </xsl:text> |
|
12056 <xsl:text> parent.appendChild(element); |
|
12057 </xsl:text> |
|
12058 <xsl:text> } |
|
12059 </xsl:text> |
|
12060 <xsl:text> } |
|
12061 </xsl:text> |
|
12062 <xsl:text> }else{ |
|
12063 </xsl:text> |
|
12064 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12065 </xsl:text> |
|
12066 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12067 </xsl:text> |
|
12068 <xsl:text> parent.appendChild(element); |
|
12069 </xsl:text> |
|
12070 <xsl:text> } |
|
12071 </xsl:text> |
|
12072 <xsl:text> } |
|
12073 </xsl:text> |
|
12074 <xsl:text> |
|
12075 </xsl:text> |
|
12076 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
12077 </xsl:text> |
|
12078 <xsl:text> current_visible_page = page_name; |
|
12079 </xsl:text> |
|
12080 <xsl:text>}; |
|
12081 </xsl:text> |
|
12082 <xsl:text> |
|
12083 </xsl:text> |
|
12084 <xsl:text>// Once connection established |
12390 <xsl:text>// Once connection established |
12085 </xsl:text> |
12391 </xsl:text> |
12086 <xsl:text>ws.onopen = function (evt) { |
12392 <xsl:text>ws.onopen = function (evt) { |
|
12393 </xsl:text> |
|
12394 <xsl:text> apply_reference_frames(); |
12087 </xsl:text> |
12395 </xsl:text> |
12088 <xsl:text> init_widgets(); |
12396 <xsl:text> init_widgets(); |
12089 </xsl:text> |
12397 </xsl:text> |
12090 <xsl:text> send_reset(); |
12398 <xsl:text> send_reset(); |
12091 </xsl:text> |
12399 </xsl:text> |