--- a/svghmi/gen_index_xhtml.xslt Thu Aug 20 13:52:00 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt Thu Aug 20 13:56:21 2020 +0200
@@ -637,7 +637,10 @@
- <xsl:template mode="inline_svg" match="@* | node()">
+ <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="@*"> + <xsl:template mode="inline_svg" match="node()"> <xsl:if test="not(@id = $discardable_elements/@id)">
<xsl:apply-templates mode="inline_svg" select="@* | node()"/>
@@ -672,19 +675,24 @@
<xsl:variable name="hmi_lists_descs" select="$parsed_widgets/widget[@type = 'List']"/>
<xsl:variable name="hmi_lists" select="$hmi_elements[@id = $hmi_lists_descs/@id]"/>
- <xsl:variable name="targets_not_to_unlink" select="$hmi_elements[@id = $hmi_lists/@id]/descendant::svg:*"/>
+ <xsl:variable name="targets_not_to_unlink" select="$hmi_lists/descendant-or-self::svg:*"/> <xsl:variable name="to_unlink" select="$hmi_elements[not(@id = $hmi_pages/@id)]/descendant-or-self::svg:use"/>
+ <func:function name="func:is_unlinkable"> + <xsl:param name="targetid"/> + <xsl:param name="eltid"/> + <func:result select="$eltid = $to_unlink/@id and not($targetid = $targets_not_to_unlink/@id)"/> <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="svg:use">
<xsl:variable name="targetid" select="substring-after(@xlink:href,'#')"/>
- <xsl:when test="@id = $to_unlink/@id and not($targetid = $targets_not_to_unlink/@id)">
+ <xsl:when test="func:is_unlinkable($targetid, @id)"> <xsl:call-template name="unlink_clone">
<xsl:with-param name="targetid" select="$targetid"/>
- <xsl:apply-templates mode="inline_svg" select="@* | node()"/>
+ <xsl:apply-templates mode="inline_svg" select="@*"/> @@ -705,6 +713,9 @@
+ <xsl:text>id</xsl:text> <xsl:variable name="excluded_use_attrs" select="exsl:node-set($_excluded_use_attrs)"/>
<xsl:variable name="_merge_use_attrs">
@@ -718,8 +729,27 @@
<xsl:variable name="merge_use_attrs" select="exsl:node-set($_merge_use_attrs)"/>
<xsl:template xmlns="http://www.w3.org/2000/svg" name="unlink_clone">
<xsl:param name="targetid"/>
+ <xsl:param name="seed" select="''"/> <xsl:variable name="target" select="//svg:*[@id = $targetid]"/>
+ <xsl:variable name="seeded_id"> + <xsl:when test="string-length($seed) > 0"> + <xsl:value-of select="$seed"/> + <xsl:value-of select="@id"/> + <xsl:value-of select="@id"/> + <xsl:attribute name="id"> + <xsl:value-of select="$seeded_id"/> + <xsl:attribute name="original"> + <xsl:value-of select="@id"/> <xsl:when test="$target[self::svg:g]">
<xsl:for-each select="@*[not(local-name() = $excluded_use_attrs/name | $merge_use_attrs)]">
@@ -746,7 +776,7 @@
<xsl:apply-templates mode="unlink_clone" select="$target/*">
- <xsl:with-param name="seed" select="@id"/>
+ <xsl:with-param name="seed" select="$seeded_id"/> @@ -756,7 +786,7 @@
<xsl:apply-templates mode="unlink_clone" select="$target">
- <xsl:with-param name="seed" select="@id"/>
+ <xsl:with-param name="seed" select="$seeded_id"/> @@ -769,10 +799,32 @@
<xsl:value-of select="."/>
+ <xsl:attribute name="original"> + <xsl:value-of select="."/> <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="@*">
+ <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="svg:use"> + <xsl:param name="seed"/> + <xsl:variable name="targetid" select="substring-after(@xlink:href,'#')"/> + <xsl:when test="func:is_unlinkable($targetid, @id)"> + <xsl:call-template name="unlink_clone"> + <xsl:with-param name="targetid" select="$targetid"/> + <xsl:with-param name="seed" select="$seed"/> + <xsl:apply-templates mode="unlink_clone" select="@*"> + <xsl:with-param name="seed" select="$seed"/> <xsl:template xmlns="http://www.w3.org/2000/svg" mode="unlink_clone" match="svg:*">
@@ -1024,8 +1076,6 @@
<xsl:text> let defaultval = local_defaults[varname];
- <xsl:text> console.log("page_local_index creat local", varname, pagename, new_index, defaultval);
<xsl:text> if(defaultval != undefined)
<xsl:text> cache[new_index] = defaultval;
@@ -3365,60 +3415,177 @@
+ <xsl:variable name="hmi_textstylelists_descs" select="$parsed_widgets/widget[@type = 'TextStyleList']"/> + <xsl:variable name="hmi_textstylelists" select="$hmi_elements[@id = $hmi_textstylelists_descs/@id]"/> + <xsl:variable name="textstylelist_related"> + <xsl:for-each select="$hmi_textstylelists"> + <xsl:attribute name="listid"> + <xsl:value-of select="@id"/> + <xsl:for-each select="func:refered_elements(.)"> + <xsl:attribute name="eltid"> + <xsl:value-of select="@id"/> + <xsl:variable name="textstylelist_related_ns" select="exsl:node-set($textstylelist_related)"/> + <func:function name="func:json_expressions"> + <xsl:param name="expressions"/> + <xsl:param name="label"/> + <xsl:when test="$label"> + <xsl:variable name="suffixes" select="str:split($label)"/> + <xsl:variable name="res"> + <xsl:for-each select="$suffixes"> + <xsl:variable name="suffix" select="."/> + <xsl:variable name="pos" select="position()"/> + <xsl:variable name="expr" select="$expressions[position() <= $pos][last()]/expression"/> + <xsl:when test="contains($suffix,'=')"> + <xsl:variable name="name" select="substring-before($suffix,'=')"/> + <xsl:if test="$expr/@name[. != $name]"> + <xsl:message terminate="yes"> + <xsl:text>JsonTable : missplaced '=' or inconsistent names in Json data expressions.</xsl:text> + <xsl:attribute name="name"> + <xsl:value-of select="$name"/> + <xsl:attribute name="content"> + <xsl:value-of select="$expr/@content"/> + <xsl:value-of select="substring-after($suffix,'=')"/> + <xsl:copy-of select="$expr/@name"/> + <xsl:attribute name="content"> + <xsl:value-of select="$expr/@content"/> + <xsl:value-of select="$suffix"/> + <func:result select="exsl:node-set($res)"/> + <func:result select="$expressions"/> + <xsl:variable name="initexpr"> + <xsl:attribute name="content"> + <xsl:text>jdata</xsl:text> + <xsl:variable name="initexpr_ns" select="exsl:node-set($initexpr)"/> <xsl:template mode="json_table_elt_render" match="svg:use">
- <xsl:param name="value_expr"/>
+ <xsl:param name="expressions"/> <xsl:variable name="targetid" select="substring-after(@xlink:href,'#')"/>
<xsl:variable name="from_list" select="$hmi_lists[(@id | */@id) = $targetid]"/>
- <xsl:if test="count($from_list) = 0">
- <xsl:message terminate="yes">
- <xsl:text>Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or HMI:List item. Reference "</xsl:text>
- <xsl:value-of select="@xlink:href"/>
- <xsl:text>" is not valid.</xsl:text>
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>").setAttribute("xlink:href",
- <xsl:text> "#"+hmi_widgets["</xsl:text>
- <xsl:value-of select="$from_list/@id"/>
- <xsl:text>"].items[</xsl:text>
- <xsl:value-of select="$value_expr"/>
+ <xsl:when test="count($from_list) > 0"> + <xsl:text> id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text>").setAttribute("xlink:href", + <xsl:text> "#"+hmi_widgets["</xsl:text> + <xsl:value-of select="$from_list/@id"/> + <xsl:text>"].items[</xsl:text> + <xsl:value-of select="$expressions/expression[1]/@content"/> + <xsl:message terminate="no"> + <xsl:text>Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or item. Reference "</xsl:text> + <xsl:value-of select="@xlink:href"/> + <xsl:text>" is not valid and will not be updated.</xsl:text> <xsl:template mode="json_table_elt_render" match="svg:text">
- <xsl:param name="value_expr"/>
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>").textContent = String(</xsl:text>
- <xsl:value-of select="$value_expr"/>
+ <xsl:param name="expressions"/> + <xsl:variable name="value_expr" select="$expressions/expression[1]/@content"/> + <xsl:variable name="original" select="@original"/> + <xsl:variable name="from_textstylelist" select="$textstylelist_related_ns/list[elt/@eltid = $original]"/> + <xsl:when test="count($from_textstylelist) > 0"> + <xsl:variable name="content_expr" select="$expressions/expression[2]/@content"/> + <xsl:if test="string-length($content_expr) = 0 or $expressions/expression[2]/@name != 'textContent'"> + <xsl:message terminate="yes"> + <xsl:text>Clones (svg:use) in JsonTable Widget pointing to a HMI:TextStyleList widget or item must have a "textContent=.someVal" assignement following value expression in label.</xsl:text> + <xsl:text> let elt = id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text> elt.textContent = String(</xsl:text> + <xsl:value-of select="$content_expr"/> + <xsl:text> elt.style = hmi_widgets["</xsl:text> + <xsl:value-of select="$from_textstylelist/@listid"/> + <xsl:text>"].styles[</xsl:text> + <xsl:value-of select="$value_expr"/> + <xsl:text> id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text>").textContent = String(</xsl:text> + <xsl:value-of select="$value_expr"/> + <func:function name="func:filter_non_widget_label"> + <xsl:param name="elt"/> + <xsl:param name="widget_elts"/> + <xsl:variable name="eltid"> + <xsl:when test="$elt/@original"> + <xsl:value-of select="$elt/@original"/> + <xsl:value-of select="$elt/@id"/> + <func:result select="$widget_elts[@id=$eltid]/@inkscape:label"/> <xsl:template mode="json_table_render" match="svg:*">
- <xsl:param name="objname"/>
+ <xsl:param name="expressions"/> + <xsl:param name="widget_elts"/> + <xsl:variable name="label" select="func:filter_non_widget_label(., $widget_elts)"/> <xsl:apply-templates mode="json_table_elt_render" select=".">
- <xsl:with-param name="value_expr">
- <xsl:value-of select="$objname"/>
- <xsl:value-of select="@inkscape:label"/>
+ <xsl:with-param name="expressions" select="func:json_expressions($expressions, $label)"/> <xsl:template mode="json_table_render" match="svg:g">
- <xsl:param name="objname"/>
- <xsl:text> let obj_</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text> = </xsl:text>
- <xsl:value-of select="$objname"/>
- <xsl:value-of select="@inkscape:label"/>
- <xsl:apply-templates mode="json_table_render" select="*[@inkscape:label]">
- <xsl:with-param name="objname">
- <xsl:text>obj_</xsl:text>
- <xsl:value-of select="@id"/>
+ <xsl:param name="expressions"/> + <xsl:param name="widget_elts"/> + <xsl:variable name="label" select="func:filter_non_widget_label(., $widget_elts)"/> + <xsl:apply-templates mode="json_table_render" select="*"> + <xsl:with-param name="expressions" select="func:json_expressions($expressions, $label)"/> + <xsl:with-param name="widget_elts" select="$widget_elts"/> <xsl:template mode="widget_defs" match="widget[@type='JsonTable']">
@@ -3440,7 +3607,8 @@
<xsl:text> spread_json_data: function(jdata) {
<xsl:apply-templates mode="json_table_render" select="$data_elt/*">
- <xsl:with-param name="objname" select="'jdata'"/>
+ <xsl:with-param name="expressions" select="$initexpr_ns"/> + <xsl:with-param name="widget_elts" select="$hmi_element/*[@inkscape:label = 'data']/descendant::svg:*"/> @@ -4079,7 +4247,7 @@
<xsl:for-each select="$hmi_element/*[@inkscape:label]">
- <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
+ <xsl:value-of select="@inkscape:label"/> <xsl:value-of select="@id"/>
@@ -4088,6 +4256,22 @@
+ <xsl:template mode="widget_defs" match="widget[@type='TextStyleList']"> + <xsl:param name="hmi_element"/> + <xsl:for-each select="$hmi_element/*[@inkscape:label]"> + <xsl:variable name="style" select="func:refered_elements(.)[self::svg:text]/@style"/> + <xsl:value-of select="@inkscape:label"/> + <xsl:text>: "</xsl:text> + <xsl:value-of select="$style"/> <xsl:template mode="widget_defs" match="widget[@type='Meter']">
<xsl:param name="hmi_element"/>
<xsl:text> frequency: 10,
--- a/svghmi/widget_jsontable.ysl2 Thu Aug 20 13:52:00 2020 +0200
+++ b/svghmi/widget_jsontable.ysl2 Thu Aug 20 13:56:21 2020 +0200
@@ -41,44 +41,128 @@
const "hmi_textstylelists_descs", "$parsed_widgets/widget[@type = 'TextStyleList']";
const "hmi_textstylelists", "$hmi_elements[@id = $hmi_textstylelists_descs/@id]";
+const "textstylelist_related" foreach "$hmi_textstylelists" list { + attrib "listid" value "@id"; + foreach "func:refered_elements(.)" elt { + attrib "eltid" value "@id"; +const "textstylelist_related_ns", "exsl:node-set($textstylelist_related)"; +def "func:json_expressions" { + // compute javascript expressions to access JSON data + // desscribed in given svg element's "label" + // knowing that parent element already has given "expressions". + const "suffixes", "str:split($label)"; + const "res" foreach "$suffixes" expression { + const "pos","position()"; + // take last available expression (i.e can have more suffixes than expressions) + const "expr","$expressions[position() <= $pos][last()]/expression"; + when "contains($suffix,'=')" { + const "name", "substring-before($suffix,'=')"; + if "$expr/@name[. != $name]" + error > JsonTable : missplaced '=' or inconsistent names in Json data expressions. + attrib "name" value "$name"; + attrib "content" > «$expr/@content»«substring-after($suffix,'=')» + attrib "content" > «$expr/@content»«$suffix» + result "exsl:node-set($res)"; + // Empty labels are ignored, expressions are then passed as-is. + otherwise result "$expressions"; +const "initexpr" expression attrib "content" > jdata +const "initexpr_ns", "exsl:node-set($initexpr)"; template "svg:use", mode="json_table_elt_render" {
// cloned element must be part of a HMI:List
const "targetid", "substring-after(@xlink:href,'#')";
const "from_list", "$hmi_lists[(@id | */@id) = $targetid]";
- const "from_textstylelist", "$hmi_textstylelists[(@id | */@id) = $targetid]";
when "count($from_list) > 0" {
| id("«@id»").setAttribute("xlink:href",
// obtain new target id from HMI:List widget
- | "#"+hmi_widgets["«$from_list/@id»"].items[«$value_expr»]);
- when "count($from_textstylelist) > 0" {
- | console.log("from_textsylelist","«@id»", "«$value_expr»", «$value_expr»,
- // obtain new style from HMI:TextStyleList widget
- | hmi_widgets["«$from_textstylelist/@id»"].items[«$value_expr»]);
+ | "#"+hmi_widgets["«$from_list/@id»"].items[«$expressions/expression[1]/@content»]); - warning > Clones (svg:use) in JsonTable Widget must point to a valid HMI:List or HMI:TextStyleList widget or item. Reference "«@xlink:href»" is not valid and will not be updated.
+ warning > Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or item. Reference "«@xlink:href»" is not valid and will not be updated. template "svg:text", mode="json_table_elt_render" {
- | id("«@id»").textContent = String(«$value_expr»);
+ const "value_expr", "$expressions/expression[1]/@content"; + const "original", "@original"; + const "from_textstylelist", "$textstylelist_related_ns/list[elt/@eltid = $original]"; + when "count($from_textstylelist) > 0" { + const "content_expr", "$expressions/expression[2]/@content"; + if "string-length($content_expr) = 0 or $expressions/expression[2]/@name != 'textContent'" + error > Clones (svg:use) in JsonTable Widget pointing to a HMI:TextStyleList widget or item must have a "textContent=.someVal" assignement following value expression in label. + | let elt = id("«@id»"); + | elt.textContent = String(«$content_expr»); + | elt.style = hmi_widgets["«$from_textstylelist/@listid»"].styles[«$value_expr»]; + | id("«@id»").textContent = String(«$value_expr»); +// only labels comming from Json widget are counted in +def "func:filter_non_widget_label" { + when "$elt/@original" value "$elt/@original"; + otherwise value "$elt/@id"; + result "$widget_elts[@id=$eltid]/@inkscape:label"; template "svg:*", mode="json_table_render" {
- apply ".", mode="json_table_elt_render" with "value_expr" > «$objname»«substring-before(@inkscape:label, ' ')»
+ const "label", "func:filter_non_widget_label(., $widget_elts)"; + apply ".", mode="json_table_elt_render" { + with "expressions", "func:json_expressions($expressions, $label)"; template "svg:g", mode="json_table_render" {
- | let obj_«@id» = «$objname»«substring-before(@inkscape:label, ' ')»;
- apply "*[@inkscape:label]", mode="json_table_render"
- with "objname" > obj_«@id»
+ /* TODO : use intermediate variables for optimization + foreach "$new_expressions" + | let obj_«@id»_«position()» = «.»; + const "label", "func:filter_non_widget_label(., $widget_elts)"; + apply "*", mode="json_table_render" { + with "expressions", "func:json_expressions($expressions, $label)"; + with "widget_elts", "$widget_elts"; template "widget[@type='JsonTable']", mode="widget_defs" {
@@ -87,6 +171,9 @@
optional_labels("forward backward cursor");
const "data_elt", "$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']";
| spread_json_data: function(jdata) {
- apply "$data_elt/*", mode="json_table_render" with "objname","'jdata'";
+ apply "$data_elt/*", mode="json_table_render" { + with "expressions","$initexpr_ns"; + with "widget_elts","$hmi_element/*[@inkscape:label = 'data']/descendant::svg:*"; --- a/tests/svghmi/py_ext_0@py_ext/pyfile.xml Thu Aug 20 13:52:00 2020 +0200
+++ b/tests/svghmi/py_ext_0@py_ext/pyfile.xml Thu Aug 20 13:56:21 2020 +0200
@@ -18,10 +18,10 @@
newdata = request.content.getvalue()
- {"name":"three", "sides":3},
- {"name":"four", "sides":4},
- {"name":"five", "sides":5},
- {"name":"six", "sides":6},
+ {"name":"three", "sides":3, "textstyle":"alarm"}, + {"name":"four", "sides":4, "textstyle":"ack"}, + {"name":"five", "sides":5, "textstyle":"active"}, + {"name":"six", "sides":6, "textstyle":"disabled"}, return json.dumps(selected_alarms)
--- a/tests/svghmi/svghmi_0@svghmi/svghmi.svg Thu Aug 20 13:52:00 2020 +0200
+++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg Thu Aug 20 13:56:21 2020 +0200
@@ -16,7 +16,7 @@
sodipodi:docname="svghmi.svg"
- inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"> @@ -182,17 +182,17 @@
inkscape:document-units="px"
- inkscape:current-layer="g907"
+ inkscape:current-layer="g1384" inkscape:zoom="0.50000001"
- inkscape:cx="632.83299"
- inkscape:cy="-317.59408"
- inkscape:window-width="2443"
- inkscape:window-height="1567"
- inkscape:window-x="816"
- inkscape:window-y="103"
- inkscape:window-maximized="0"
+ inkscape:cx="503.83301" + inkscape:window-width="1800" + inkscape:window-height="836" + inkscape:window-maximized="1" inkscape:guide-bbox="true" />
@@ -3236,7 +3236,7 @@
inkscape:connector-curvature="0"
d="M 130.96206,4.0725977 79.111776,-41.363223"
- style="fill:none;fill-rule:evenodd;stroke:#ff3000;stroke-width:2.96333337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0, 32.59666667;stroke-dashoffset:29.63333321;stroke-opacity:1;marker-end:url(#marker1656)" />
+ style="fill:none;fill-rule:evenodd;stroke:#ff3000;stroke-width:2.9633333;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:29.63333301;stroke-opacity:1;marker-end:url(#marker1656)" /> style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"