SVGHMI: created widget_*.ysl2. Renamed widget_common in widgets_common, so that it doesn't match globing.
--- a/svghmi/gen_index_xhtml.xslt Tue Mar 17 14:11:54 2020 +0100
+++ b/svghmi/gen_index_xhtml.xslt Wed Mar 18 09:16:14 2020 +0100
@@ -624,6 +624,230 @@
+ <func:function name="func:escape_quotes"> + <xsl:param name="txt"/> + <xsl:variable name="frst" select="substring-before($txt,'"')"/> + <xsl:variable name="frstln" select="string-length($frst)"/> + <xsl:when test="$frstln > 0 and string-length($txt) > $frstln"> + <func:result select="concat($frst,'\"',func:escape_quotes(substring-after($txt,'"')))"/> + <func:result select="$txt"/> + <xsl:template mode="widget_defs" match="widget[@type='Display']"> + <xsl:param name="hmi_element"/> + <xsl:text> frequency: 5, + <xsl:text> dispatch: function(value) { + <xsl:when test="$hmi_element[self::svg:text]"> + <xsl:text> this.element.textContent = String(value); + <xsl:message terminate="no"> + <xsl:text>Display widget as a group not implemented</xsl:text> + <xsl:template mode="widget_defs" match="widget[@type='Input']"> + <xsl:param name="hmi_element"/> + <xsl:variable name="value_elt"> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>value</xsl:text> + <xsl:with-param name="mandatory" select="'no'"/> + <xsl:variable name="have_value" select="string-length($value_elt)>0"/> + <xsl:value-of select="$value_elt"/> + <xsl:if test="$have_value"> + <xsl:text> frequency: 5, + <xsl:text> dispatch: function(value) { + <xsl:if test="$have_value"> + <xsl:text> this.value_elt.textContent = String(value); + <xsl:variable name="edit_elt_id" select="$hmi_element/*[@inkscape:label='edit'][1]/@id"/> + <xsl:text> init: function() { + <xsl:if test="$edit_elt_id"> + <xsl:text> id("</xsl:text> + <xsl:value-of select="$edit_elt_id"/> + <xsl:text>").addEventListener( + <xsl:text> evt => alert('XXX TODO : Edit value')); + <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]"> + <xsl:text> id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text>").addEventListener( + <xsl:text> evt => {let new_val = change_hmi_value(this.indexes[0], "</xsl:text> + <xsl:value-of select="func:escape_quotes(@inkscape:label)"/> + <xsl:if test="$have_value"> + <xsl:text> this.value_elt.textContent = String(new_val); + <xsl:template mode="widget_defs" match="widget[@type='Jump']"> + <xsl:param name="hmi_element"/> + <xsl:text> on_click: function(evt) { + <xsl:text> switch_page(this.args[0], this.indexes[0]); + <xsl:text> init: function() { + <xsl:text> this.element.setAttribute("onclick", "hmi_widgets['</xsl:text> + <xsl:value-of select="$hmi_element/@id"/> + <xsl:text>'].on_click(evt)"); + <xsl:template mode="widget_defs" match="widget[@type='Meter']"> + <xsl:param name="hmi_element"/> + <xsl:text> frequency: 10, + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>needle range</xsl:text> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>value min max</xsl:text> + <xsl:with-param name="mandatory" select="'no'"/> + <xsl:text> dispatch: function(value) { + <xsl:text> if(this.value_elt) + <xsl:text> this.value_elt.textContent = String(value); + <xsl:text> let [min,max,totallength] = this.range; + <xsl:text> let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); + <xsl:text> let tip = this.range_elt.getPointAtLength(length); + <xsl:text> this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y); + <xsl:text> origin: undefined, + <xsl:text> range: undefined, + <xsl:text> init: function() { + <xsl:text> let min = this.min_elt ? + <xsl:text> Number(this.min_elt.textContent) : + <xsl:text> this.args.length >= 1 ? this.args[0] : 0; + <xsl:text> let max = this.max_elt ? + <xsl:text> Number(this.max_elt.textContent) : + <xsl:text> this.args.length >= 2 ? this.args[1] : 100; + <xsl:text> this.range = [min, max, this.range_elt.getTotalLength()] + <xsl:text> this.origin = this.needle_elt.getPointAtLength(0); + <xsl:template mode="widget_defs" match="widget[@type='Switch']"> + <xsl:param name="hmi_element"/> + <xsl:text> frequency: 5, + <xsl:text> dispatch: function(value) { + <xsl:text> for(let choice of this.choices){ + <xsl:text> if(value != choice.value){ + <xsl:text> choice.elt.setAttribute("style", "display:none"); + <xsl:text> choice.elt.setAttribute("style", choice.style); + <xsl:text> init: function() { + <xsl:text> // Hello Switch + <xsl:variable name="regex" select="'^("[^"].*"|\-?[0-9]+)(#.*)?$'"/> + <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,$regex)]"> + <xsl:variable name="literal" select="regexp:match(@inkscape:label,$regex)[2]"/> + <xsl:text> elt:id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text> style:"</xsl:text> + <xsl:value-of select="@style"/> + <xsl:text> value:</xsl:text> + <xsl:value-of select="$literal"/> + <xsl:text> }</xsl:text> + <xsl:if test="position()!=last()"> <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
@@ -675,8 +899,6 @@
<xsl:template name="scripts">
- <xsl:text>//(function(){
<xsl:text>id = idstr => document.getElementById(idstr);
@@ -1535,236 +1757,5 @@
- <xsl:template mode="widget_defs" match="widget[@type='Display']">
- <xsl:param name="hmi_element"/>
- <xsl:text> frequency: 5,
- <xsl:text> dispatch: function(value) {
- <xsl:when test="$hmi_element[self::svg:text]">
- <xsl:text> this.element.textContent = String(value);
- <xsl:message terminate="no">
- <xsl:text>Display widget as a group not implemented</xsl:text>
- <xsl:template mode="widget_defs" match="widget[@type='Meter']">
- <xsl:param name="hmi_element"/>
- <xsl:text> frequency: 10,
- <xsl:call-template name="defs_by_labels">
- <xsl:with-param name="hmi_element" select="$hmi_element"/>
- <xsl:with-param name="labels">
- <xsl:text>needle range</xsl:text>
- <xsl:call-template name="defs_by_labels">
- <xsl:with-param name="hmi_element" select="$hmi_element"/>
- <xsl:with-param name="labels">
- <xsl:text>value min max</xsl:text>
- <xsl:with-param name="mandatory" select="'no'"/>
- <xsl:text> dispatch: function(value) {
- <xsl:text> if(this.value_elt)
- <xsl:text> this.value_elt.textContent = String(value);
- <xsl:text> let [min,max,totallength] = this.range;
- <xsl:text> let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
- <xsl:text> let tip = this.range_elt.getPointAtLength(length);
- <xsl:text> this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
- <xsl:text> origin: undefined,
- <xsl:text> range: undefined,
- <xsl:text> init: function() {
- <xsl:text> let min = this.min_elt ?
- <xsl:text> Number(this.min_elt.textContent) :
- <xsl:text> this.args.length >= 1 ? this.args[0] : 0;
- <xsl:text> let max = this.max_elt ?
- <xsl:text> Number(this.max_elt.textContent) :
- <xsl:text> this.args.length >= 2 ? this.args[1] : 100;
- <xsl:text> this.range = [min, max, this.range_elt.getTotalLength()]
- <xsl:text> this.origin = this.needle_elt.getPointAtLength(0);
- <func:function name="func:escape_quotes">
- <xsl:param name="txt"/>
- <xsl:variable name="frst" select="substring-before($txt,'"')"/>
- <xsl:variable name="frstln" select="string-length($frst)"/>
- <xsl:when test="$frstln > 0 and string-length($txt) > $frstln">
- <func:result select="concat($frst,'\"',func:escape_quotes(substring-after($txt,'"')))"/>
- <func:result select="$txt"/>
- <xsl:template mode="widget_defs" match="widget[@type='Input']">
- <xsl:param name="hmi_element"/>
- <xsl:variable name="value_elt">
- <xsl:call-template name="defs_by_labels">
- <xsl:with-param name="hmi_element" select="$hmi_element"/>
- <xsl:with-param name="labels">
- <xsl:text>value</xsl:text>
- <xsl:with-param name="mandatory" select="'no'"/>
- <xsl:variable name="have_value" select="string-length($value_elt)>0"/>
- <xsl:value-of select="$value_elt"/>
- <xsl:if test="$have_value">
- <xsl:text> frequency: 5,
- <xsl:text> dispatch: function(value) {
- <xsl:if test="$have_value">
- <xsl:text> this.value_elt.textContent = String(value);
- <xsl:variable name="edit_elt_id" select="$hmi_element/*[@inkscape:label='edit'][1]/@id"/>
- <xsl:text> init: function() {
- <xsl:if test="$edit_elt_id">
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="$edit_elt_id"/>
- <xsl:text>").addEventListener(
- <xsl:text> evt => alert('XXX TODO : Edit value'));
- <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>").addEventListener(
- <xsl:text> evt => {let new_val = change_hmi_value(this.indexes[0], "</xsl:text>
- <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
- <xsl:if test="$have_value">
- <xsl:text> this.value_elt.textContent = String(new_val);
- <xsl:template mode="widget_defs" match="widget[@type='Button']"/>
- <xsl:template mode="widget_defs" match="widget[@type='Toggle']">
- <xsl:text> frequency: 5,
- <xsl:template mode="widget_defs" match="widget[@type='Switch']">
- <xsl:param name="hmi_element"/>
- <xsl:text> frequency: 5,
- <xsl:text> dispatch: function(value) {
- <xsl:text> for(let choice of this.choices){
- <xsl:text> if(value != choice.value){
- <xsl:text> choice.elt.setAttribute("style", "display:none");
- <xsl:text> choice.elt.setAttribute("style", choice.style);
- <xsl:text> init: function() {
- <xsl:text> // Hello Switch
- <xsl:variable name="regex" select="'^("[^"].*"|\-?[0-9]+)(#.*)?$'"/>
- <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,$regex)]">
- <xsl:variable name="literal" select="regexp:match(@inkscape:label,$regex)[2]"/>
- <xsl:text> elt:id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text> style:"</xsl:text>
- <xsl:value-of select="@style"/>
- <xsl:text> value:</xsl:text>
- <xsl:value-of select="$literal"/>
- <xsl:text> }</xsl:text>
- <xsl:if test="position()!=last()">
- <xsl:template mode="widget_defs" match="widget[@type='Jump']">
- <xsl:param name="hmi_element"/>
- <xsl:text> on_click: function(evt) {
- <xsl:text> switch_page(this.args[0], this.indexes[0]);
- <xsl:text> init: function() {
- <xsl:text> this.element.setAttribute("onclick", "hmi_widgets['</xsl:text>
- <xsl:value-of select="$hmi_element/@id"/>
- <xsl:text>'].on_click(evt)");
--- a/svghmi/gen_index_xhtml.ysl2 Tue Mar 17 14:11:54 2020 +0100
+++ b/svghmi/gen_index_xhtml.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -60,7 +60,9 @@
- include widget_common.ysl2
+ include widgets_common.ysl2 comment > Made with SVGHMI. https://beremiz.org
@@ -73,6 +75,7 @@
xmlns:xlink="http://www.w3.org/1999/xlink" {
body style="margin:0;overflow:hidden;" {
@@ -83,30 +86,11 @@
| id = idstr => document.getElementById(idstr);
| var hmi_hash = [«$hmitree/@hash»];
- function evaluate_js_from_descriptions() {
- const "midmark" > \n«$mark»
- apply """//*[contains(child::svg:desc, $midmark) or \
- starts-with(child::svg:desc, $mark)]""",2
- mode="code_from_descs";
apply "$hmi_elements", mode="hmi_elements";
@@ -137,177 +121,5 @@
| var default_page = "«$default_page»";
| var svg_root = id("«/svg:svg/@id»");
- // template "*", mode="code_from_descs" {
- // var path, role, name, priv;
- // /* if label is used, use it as default name */
- // if "@inkscape:label"
- // |> name = "«@inkscape:label»";
- // | /* -------------- */
- // // this breaks indent, but fixing indent could break string literals
- // value "substring-after(svg:desc, $mark)";
- // // nobody reads generated code anyhow...
- // /* -------------- */
- template "widget[@type='Display']", mode="widget_defs" {
- | dispatch: function(value) {
- when "$hmi_element[self::svg:text]"{
- // TODO : care about <tspan> ?
- | this.element.textContent = String(value);
- warning > Display widget as a group not implemented
- template "widget[@type='Meter']", mode="widget_defs" {
- labels("needle range");
- optional_labels("value min max");
- | dispatch: function(value) {
- | this.value_elt.textContent = String(value);
- | let [min,max,totallength] = this.range;
- | let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
- | let tip = this.range_elt.getPointAtLength(length);
- | this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y);
- | let min = this.min_elt ?
- | Number(this.min_elt.textContent) :
- | this.args.length >= 1 ? this.args[0] : 0;
- | let max = this.max_elt ?
- | Number(this.max_elt.textContent) :
- | this.args.length >= 2 ? this.args[1] : 100;
- | this.range = [min, max, this.range_elt.getTotalLength()]
- | this.origin = this.needle_elt.getPointAtLength(0);
- def "func:escape_quotes" {
- // have to use a python string to enter escaped quote
- const "frst", !"substring-before($txt,'\"')"!;
- const "frstln", "string-length($frst)";
- when "$frstln > 0 and string-length($txt) > $frstln" {
- result !"concat($frst,'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!;
- template "widget[@type='Input']", mode="widget_defs" {
- optional_labels("value");
- const "have_value","string-length($value_elt)>0";
- | dispatch: function(value) {
- | this.value_elt.textContent = String(value);
- const "edit_elt_id","$hmi_element/*[@inkscape:label='edit'][1]/@id";
- | id("«$edit_elt_id»").addEventListener(
- | evt => alert('XXX TODO : Edit value'));
- foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]" {
- | id("«@id»").addEventListener(
- | evt => {let new_val = change_hmi_value(this.indexes[0], "«func:escape_quotes(@inkscape:label)»");
- | this.value_elt.textContent = String(new_val);
- /* TODO gray out value until refreshed */
- template "widget[@type='Button']", mode="widget_defs" {
- template "widget[@type='Toggle']", mode="widget_defs" {
- template "widget[@type='Switch']", mode="widget_defs" {
- | dispatch: function(value) {
- | for(let choice of this.choices){
- | if(value != choice.value){
- | choice.elt.setAttribute("style", "display:none");
- | choice.elt.setAttribute("style", choice.style);
- const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+)(#.*)?$'"!;
- foreach "$hmi_element/*[regexp:test(@inkscape:label,$regex)]" {
- const "literal", "regexp:match(@inkscape:label,$regex)[2]";
- | }`if "position()!=last()" > ,`
- template "widget[@type='Jump']", mode="widget_defs" {
- | on_click: function(evt) {
- | switch_page(this.args[0], this.indexes[0]);
- /* registering event this way doies not "click" through svg:use
- | this.element.onclick = evt => switch_page(this.args[0]);
- event must be registered by adding attribute to element instead
- TODO : generalize mouse event handling by global event capture + getElementsAtPoint()
- | this.element.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_click(evt)");
--- a/svghmi/widget_common.ysl2 Tue Mar 17 14:11:54 2020 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-in xsl decl labels(*ptr, name="defs_by_labels") alias call-template {
- with "hmi_element", "$hmi_element";
- with "labels"{text *ptr};
-in xsl decl optional_labels(*ptr, name="defs_by_labels") alias call-template {
- with "hmi_element", "$hmi_element";
- with "labels"{text *ptr};
- with "mandatory","'no'";
-template "svg:*", mode="hmi_elements" {
- const "widget", "func:parselabel(@inkscape:label)/widget";
- | type: "«$widget/@type»",
- | "«@value»"`if "position()!=last()" > ,`
- foreach "$widget/path" {
- warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
- | «@index»`if "position()!=last()" > ,`
- | element: id("«@id»"),
- apply "$widget", mode="widget_defs" with "hmi_element",".";
- | }`if "position()!=last()" > ,`
-function "defs_by_labels" {
- param "mandatory","'yes'";
- const "widget_type","@type";
- foreach "str:split($labels)" {
- const "elt_id","$result_svg_ns//*[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]/@id";
- if "$mandatory='yes'" {
- // TODO FIXME error > «$widget_type» widget must have a «$name» element
- warning > «$widget_type» widget must have a «$name» element
- // otherwise produce nothing
- | «$name»_elt: id("«$elt_id»"),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_custom.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,60 @@
+// widget entierely defined from JS code in Inkscape description field +// a preliminary implementation was initially attempted but disabled +// code collected around before code refactoring + function evaluate_js_from_descriptions() { + const "midmark" > \n«$mark» + apply """//*[contains(child::svg:desc, $midmark) or \ + starts-with(child::svg:desc, $mark)]""",2 + mode="code_from_descs"; + // template "*", mode="code_from_descs" { + // var path, role, name, priv; + // /* if label is used, use it as default name */ + // if "@inkscape:label" + // |> name = "«@inkscape:label»"; + // | /* -------------- */ + // // this breaks indent, but fixing indent could break string literals + // value "substring-after(svg:desc, $mark)"; + // // nobody reads generated code anyhow... + // /* -------------- */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_display.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,19 @@
+template "widget[@type='Display']", mode="widget_defs" { + | dispatch: function(value) { + when "$hmi_element[self::svg:text]"{ + // TODO : care about <tspan> ? + | this.element.textContent = String(value); + warning > Display widget as a group not implemented --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_input.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,37 @@
+template "widget[@type='Input']", mode="widget_defs" { + optional_labels("value"); + const "have_value","string-length($value_elt)>0"; + | dispatch: function(value) { + | this.value_elt.textContent = String(value); + const "edit_elt_id","$hmi_element/*[@inkscape:label='edit'][1]/@id"; + | id("«$edit_elt_id»").addEventListener( + | evt => alert('XXX TODO : Edit value')); + foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]" { + | id("«@id»").addEventListener( + | evt => {let new_val = change_hmi_value(this.indexes[0], "«func:escape_quotes(@inkscape:label)»"); + | this.value_elt.textContent = String(new_val); + /* TODO gray out value until refreshed */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_jump.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,16 @@
+template "widget[@type='Jump']", mode="widget_defs" { + | on_click: function(evt) { + | switch_page(this.args[0], this.indexes[0]); + /* registering event this way doies not "click" through svg:use + | this.element.onclick = evt => switch_page(this.args[0]); + event must be registered by adding attribute to element instead + TODO : generalize mouse event handling by global event capture + getElementsAtPoint() + | this.element.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_click(evt)"); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_meter.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,31 @@
+template "widget[@type='Meter']", mode="widget_defs" { + labels("needle range"); + optional_labels("value min max"); + | dispatch: function(value) { + | this.value_elt.textContent = String(value); + | let [min,max,totallength] = this.range; + | let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); + | let tip = this.range_elt.getPointAtLength(length); + | this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y); + | let min = this.min_elt ? + | Number(this.min_elt.textContent) : + | this.args.length >= 1 ? this.args[0] : 0; + | let max = this.max_elt ? + | Number(this.max_elt.textContent) : + | this.args.length >= 2 ? this.args[1] : 100; + | this.range = [min, max, this.range_elt.getTotalLength()] + | this.origin = this.needle_elt.getPointAtLength(0); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widget_switch.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,29 @@
+template "widget[@type='Switch']", mode="widget_defs" { + | dispatch: function(value) { + | for(let choice of this.choices){ + | if(value != choice.value){ + | choice.elt.setAttribute("style", "display:none"); + | choice.elt.setAttribute("style", choice.style); + const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+)(#.*)?$'"!; + foreach "$hmi_element/*[regexp:test(@inkscape:label,$regex)]" { + const "literal", "regexp:match(@inkscape:label,$regex)[2]"; + | }`if "position()!=last()" > ,` --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widgets_common.ysl2 Wed Mar 18 09:16:14 2020 +0100
@@ -0,0 +1,76 @@
+in xsl decl labels(*ptr, name="defs_by_labels") alias call-template { + with "hmi_element", "$hmi_element"; + with "labels"{text *ptr}; +in xsl decl optional_labels(*ptr, name="defs_by_labels") alias call-template { + with "hmi_element", "$hmi_element"; + with "labels"{text *ptr}; + with "mandatory","'no'"; +template "svg:*", mode="hmi_elements" { + const "widget", "func:parselabel(@inkscape:label)/widget"; + | type: "«$widget/@type»", + | "«@value»"`if "position()!=last()" > ,` + foreach "$widget/path" { + warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree + | «@index»`if "position()!=last()" > ,` + | element: id("«@id»"), + apply "$widget", mode="widget_defs" with "hmi_element","."; + | }`if "position()!=last()" > ,` +function "defs_by_labels" { + param "mandatory","'yes'"; + const "widget_type","@type"; + foreach "str:split($labels)" { + const "elt_id","$result_svg_ns//*[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]/@id"; + if "$mandatory='yes'" { + // TODO FIXME error > «$widget_type» widget must have a «$name» element + warning > «$widget_type» widget must have a «$name» element + // otherwise produce nothing + | «$name»_elt: id("«$elt_id»"), +def "func:escape_quotes" { + // have to use a python string to enter escaped quote + const "frst", !"substring-before($txt,'\"')"!; + const "frstln", "string-length($frst)"; + when "$frstln > 0 and string-length($txt) > $frstln" { + result !"concat($frst,'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!;