--- a/svghmi/gen_index_xhtml.xslt Mon Oct 28 10:30:20 2019 +0100
+++ b/svghmi/gen_index_xhtml.xslt Mon Oct 28 19:52:43 2019 +0100
@@ -3,6 +3,7 @@
<xsl:output method="xml" cdata-section-elements="xhtml:script"/>
<xsl:variable name="geometry" select="ns:GetSVGGeometry()"/>
<xsl:variable name="hmitree" select="ns:GetHMITree()"/>
+ <xsl:variable name="svg_root_id" select="/svg:svg/@id"/> <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
<xsl:variable name="hmi_geometry" select="$geometry[@Id = $hmi_elements/@id]"/>
<xsl:variable name="hmi_pages" select="$hmi_elements[func:parselabel(@inkscape:label)/widget/@type = 'Page']"/>
@@ -97,9 +98,25 @@
- <xsl:template mode="identity_svg" match="@* | node()">
+ <xsl:template mode="inline_svg" match="@* | node()"> + <xsl:apply-templates mode="inline_svg" select="@* | node()"/> + <xsl:template mode="inline_svg" match="svg:svg/@width"/> + <xsl:template mode="inline_svg" match="svg:svg/@height"/> + <xsl:template mode="inline_svg" match="svg:svg"> - <xsl:apply-templates mode="identity_svg" select="@* | node()"/>
+ <xsl:attribute name="preserveAspectRatio"> + <xsl:text>none</xsl:text> + <xsl:attribute name="height"> + <xsl:text>100vh</xsl:text> + <xsl:attribute name="width"> + <xsl:text>100vw</xsl:text> + <xsl:apply-templates mode="inline_svg" select="@* | node()"/> @@ -108,19 +125,8 @@
<html xmlns="http://www.w3.org/1999/xhtml">
- <body style="margin:0;">
- <xsl:apply-templates mode="testgeo" select="$hmi_geometry"/>
- <xsl:apply-templates mode="testtree" select="$hmitree"/>
- <xsl:apply-templates mode="testtree" select="$indexed_hmitree"/>
- <xsl:apply-templates mode="identity_svg" select="@* | node()"/>
+ <body style="margin:0;overflow:hidden;"> + <xsl:apply-templates mode="inline_svg" select="svg:svg"/> <xsl:call-template name="scripts"/>
@@ -288,6 +294,16 @@
<xsl:value-of select="@id"/>
+ <xsl:text> bbox: [</xsl:text> + <xsl:value-of select="$p/@x"/> + <xsl:text>, </xsl:text> + <xsl:value-of select="$p/@y"/> + <xsl:text>, </xsl:text> + <xsl:value-of select="$p/@w"/> + <xsl:text>, </xsl:text> + <xsl:value-of select="$p/@h"/> <xsl:for-each select="$page_ids">
@@ -316,6 +332,10 @@
<xsl:value-of select="$default_page"/>
+ <xsl:text>var svg_root = document.getElementById("</xsl:text> + <xsl:value-of select="$svg_root_id"/> @@ -696,19 +716,25 @@
- <xsl:text> /* add new subsribers if any */
- <xsl:text> if(new_desc) for(let widget of new_desc.widgets){
+ <xsl:text> if(new_desc) { + <xsl:text> /* add new subsribers if any */ - <xsl:text> for(let index of widget.indexes){
+ <xsl:text> for(let widget of new_desc.widgets){ + <xsl:text> for(let index of widget.indexes){ - <xsl:text> subscribers[index].add(widget);
+ <xsl:text> subscribers[index].add(widget);
+ <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
<xsl:text> current_page = page_name;
@@ -829,6 +855,27 @@
+ <xsl:template name="defs_by_labels"> + <xsl:param name="labels" select="''"/> + <xsl:param name="mandatory" select="'yes'"/> + <xsl:param name="hmi_element"/> + <xsl:for-each select="str:split($labels)"> + <xsl:variable name="name" select="."/> + <xsl:variable name="elt_id" select="$hmi_element//*[@inkscape:label=$name][1]/@id"/> + <xsl:if test="$mandatory='yes' and not($elt_id)"> + <xsl:message terminate="yes"> + <xsl:text>Meter widget must have a </xsl:text> + <xsl:value-of select="$name"/> + <xsl:text> element</xsl:text> + <xsl:value-of select="$name"/> + <xsl:text>_elt: document.getElementById("</xsl:text> + <xsl:value-of select="$elt_id"/> <xsl:template mode="widget_defs" match="widget[@type='Display']">
<xsl:param name="hmi_element"/>
@@ -853,22 +900,12 @@
<xsl:param name="hmi_element"/>
- <xsl:for-each select="str:split('value min max needle range')">
- <xsl:variable name="name" select="."/>
- <xsl:variable name="elt_id" select="$hmi_element//*[@inkscape:label=$name][1]/@id"/>
- <xsl:if test="not($elt_id)">
- <xsl:message terminate="yes">
- <xsl:text>Meter widget must have a </xsl:text>
- <xsl:value-of select="$name"/>
- <xsl:text> element</xsl:text>
- <xsl:value-of select="$name"/>
- <xsl:text>_elt: document.getElementById("</xsl:text>
- <xsl:value-of select="$elt_id"/>
+ <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <test>value min max needle range</test> <xsl:text>dispatch: function(value) {
<xsl:text> this.value_elt.textContent = String(value);
@@ -900,16 +937,12 @@
<xsl:param name="hmi_element"/>
- <xsl:variable name="value_elt_id" select="$hmi_element//*[self::svg:text][@inkscape:label='value'][1]/@id"/>
- <xsl:if test="not($value_elt_id)">
- <xsl:message terminate="yes">
- <xsl:text>Input widget must have a text element</xsl:text>
- <xsl:text>value_elt: document.getElementById("</xsl:text>
- <xsl:value-of select="$value_elt_id"/>
+ <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> <xsl:text>dispatch: function(value) {
<xsl:text> this.value_elt.textContent = String(value);
@@ -955,4 +988,16 @@
+ <xsl:template mode="widget_defs" match="widget[@type='Jump']"> + <xsl:text>init: function() { + <xsl:text> this.element.addEventListener( + <xsl:text> evt => switch_page(this.args[0])); --- a/svghmi/gen_index_xhtml.ysl2 Mon Oct 28 10:30:20 2019 +0100
+++ b/svghmi/gen_index_xhtml.ysl2 Mon Oct 28 19:52:43 2019 +0100
@@ -3,6 +3,11 @@
// overrides yslt's output function to set CDATA
decl output(method, cdata-section-elements="xhtml:script");
+in xsl decl labels(*ptr, name="defs_by_labels") alias call-template { + with "hmi_element", "$hmi_element"; + with "labels"{test *ptr}; xmlns:dc="http://purl.org/dc/elements/1.1/"
@@ -25,6 +30,7 @@
const "geometry", "ns:GetSVGGeometry()";
const "hmitree", "ns:GetHMITree()";
+ const "svg_root_id", "/svg:svg/@id"; const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
const "hmi_geometry", "$geometry[@Id = $hmi_elements/@id]";
@@ -96,9 +102,19 @@
* - copy every attributes
* - copy every sub-elements
- template "@* | node()", mode="identity_svg" {
+ template "@* | node()", mode="inline_svg" { /* use real xsl:copy instead copy-of alias from yslt.yml2 */
- xsl:copy apply "@* | node()", mode="identity_svg";
+ xsl:copy apply "@* | node()", mode="inline_svg"; + /* replaces inkscape's height and width hints. forces fit */ + template "svg:svg/@width", mode="inline_svg"; + template "svg:svg/@height", mode="inline_svg"; + template "svg:svg", mode="inline_svg" xsl:copy { + attrib "preserveAspectRatio" > none + attrib "height" > 100vh + apply "@* | node()", mode="inline_svg"; /*const "mark" > =HMI=\n*/
@@ -106,21 +122,21 @@
/* copy root node and add geometry as comment for a test */
comment > Made with SVGHMI. https://beremiz.org
+ apply "$hmi_geometry", mode="testgeo"; + apply "$hmitree", mode="testtree"; + apply "$indexed_hmitree", mode="testtree"; html xmlns="http://www.w3.org/1999/xhtml" {
- body style="margin:0;" {
- apply "$hmi_geometry", mode="testgeo";
- apply "$hmitree", mode="testtree";
- apply "$indexed_hmitree", mode="testtree";
- apply "@* | node()", mode="identity_svg";
+ body style="margin:0;overflow:hidden;" { + apply "svg:svg", mode="inline_svg"; @@ -230,6 +246,7 @@
const "page_elements", "$hmi_elements[@id = $page_ids]";
| "«$desc/arg[1]/@value»": {
+ | bbox: [«$p/@x», «$p/@y», «$p/@w», «$p/@h»], | hmi_widgets.«.»`if "position()!=last()" > ,`
@@ -241,7 +258,7 @@
| var default_page = "«$default_page»";
+ | var svg_root = document.getElementById("«$svg_root_id»"); @@ -307,6 +324,20 @@
+ function "defs_by_labels" { + param "mandatory","'yes'"; + foreach "str:split($labels)" { + const "elt_id","$hmi_element//*[@inkscape:label=$name][1]/@id"; + if "$mandatory='yes' and not($elt_id)" error > Meter widget must have a «$name» element + | «$name»_elt: document.getElementById("«$elt_id»"), template "widget[@type='Display']", mode="widget_defs" {
@@ -326,12 +357,7 @@
template "widget[@type='Meter']", mode="widget_defs" {
- foreach "str:split('value min max needle range')" {
- const "elt_id","$hmi_element//*[@inkscape:label=$name][1]/@id";
- if "not($elt_id)" error > Meter widget must have a «$name» element
- | «$name»_elt: document.getElementById("«$elt_id»"),
+ labels("value min max needle range"); | dispatch: function(value) {
| this.value_elt.textContent = String(value);
| let [min,max,totallength] = this.range;
@@ -350,9 +376,7 @@
template "widget[@type='Input']", mode="widget_defs" {
- const "value_elt_id","$hmi_element//*[self::svg:text][@inkscape:label='value'][1]/@id";
- if "not($value_elt_id)" error > Input widget must have a text element
- | value_elt: document.getElementById("«$value_elt_id»"),
| dispatch: function(value) {
| this.value_elt.textContent = String(value);
@@ -378,16 +402,13 @@
template "widget[@type='Change']", mode="widget_defs" {
- // HMI:Change:-10@/PUMP/VALUE
- // HMI:Change:+1@/PUMP/VALUE
- // HMI:Change:=42@/PUMP/VALUE
- // | frequency: 10`apply ".", mode="refresh_frequency"`,
- // template "widget", mode="refresh_frequency" > 10
- template "widget[@type='Meter']", mode="refresh_frequency" > 10
- template "widget[@type='Display']", mode="refresh_frequency" > 5
- template "widget[@type='Input']", mode="refresh_frequency" > 5
+ template "widget[@type='Jump']", mode="widget_defs" { + | this.element.addEventListener( + | evt => switch_page(this.args[0])); --- a/svghmi/svghmi.js Mon Oct 28 10:30:20 2019 +0100
+++ b/svghmi/svghmi.js Mon Oct 28 19:52:43 2019 +0100
@@ -188,13 +188,16 @@
subscribers[index].delete(widget);
- /* add new subsribers if any */
- if(new_desc) for(let widget of new_desc.widgets){
- for(let index of widget.indexes){
- subscribers[index].add(widget);
+ /* add new subsribers if any */ + for(let widget of new_desc.widgets){ + for(let index of widget.indexes){ + subscribers[index].add(widget); + svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
current_page = page_name;