beremiz

Merge from default branch + fix traffic light example SVG missing background + update generated XSLT
--- a/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:46:26 2022 +0200
+++ b/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:53:35 2022 +0200
@@ -1251,14 +1251,14 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
- inkscape:cx="205.65994"
- inkscape:cy="103.00174"
+ inkscape:cx="52.116754"
+ inkscape:cy="96.940825"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
- inkscape:window-width="1600"
- inkscape:window-height="836"
+ inkscape:window-width="3840"
+ inkscape:window-height="2096"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
@@ -1274,7 +1274,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
+ <dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -1283,6 +1283,14 @@
inkscape:groupmode="layer"
id="layer1"
transform="translate(37.474617,-760.93329)">
+ <rect
+ style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ id="rect250"
+ width="320"
+ height="240"
+ x="-37.474617"
+ y="760.93329"
+ inkscape:label="HMI:Page:Home" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#282828;fill-opacity:1;stroke:none;stroke-width:2.04116011;marker:none;enable-background:accumulate"
d="m 114.28125,14.28125 v 130 h 18.9375 v 93.5625 h 5.71875 V 176.4375 h 8.90625 v 15.71875 h 36.4375 v -32.5 h -36.4375 v 12.125 h -8.90625 v -27.5 h 21.78125 v -130 z"
@@ -1529,13 +1537,5 @@
x="62.818459"
y="812.17749"
style="font-size:6.17188501px;line-height:1.25;font-family:sans-serif">ON</tspan></text>
- <rect
- style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
- id="rect250"
- width="320"
- height="240"
- x="-37.474617"
- y="760.93329"
- inkscape:label="HMI:Page:Home" />
</g>
</svg>
--- a/svghmi/gen_index_xhtml.xslt Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/gen_index_xhtml.xslt Sun Jul 17 22:53:35 2022 +0200
@@ -2059,6 +2059,10 @@
</xsl:text>
<xsl:text>}
</xsl:text>
+ <xsl:message terminate="no">
+ <xsl:value-of select="@type"/>
+ <xsl:text> widget is used in SVG but widget type is not declared</xsl:text>
+ </xsl:message>
</xsl:template>
<xsl:variable name="included_ids" select="$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id"/>
<xsl:variable name="hmi_widgets" select="$hmi_elements[@id = $included_ids]"/>
@@ -2080,6 +2084,8 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>
+</xsl:text>
</xsl:template>
<xsl:template name="defs_by_labels">
<xsl:param name="labels" select="''"/>
@@ -4880,6 +4886,7 @@
</xsl:variable>
<xsl:variable name="have_edit" select="string-length($edit_elt)&gt;0"/>
<xsl:value-of select="$edit_elt"/>
+ <xsl:variable name="action_elements" select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]"/>
<xsl:if test="$have_value">
<xsl:text> frequency: 5,
</xsl:text>
@@ -4920,6 +4927,14 @@
<xsl:text> },
</xsl:text>
</xsl:if>
+ <xsl:for-each select="$action_elements">
+ <xsl:text> action_elt_</xsl:text>
+ <xsl:value-of select="position()"/>
+ <xsl:text>: id("</xsl:text>
+ <xsl:value-of select="@id"/>
+ <xsl:text>"),
+</xsl:text>
+ </xsl:for-each>
<xsl:text> init: function() {
</xsl:text>
<xsl:if test="$have_edit">
@@ -4936,10 +4951,10 @@
<xsl:text> this.animate();
</xsl:text>
</xsl:if>
- <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>").onclick = () =&gt; this.on_op_click("</xsl:text>
+ <xsl:for-each select="$action_elements">
+ <xsl:text> this.action_elt_</xsl:text>
+ <xsl:value-of select="position()"/>
+ <xsl:text>.onclick = () =&gt; this.on_op_click("</xsl:text>
<xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
<xsl:text>");
</xsl:text>
@@ -5696,7 +5711,7 @@
</xsl:text>
<xsl:text>.fade-out-page {
</xsl:text>
- <xsl:text> animation: fadeOut 0.6s both;
+ <xsl:text> animation: cubic-bezier(0, 0.8, 0.6, 1) fadeOut 0.6s both;
</xsl:text>
<xsl:text>}
</xsl:text>
@@ -6105,6 +6120,14 @@
<xsl:text> },
</xsl:text>
</xsl:template>
+ <xsl:template match="widget[@type='List']" mode="widget_class">
+ <xsl:text>class </xsl:text>
+ <xsl:text>ListWidget</xsl:text>
+ <xsl:text> extends Widget{
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ </xsl:template>
<xsl:template match="widget[@type='ListSwitch']" mode="widget_desc">
<type>
<xsl:value-of select="@type"/>
@@ -7867,6 +7890,14 @@
<xsl:text> ].reverse(),
</xsl:text>
</xsl:template>
+ <xsl:template match="widget[@type='TextList']" mode="widget_class">
+ <xsl:text>class </xsl:text>
+ <xsl:text>TextListWidget</xsl:text>
+ <xsl:text> extends Widget{
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ </xsl:template>
<xsl:template match="widget[@type='TextStyleList']" mode="widget_desc">
<type>
<xsl:value-of select="@type"/>
@@ -7908,6 +7939,14 @@
<xsl:text> },
</xsl:text>
</xsl:template>
+ <xsl:template match="widget[@type='TextStyleList']" mode="widget_class">
+ <xsl:text>class </xsl:text>
+ <xsl:text>TextStyleListWidget</xsl:text>
+ <xsl:text> extends Widget{
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ </xsl:template>
<xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
<type>
<xsl:value-of select="@type"/>
@@ -10499,40 +10538,92 @@
</xsl:text>
<xsl:text>function animate() {
</xsl:text>
- <xsl:text> // Do the page swith if any one pending
-</xsl:text>
- <xsl:text> if(current_subscribed_page != current_visible_page){
-</xsl:text>
- <xsl:text> switch_visible_page(current_subscribed_page);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> while(widget = need_cache_apply.pop()){
-</xsl:text>
- <xsl:text> widget.apply_cache();
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> if(jumps_need_update) update_jumps();
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> apply_updates();
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> pending_widget_animates.forEach(widget =&gt; widget._animate());
-</xsl:text>
- <xsl:text> pending_widget_animates = [];
+ <xsl:text> let rearm = true;
+</xsl:text>
+ <xsl:text> do{
+</xsl:text>
+ <xsl:text> if(page_fading == "pending" || page_fading == "forced"){
+</xsl:text>
+ <xsl:text> if(page_fading == "pending")
+</xsl:text>
+ <xsl:text> svg_root.classList.add("fade-out-page");
+</xsl:text>
+ <xsl:text> page_fading = "in_progress";
+</xsl:text>
+ <xsl:text> if(page_fading_args.length)
+</xsl:text>
+ <xsl:text> setTimeout(function(){
+</xsl:text>
+ <xsl:text> switch_page(...page_fading_args);
+</xsl:text>
+ <xsl:text> },1);
+</xsl:text>
+ <xsl:text> break;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // Do the page swith if pending
+</xsl:text>
+ <xsl:text> if(page_switch_in_progress){
+</xsl:text>
+ <xsl:text> if(current_subscribed_page != current_visible_page){
+</xsl:text>
+ <xsl:text> switch_visible_page(current_subscribed_page);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> page_switch_in_progress = false;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> if(page_fading == "in_progress"){
+</xsl:text>
+ <xsl:text> svg_root.classList.remove("fade-out-page");
+</xsl:text>
+ <xsl:text> page_fading = "off";
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> while(widget = need_cache_apply.pop()){
+</xsl:text>
+ <xsl:text> widget.apply_cache();
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> if(jumps_need_update) update_jumps();
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> apply_updates();
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> pending_widget_animates.forEach(widget =&gt; widget._animate());
+</xsl:text>
+ <xsl:text> pending_widget_animates = [];
+</xsl:text>
+ <xsl:text> rearm = false;
+</xsl:text>
+ <xsl:text> } while(0);
</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> requestAnimationFrameID = null;
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> if(rearm) requestHMIAnimation();
+</xsl:text>
<xsl:text>}
</xsl:text>
<xsl:text>
@@ -10787,21 +10878,27 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text>var page_fading_in_progress = false;
+ <xsl:text>var page_fading = "off";
+</xsl:text>
+ <xsl:text>var page_fading_args = "off";
</xsl:text>
<xsl:text>function fading_page_switch(...args){
</xsl:text>
- <xsl:text> svg_root.classList.add("fade-out-page");
-</xsl:text>
- <xsl:text> page_fading_in_progress = true;
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> setTimeout(function(){
-</xsl:text>
- <xsl:text> switch_page(...args);
-</xsl:text>
- <xsl:text> },1);
+ <xsl:text> if(page_fading == "in_progress")
+</xsl:text>
+ <xsl:text> page_fading = "forced";
+</xsl:text>
+ <xsl:text> else
+</xsl:text>
+ <xsl:text> page_fading = "pending";
+</xsl:text>
+ <xsl:text> page_fading_args = args;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> requestHMIAnimation();
+</xsl:text>
+ <xsl:text>
</xsl:text>
<xsl:text>}
</xsl:text>
@@ -11163,6 +11260,8 @@
</xsl:text>
<xsl:text>var page_node_local_index = hmi_local_index("page_node");
</xsl:text>
+ <xsl:text>var page_switch_in_progress = false;
+</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text>function toggleFullscreen() {
@@ -11217,7 +11316,7 @@
</xsl:text>
<xsl:text>function switch_page(page_name, page_index) {
</xsl:text>
- <xsl:text> if(current_subscribed_page != current_visible_page){
+ <xsl:text> if(page_switch_in_progress){
</xsl:text>
<xsl:text> /* page switch already going */
</xsl:text>
@@ -11227,6 +11326,8 @@
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text> page_switch_in_progress = true;
+</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> if(page_name == undefined)
@@ -11409,12 +11510,6 @@
</xsl:text>
<xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
</xsl:text>
- <xsl:text> if(page_fading_in_progress)
-</xsl:text>
- <xsl:text> svg_root.classList.remove("fade-out-page");
-</xsl:text>
- <xsl:text> page_fading_in_progress = false;
-</xsl:text>
<xsl:text> current_visible_page = page_name;
</xsl:text>
<xsl:text>};
@@ -11539,6 +11634,24 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>
+//
+//
+// Declarations from SVG scripts (inkscape document properties)
+//
+//
+</xsl:text>
+ <xsl:for-each select="/svg:svg/svg:script">
+ <xsl:text>
+</xsl:text>
+ <xsl:text>/* </xsl:text>
+ <xsl:value-of select="@id"/>
+ <xsl:text> */
+</xsl:text>
+ <xsl:value-of select="text()"/>
+ <xsl:text>
+</xsl:text>
+ </xsl:for-each>
</script>
</body>
</html>
--- a/svghmi/gen_index_xhtml.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/gen_index_xhtml.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -102,6 +102,12 @@
include text svghmi.js
+ | \n//\n//\n// Declarations from SVG scripts (inkscape document properties) \n//\n//
+ foreach "/svg:svg/svg:script" {
+ |
+ | /* «@id» */
+ | «text()»
+ }
}
}
}
--- a/svghmi/svghmi.js Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/svghmi.js Sun Jul 17 22:53:35 2022 +0200
@@ -59,32 +59,49 @@
// Called on requestAnimationFrame, modifies DOM
var requestAnimationFrameID = null;
function animate() {
- // Do the page swith if any one pending
- if(page_switch_in_progress){
- if(current_subscribed_page != current_visible_page){
- switch_visible_page(current_subscribed_page);
+ let rearm = true;
+ do{
+ if(page_fading == "pending" || page_fading == "forced"){
+ if(page_fading == "pending")
+ svg_root.classList.add("fade-out-page");
+ page_fading = "in_progress";
+ if(page_fading_args.length)
+ setTimeout(function(){
+ switch_page(...page_fading_args);
+ },1);
+ break;
}
- page_switch_in_progress = false;
+ // Do the page swith if pending
+ if(page_switch_in_progress){
+ if(current_subscribed_page != current_visible_page){
+ switch_visible_page(current_subscribed_page);
+ }
- if(page_fading_in_progress){
- svg_root.classList.remove("fade-out-page");
- page_fading_in_progress = false;
- }
- }
+ page_switch_in_progress = false;
- while(widget = need_cache_apply.pop()){
- widget.apply_cache();
- }
+ if(page_fading == "in_progress"){
+ svg_root.classList.remove("fade-out-page");
+ page_fading = "off";
+ }
+ }
- if(jumps_need_update) update_jumps();
+ while(widget = need_cache_apply.pop()){
+ widget.apply_cache();
+ }
+
+ if(jumps_need_update) update_jumps();
- apply_updates();
+ apply_updates();
- pending_widget_animates.forEach(widget => widget._animate());
- pending_widget_animates = [];
+ pending_widget_animates.forEach(widget => widget._animate());
+ pending_widget_animates = [];
+ rearm = false;
+ } while(0);
requestAnimationFrameID = null;
+
+ if(rearm) requestHMIAnimation();
}
function requestHMIAnimation() {
@@ -212,14 +229,17 @@
}
-var page_fading_in_progress = false;
+var page_fading = "off";
+var page_fading_args = "off";
function fading_page_switch(...args){
- svg_root.classList.add("fade-out-page");
- page_fading_in_progress = true;
+ if(page_fading == "in_progress")
+ page_fading = "forced";
+ else
+ page_fading = "pending";
+ page_fading_args = args;
- setTimeout(function(){
- switch_page(...args);
- },1);
+ requestHMIAnimation();
+
}
document.body.style.backgroundColor = "black";
--- a/svghmi/widget_jump.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widget_jump.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -145,7 +145,7 @@
emit "cssdefs:jump"
||
.fade-out-page {
- animation: fadeOut 0.6s both;
+ animation: cubic-bezier(0, 0.8, 0.6, 1) fadeOut 0.6s both;
}
@keyframes fadeOut {
--- a/svghmi/widget_list.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widget_list.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -27,3 +27,4 @@
| },
}
+widget_class("List");
--- a/svghmi/widget_textlist.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widget_textlist.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -26,3 +26,5 @@
// could find a proper way in xpath to reverse()
| ].reverse(),
}
+
+widget_class("TextList");
--- a/svghmi/widget_textstylelist.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widget_textstylelist.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -26,3 +26,4 @@
| },
}
+widget_class("TextStyleList");
--- a/svghmi/widgets_common.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widgets_common.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -477,14 +477,17 @@
generate-id() = generate-id(key('TypesKey', @type)) and
not(@type = $excluded_types)]""";
apply "$used_widget_types", mode="widget_class";
+
}
-template "widget", mode="widget_class"
-||
-class «@type»Widget extends Widget{
- /* empty class, as «@type» widget didn't provide any */
+template "widget", mode="widget_class" {
+ ||
+ class «@type»Widget extends Widget{
+ /* empty class, as «@type» widget didn't provide any */
+ }
+ ||
+ warning > «@type» widget is used in SVG but widget type is not declared
}
-||
const "included_ids","$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id";
const "hmi_widgets","$hmi_elements[@id = $included_ids]";
@@ -494,6 +497,7 @@
| var hmi_widgets = {
apply "$hmi_widgets", mode="hmi_widgets";
| }
+ |
}
function "defs_by_labels" {
--- a/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:46:26 2022 +0200
+++ b/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:53:35 2022 +0200
@@ -18,6 +18,14 @@
viewBox="0 0 1280 720"
height="720"
width="1280">
+ <script
+ id="script1244">
+hmi_widgets[&quot;g443-3&quot;].off_action = function(){
+ console.log(&quot;Hello from Inkscape&quot;);
+ PushButtonWidget.prototype.off_action.call(this);
+ fading_page_switch();
+ console.log(&quot;Bye from Inkscape&quot;);
+}</script>
<metadata
id="metadata4542">
<rdf:RDF>
@@ -125,12 +133,12 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:document-units="px"
- inkscape:current-layer="g5053"
+ inkscape:current-layer="hmi0"
showgrid="false"
units="px"
- inkscape:zoom="0.80184804"
- inkscape:cx="784.66046"
- inkscape:cy="-449.34319"
+ inkscape:zoom="0.14174805"
+ inkscape:cx="-1530.0784"
+ inkscape:cy="-1404.9832"
inkscape:window-width="1600"
inkscape:window-height="836"
inkscape:window-x="0"
@@ -8399,4 +8407,47 @@
sodipodi:role="line">Freq4</tspan></text>
</g>
</g>
+ <g
+ transform="matrix(0.57180538,0,0,0.57180538,-77.992226,121.53383)"
+ id="g443-3"
+ inkscape:label="HMI:PushButton@/SELECTION"
+ style="stroke-width:1">
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5492-6"
+ width="245.44583"
+ height="95.723877"
+ x="971.96545"
+ y="594.82263"
+ ry="23.930969"
+ inkscape:label="inactive"
+ rx="23.930969" />
+ <rect
+ rx="23.930969"
+ inkscape:label="active"
+ ry="23.930969"
+ y="594.82263"
+ x="971.96545"
+ height="95.723877"
+ width="245.44583"
+ id="rect433-7"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fdfdfd;fill-opacity:1;fill-rule:nonzero;stroke:#ffd0b2;stroke-width:28.60938263;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <g
+ style="stroke-width:1"
+ inkscape:label="text"
+ id="g952-5">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1090.7626"
+ y="656.98151"
+ id="text950-3"
+ inkscape:label="setting_jmp"><tspan
+ sodipodi:role="line"
+ id="tspan948-5"
+ x="1090.7626"
+ y="656.98151"
+ style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.99999994px">up</tspan></text>
+ </g>
+ </g>
</svg>