bartbutenaers / node-red-contrib-ui-svg

A Node-RED widget node to show interactive SVG (vector graphics) in the dashboard
Apache License 2.0
93 stars 27 forks source link

Unresponsive SVG files #70

Closed byunchov closed 3 years ago

byunchov commented 3 years ago

Hi! I have recentrly started wokring with your project and I quite like how easy it is to use. But i have faced some problems regarding the work with fairly vector crowded SVG files. Currently I'm tinkering around trying to find what suits my needs, however I have stumbled upon an annoying bug regarding the interaction with the SVG file. When there is small number of vectors everyting works as expected. But when I add more vectors things start to act out strangely. The event handlers are not responding and I'm unable to change any of the vectors' proprties. I've tought that maybe I'm doing something stupid and wrong, but when I have opened the console it was full of various errors. So i decided to open up an issue on GitHub and maybe help someone else with the same problem. That's my Node Red test flow:

[{"id":"b1ed5962.a8bce8","type":"debug","z":"15a060b.ba89d9f","name":"Floorplan output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","statusVal":"","statusType":"auto","x":640,"y":1820,"wires":[]},{"id":"4da1b9d.8237948","type":"ui_svg_graphics","z":"15a060b.ba89d9f","group":"7bb4a379.1539bc","order":1,"width":"0","height":"0","svgString":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"950px\" height=\"415px\" viewBox=\"-0.025699207559227943 0 950.0513916015625 415\" preserveAspectRatio=\"xMinYMin meet\">\n  <rect id=\"svgEditorBackground\" x=\"0\" y=\"0\" width=\"1550\" height=\"710\" style=\"fill: none; stroke: none;\" />\n  <defs id=\"svgEditorDefs\">\n    <linearGradient gradientUnits=\"objectBoundingBox\" id=\"lgrd2-peachpuff-sienna-v\" spreadMethod=\"pad\" x1=\"0%\" x2=\"0%\" y1=\"0%\" y2=\"100%\">\n      <stop offset=\"0%\" style=\"stop-color:peachpuff;  stop-opacity:0.84;\" />\n      <stop offset=\"100%\" style=\"stop-color:sienna;  stop-opacity:1\" />\n    </linearGradient>\n    <radialGradient cx=\"50%\" cy=\"50%\" fx=\"50%\" fy=\"50%\" id=\"led-green\" r=\"50%\" gradientUnits=\"objectBoundingBox\" spreadMethod=\"pad\" gradientTransform=\"\">\n      <stop offset=\"0\" style=\"stop-opacity: 0.809; stop-color: rgb(50, 205, 50);\" />\n      <stop offset=\"1\" style=\"stop-color: rgb(0, 128, 0); stop-opacity: 0.884;\" />\n    </radialGradient>\n    <radialGradient cx=\"50%\" cy=\"50%\" fx=\"50%\" fy=\"50%\" id=\"led-red\" r=\"50%\" gradientUnits=\"objectBoundingBox\" spreadMethod=\"pad\" gradientTransform=\"\">\n      <stop offset=\"0\" style=\"stop-opacity: 0.809; stop-color: rgb(255, 99, 99);\" />\n      <stop offset=\"1\" style=\"stop-color: rgb(255, 0, 0); stop-opacity: 0.884;\" />\n    </radialGradient>\n    <linearGradient gradientUnits=\"objectBoundingBox\" id=\"lgrd2-black-white\" spreadMethod=\"pad\" x1=\"0%\" x2=\"100%\" y1=\"0%\" y2=\"100%\">\n      <stop offset=\"0%\" stop-color=\"black\" />\n      <stop offset=\"100%\" stop-color=\"white\" />\n    </linearGradient>\n    <polygon id=\"svgEditorIconDefs\" style=\"fill:rosybrown;\" />\n    <symbol xmlns=\"http://www.w3.org/2000/svg\" id=\"f769\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 512 512\">\n      <path d=\"M416 0c-52.9 0-96 43.1-96 96s43.1 96 96 96 96-43.1 96-96-43.1-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm-160-16C256 50.1 205.9 0 144 0S32 50.1 32 112v166.5C12.3 303.2 0 334 0 368c0 79.5 64.5 144 144 144s144-64.5 144-144c0-34-12.3-64.9-32-89.5V112zM144 448c-44.1 0-80-35.9-80-80 0-25.5 12.2-48.9 32-63.8V112c0-26.5 21.5-48 48-48s48 21.5 48 48v192.2c19.8 14.8 32 38.3 32 63.8 0 44.1-35.9 80-80 80zm16-125.1V112c0-8.8-7.2-16-16-16s-16 7.2-16 16v210.9c-18.6 6.6-32 24.2-32 45.1 0 26.5 21.5 48 48 48s48-21.5 48-48c0-20.9-13.4-38.5-32-45.1z\" />\n    </symbol>\n    <linearGradient gradientUnits=\"objectBoundingBox\" id=\"termomrter\" spreadMethod=\"pad\" x1=\"8.4%\" x2=\"97%\" y1=\"100%\" y2=\"31%\">\n      <stop offset=\"0\" stop-color=\"black\" style=\"stop-opacity: 0.916;\" />\n      <stop offset=\"100%\" stop-color=\"#404040\" style=\"stop-opacity: 1;\" />\n    </linearGradient>\n  </defs>\n  <circle id=\"e1_circle\" cx=\"126.871\" cy=\"59.452\" style=\"fill:url(#led-green);stroke:black;stroke-width:1px;\" r=\"23.403\" />\n  <ellipse id=\"e2_ellipse\" cx=\"448.5292053222656\" cy=\"110.58894348144533\" style=\"fill:url(#lgrd2-peachpuff-sienna-v);stroke:black;stroke-width:1px;\" rx=\"129.32258064516128\" ry=\"79.67741935483866\" />\n  <rect x=\"242.5\" y=\"245\" style=\"fill: url(#termomrter); stroke-width: 1px;stroke:silver;\" id=\"e3_rectangle\" width=\"160\" height=\"105\" rx=\"10\" ry=\"10\" />\n  <text style=\"fill:black;font-family:Arial;font-size:20px;\" x=\"301.043\" y=\"277.276\" id=\"e1_texte\" /><text style=\"fill:black;font-family:Arial;font-size:20px;\" x=\"641.696\" y=\"258.385\" id=\"e2_texte\" />\n  <rect x=\"285\" y=\"252.5\" style=\"fill:black;stroke:none;stroke-width:1px;\" id=\"e2_rectangle\" width=\"110\" height=\"65\" rx=\"5\" />\n  <text style=\"fill:firebrick; font-family: &quot;Arial Black&quot;; font-size: 50pt; font-weight: bolder; text-anchor: end;\" x=\"390.0000915527344\" y=\"305.99993896484375\" id=\"temp_in\">100</text>\n  <circle id=\"e2_circle\" cx=\"270\" cy=\"270\" style=\"fill:url(#led-green);stroke:none;stroke-width:1px;\" r=\"25\" transform=\"matrix(0.375995 0 0 0.375995 158.477 160.211)\" />\n  <text style=\"fill:white;font-family:Arial Black;font-size:20pt;font-weight:bolder;text-anchor:middle;\" x=\"263\" y=\"314\" id=\"e4_texte\">°C</text>\n  <rect x=\"415.00015146756834\" y=\"245\" style=\"fill: url(#termomrter); stroke-width: 1px;stroke:silver;\" id=\"e4_rectangle\" width=\"165\" height=\"105\" rx=\"10\" ry=\"10\" />\n  <rect x=\"452.50015146756834\" y=\"252.5\" style=\"fill:black;stroke:black;stroke-width:1px;\" id=\"e1_rectangle\" width=\"120\" height=\"65\" rx=\"5\" />\n  <text style=\"fill:firebrick; font-family: &quot;Arial Black&quot;; font-size:48pt; font-weight: bolder; text-anchor: end;\" x=\"569.2001342773438\" y=\"307\" id=\"e1_in\">99.9</text>\n  <circle id=\"e3_circle\" cx=\"724.9999466874528\" cy=\"270\" style=\"fill:url(#led-green);stroke:none;stroke-width:1px;\" r=\"25\" transform=\"matrix(0.375995 0 0 0.375995 158.477 160.211)\" />\n  <text style=\"fill:white;font-family:Arial Black;font-size:20pt;font-weight:bolder;text-anchor:middle;\" x=\"434.0001525878906\" y=\"314\" id=\"e3_texte\">°C</text>\n  <text style=\"fill:white;font-family:Arial Black;font-size:20px;font-weight:bold;text-anchor:middle;\" x=\"322.0000305175781\" y=\"340.00006103515625\" id=\"e5_texte\">ВХОД</text>\n  <text style=\"fill:white;font-family:Arial Black;font-size:20px;font-weight:bold;text-anchor:middle;\" x=\"750\" y=\"340\" id=\"e6_texte\" dx=\"0\" dy=\"0\">ВХОД</text>\n  <text style=\"fill:white;font-family:Arial Black;font-size:20px;font-weight:bold;text-anchor:middle;\" x=\"495.5\" y=\"340\" id=\"e7_texte\">ИЗХОД</text>\n</svg>","clickableShapes":[{"targetId":"#e1_circle","action":"click","payload":"#e1_circle","payloadType":"str","topic":"#e1_circle"}],"smilAnimations":[],"bindings":[{"selector":"#temp_in","bindSource":"payload.tin","bindType":"text","attribute":""}],"showCoordinates":false,"autoFormatAfterEdit":true,"showBrowserErrors":false,"outputField":"","editorUrl":"http://drawsvg.org/drawsvg.html","directory":"","panning":"disabled","zooming":"disabled","panOnlyWhenZoomed":false,"doubleClickZoomEnabled":false,"mouseWheelZoomEnabled":false,"name":"","x":440,"y":1820,"wires":[["b1ed5962.a8bce8"]]},{"id":"3e8e0aeb.4dc8d6","type":"inject","z":"15a060b.ba89d9f","name":"manual injector","props":[{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"databind","payload":"{\"tin\":105}","payloadType":"json","x":240,"y":1800,"wires":[["4da1b9d.8237948"]]},{"id":"1653de5d.358ab2","type":"inject","z":"15a060b.ba89d9f","name":"selector","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"command\":\"update_text\",\"selector\":\"#temp_in\",\"textContent\":105}","payloadType":"json","x":210,"y":1840,"wires":[["4da1b9d.8237948"]]},{"id":"7bb4a379.1539bc","type":"ui_group","z":"","name":"Floorplan test","tab":"7b74af43.9058a","order":1,"disp":true,"width":"18","collapse":false},{"id":"7b74af43.9058a","type":"ui_tab","z":"","name":"SVG","icon":"dashboard","disabled":false,"hidden":false}]

I have also appended the console log file so you colud get a clearer picture of what may be going on. console.log I'm sorry if I have opened a new issue for the same problem. Every guidance is appreciated. Thanks in advance.

bartbutenaers commented 3 years ago

Hi Bozhidar (@byunchov), Sorry for the delay. I must be getting nuts: I investigated 6 days ago your issue, fixed it on Github (as you can see in the history of the code), and added a large explanation of the solution here. But I must have forgotten somehow to post my comment ...

Summarized: You have a few times &quot; in your svg, which causes AngularJs to raise a parsing exception (which you can see when you have a look at your browser console log, where you are displaying your dashboard). I assume you don't use DrawSvg (because the recent version shouldn't generate those &quot; anymore), but instead you have used some third party SVG editor?

It is now fixed on Github (not on NPM yet!), so you should be able to solve it in two different ways:

  1. Remove manually those &quot; a couple of times in your SVG string.
  2. Install the latest version directly from Github, from within your .node-red folder:
    npm install bartbutenaers/node-red-contrib-ui-svg

Solution 2 is better, since the problem should be solved for you and other users. Therefore it would be nice if you could test my fix (second solution), and let me know whether it works. Then I can publish the fix on NPM.

Thanks and sorry for the (unwanted) delay! Bart

byunchov commented 3 years ago

Thank you Bart (@bartbutenaers)! I have installed the latest version directly from Github and everything seem to work as expected. I have noticed the &quot; in the document generated from DrawSVG, but Ididn't suspect that it was the main culprit. Thank you for the reply. I have managed to solve the problem with the node-red-contrib-dashboard template node and little bit of JS magic. I will stick to this solution as it gives me some flexibility on howI handle click events. I will try to implement my idea with your repo as well and if you'd like I would give you a feed back of how it all turned out to be Don't be so worried about the delay. I know how hard it is to keep up with a lot of work over your head.

bartbutenaers commented 3 years ago

Thanks for the fast response!

it gives me some flexibility on howI handle click events.

If there are any improvements I can make to allow the same behaviour, please let me know!

byunchov commented 3 years ago

I feel how my issue report is slowly phasing into a feature request. Maybe I should have given more information about what I am trying to achieve. Me and my brother are making a pellet line consisting of furnace, drying drum, suction cyclone, shredder and a handfull of other noisy machinery. My goal is to make the process almost fully autonomous with dummy-friendly GUI, which is not crappy looking and is feature dence. I stumbled upon your nice project and honestly I really liked what functionalities it offered. Unfortunately I was unable to play around with all of this, because of this small bug. I will use my DXF files from my layout plans to turn them into SVG in scale. What I want is when particular graphic is clicked a specific SVG filter to be applied so that it mimics the behaviour of a toggle button. Then, depending on the current state of the button after a reset button is pressed the selected machine to be turned on/off and the state of the selector graphics to be restet to normal (the filter to be cleared). Rigth now I do it in JS and I know there migth be a workaround with a fuction node and maybe some other nodes but the flow gets crowded considering that I will have 20+ "selectors". If you are interesetd I could share my dummy code and maybe we could figure something out?

Roughly that's what I'm trying to achieve:

[{"id":"74cdaa01.710ae4","type":"ui_template","z":"15a060b.ba89d9f","group":"8c42d853.3e44f8","name":"Сушилна","order":1,"width":18,"height":10,"format":"<!--<div ng-bind-html=\"msg.payload\"></div>-->\n<div style=\"width:100%;height:100%\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" id=\"suhhilna\" width=\"950\" height=\"515\" viewBox=\"0 0 950 515\" preserveAspectRatio=\"xMidYMin meet\" version=\"1.1\">\n      <rect id=\"svgEditorBackground\" x=\"0\" y=\"0\" width=\"1550\" height=\"710\" style=\"fill:gray;stroke:none\" />\n      <defs id=\"svgEditorDefs\">\n          <filter id=\"innershadow\" x0=\"-50%\" y0=\"-50%\" width=\"200%\" height=\"200%\">\n            <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"7\" result=\"blur\"></feGaussianBlur>\n            <feOffset dy=\"4\" dx=\"4\"></feOffset>\n            <feComposite in2=\"SourceAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" result=\"shadowDiff\"></feComposite>\n            <feFlood flood-color=\"#444444\" flood-opacity=\"0.8\"></feFlood>\n            <feComposite in2=\"shadowDiff\" operator=\"in\"></feComposite>\n            <feComposite in2=\"SourceGraphic\" operator=\"over\" result=\"firstfilter\"></feComposite>\n            <feGaussianBlur in=\"firstfilter\" stdDeviation=\"10\" result=\"blur2\"></feGaussianBlur>            \n            <feOffset dy=\"-4\" dx=\"-4\"></feOffset>\n            <feComposite in2=\"firstfilter\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" result=\"shadowDiff\"></feComposite>\n            <feFlood flood-color=\"#444444\" flood-opacity=\"0.9\"></feFlood>\n            <feComposite in2=\"shadowDiff\" operator=\"in\"></feComposite>\n            <feComposite in2=\"firstfilter\" operator=\"over\"></feComposite>\n\t\t</filter>\n        <linearGradient y2=\"0.31\" y1=\"1\" x2=\"0.97000003\" x1=\"0.083999999\" spreadMethod=\"pad\" id=\"metal\" gradientUnits=\"objectBoundingBox\">\n          <stop id=\"stop4956\" style=\"stop-opacity:1;stop-color:#414141\" stop-color=\"black\" offset=\"0\" />\n          <stop id=\"stop4958\" style=\"stop-opacity:1;stop-color:#888888\" stop-color=\"#404040\" offset=\"100%\" />\n        </linearGradient>\n        <linearGradient gradientUnits=\"objectBoundingBox\" id=\"lgrd2-peachpuff-sienna-v\" spreadMethod=\"pad\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n          <stop offset=\"0%\" style=\"stop-color:peachpuff;  stop-opacity:0.84;\" id=\"stop4054\" />\n          <stop offset=\"100%\" style=\"stop-color:sienna;  stop-opacity:1\" id=\"stop4056\" />\n        </linearGradient>\n        <linearGradient gradientUnits=\"objectBoundingBox\" id=\"lgrd2-black-white\" spreadMethod=\"pad\" x1=\"0\" x2=\"1\" y1=\"0\" y2=\"1\">\n          <stop offset=\"0%\" stop-color=\"black\" id=\"stop4059\" />\n          <stop offset=\"100%\" stop-color=\"white\" id=\"stop4061\" />\n        </linearGradient>\n        <polygon id=\"svgEditorIconDefs\" style=\"fill:#bc8f8f\" />\n        <polygon id=\"svgEditorShapeDefs\" style=\"vector-effect:non-scaling-stroke;fill:#bc8f8f;stroke:#000000;stroke-width:1px\" />\n        <path id=\"svgEditorClosePathDefs\" style=\"fill:#bc8f8f;stroke:#000000;stroke-width:1px\" d=\"\" inkscape:connector-curvature=\"0\" />\n        <linearGradient gradientUnits=\"userSpaceOnUse\" id=\"btn_green\" spreadMethod=\"reflect\" x1=\"300.98605\" x2=\"294.3204\" y1=\"446.24188\" y2=\"398.87521\" \n            gradientTransform=\"matrix(1.6832508,0,0,0.59408853,110.20684,33.165468)\">\n          <stop offset=\"0.185455\" stop-color=\"forestgreen\" style=\"stop-opacity: 1;\" id=\"stop4067\" />\n          <stop offset=\"1\" stop-color=\"limegreen\" style=\"stop-opacity: 0.936;\" id=\"stop4069\" />\n        </linearGradient>\n        <radialGradient cx=\"0.5\" cy=\"0.5\" fx=\"0.5\" fy=\"0.5\" id=\"led-green\" r=\"0.5\" gradientUnits=\"objectBoundingBox\" spreadMethod=\"pad\">\n          <stop offset=\"0\" style=\"stop-opacity: 1; stop-color: rgb(50, 205, 50);\" id=\"stop4072\" />\n          <stop offset=\"1\" style=\"stop-color: rgb(0, 128, 0); stop-opacity: 0.884;\" id=\"stop4074\" />\n        </radialGradient>\n        <radialGradient cx=\"0.5\" cy=\"0.5\" fx=\"0.5\" fy=\"0.5\" id=\"led-red\" r=\"0.5\" gradientUnits=\"objectBoundingBox\" spreadMethod=\"pad\">\n          <stop offset=\"0\" style=\"stop-opacity: 1; stop-color: rgb(255, 99, 99);\" id=\"stop4077\" />\n          <stop offset=\"1\" style=\"stop-color: rgb(255, 0, 0); stop-opacity: 0.884;\" id=\"stop4079\" />\n        </radialGradient>\n        <linearGradient gradientUnits=\"objectBoundingBox\" id=\"termomrter\" spreadMethod=\"pad\" x1=\"0.083999999\" x2=\"0.97000003\" y1=\"1\" y2=\"0.31\">\n          <stop offset=\"0\" stop-color=\"black\" style=\"stop-opacity: 1;\" id=\"stop4082\" />\n          <stop offset=\"100%\" stop-color=\"#404040\" style=\"stop-opacity: 1;\" id=\"stop4084\" />\n        </linearGradient>\n        <linearGradient inkscape:collect=\"always\" xlink:href=\"#termomrter\" id=\"linearGradient4117\" gradientUnits=\"userSpaceOnUse\" x1=\"251.83737\" y1=\"565.56995\" x2=\"367.58167\" y2=\"475.43048\" spreadMethod=\"pad\" gradientTransform=\"matrix(1.2324236,0,0,0.81140933,0,40)\" />\n        <radialGradient inkscape:collect=\"always\" xlink:href=\"#led-red\" id=\"radialGradient4119\" gradientUnits=\"userSpaceOnUse\" cx=\"415.86823\" cy=\"558.32318\" fx=\"415.86823\" fy=\"558.32318\" r=\"25\" spreadMethod=\"pad\" gradientTransform=\"matrix(0.375995,0,0,0.375995,158.477,200.211)\" />\n        <linearGradient inkscape:collect=\"always\" xlink:href=\"#termomrter\" id=\"linearGradient4127\" gradientUnits=\"userSpaceOnUse\" x1=\"386.19556\" y1=\"574.28491\" x2=\"503.72339\" y2=\"482.7565\" spreadMethod=\"pad\" gradientTransform=\"matrix(1.2514143,0,0,0.79909587,0,40)\" />\n        <radialGradient inkscape:collect=\"always\" xlink:href=\"#led-red\" id=\"radialGradient4129\" gradientUnits=\"userSpaceOnUse\" cx=\"870.86816\" cy=\"558.32318\" fx=\"870.86816\" fy=\"558.32318\" r=\"25\" spreadMethod=\"pad\" gradientTransform=\"matrix(0.375995,0,0,0.375995,158.477,200.211)\" />\n        <radialGradient inkscape:collect=\"always\" xlink:href=\"#led-green\" id=\"radialGradient4139\" gradientUnits=\"userSpaceOnUse\" cx=\"271.90759\" cy=\"91.749794\" fx=\"271.90759\" fy=\"91.749794\" r=\"10\" spreadMethod=\"pad\" gradientTransform=\"translate(-5.9802136,8.390288)\" />\n        <linearGradient inkscape:collect=\"always\" xlink:href=\"#metal\" id=\"linearGradient4954\" x1=\"302.42807\" y1=\"309.26257\" x2=\"358.81293\" y2=\"91.411865\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1.2232347,0,0,1.1944445,107.34421,-49.964281)\" />\n        <linearGradient inkscape:collect=\"always\" xlink:href=\"#metal\" id=\"linearGradient4970\" x1=\"811.60077\" y1=\"338.30933\" x2=\"862.00537\" y2=\"127.29317\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1,0,0,0.99033605,44.42446,-21.113614)\" />\n        <radialGradient inkscape:collect=\"always\" xlink:href=\"#led-green\" id=\"radialGradient5046\" gradientUnits=\"userSpaceOnUse\" cx=\"23.273359\" cy=\"49.701633\" fx=\"23.273359\" fy=\"49.701633\" r=\"10\" spreadMethod=\"pad\" gradientTransform=\"translate(0,40)\" />\n        <linearGradient inkscape:collect=\"always\" xlink:href=\"#termomrter\" id=\"linearGradient5048\" gradientUnits=\"userSpaceOnUse\" x1=\"15.317049\" y1=\"182.34583\" x2=\"176.87546\" y2=\"56.527206\" spreadMethod=\"pad\" gradientTransform=\"scale(5.2098807,0.19194297)\" />\n      </defs>\n      <circle id=\"led1\" cx=\"23.273359\" cy=\"89.70163\" style=\"fill:url(#led-red);stroke:none;stroke-width:1px\" r=\"10\" />\n      <rect ry=\"10\" rx=\"10\" height=\"105\" width=\"160\" id=\"e3_rectangle\" style=\"fill:url(#linearGradient4117);stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" y=\"393.40872\" x=\"297.34631\" />\n      <rect rx=\"5\" height=\"65\" width=\"110\" id=\"e2_rectangle\" style=\"fill:#000000;stroke:none;stroke-width:1px\" y=\"400.90884\" x=\"339.8465\" />\n      <text class=\"temp\" id=\"temp_in\" y=\"458.40881\" x=\"444.84659\" style=\"font-weight:bolder;font-size:66.66666412px;font-family:'Arial Black';text-anchor:end;fill:#b22222\">100</text>\n      <circle r=\"9.3998747\" style=\"fill:url(#led-red);stroke:none;stroke-width:0.37599501px\" cy=\"410.13773\" cx=\"314.84137\" id=\"led_tin\" />\n      <text id=\"e4_texte\" y=\"462.40887\" x=\"317.8465\" style=\"font-weight:bolder;font-size:26.66666603px;font-family:'Arial Black';text-anchor:middle;fill:#ffffff\">°C</text>\n      <text id=\"e5_texte\" y=\"488.40894\" x=\"376.84653\" style=\"font-weight:bold;font-size:20px;font-family:'Arial Black';text-anchor:middle;fill:#ffffff\">ВХОД</text>\n      <rect ry=\"10\" rx=\"10\" height=\"105\" width=\"165\" id=\"e4_rectangle\" style=\"fill:url(#linearGradient4127);stroke:none;stroke-width:1px\" y=\"393.40872\" x=\"469.84665\" />\n      <rect rx=\"5\" height=\"65\" width=\"120\" id=\"e1_rectangle\" style=\"fill:#000000;stroke:#000000;stroke-width:1px\" y=\"400.90884\" x=\"507.34665\" />\n      <text class=\"temp\" id=\"temp_out\" y=\"457.40887\" x=\"624.04663\" style=\"font-weight:bolder;font-size:64px;font-family:'Arial Black';text-anchor:end;fill:#b22222\">99.9</text>\n      <circle r=\"9.3998747\" style=\"fill:url(#led-red);stroke:none;stroke-width:0.37599501px\" cy=\"410.13773\" cx=\"485.91907\" id=\"led_tout\" />\n      <text id=\"e3_texte\" y=\"462.40887\" x=\"488.84665\" style=\"font-weight:bolder;font-size:26.66666603px;font-family:'Arial Black';text-anchor:middle;fill:#ffffff\">°C</text>\n      <text id=\"e7_texte\" y=\"488.40887\" x=\"550.3465\" style=\"font-weight:bold;font-size:20px;font-family:'Arial Black';text-anchor:middle;fill:#ffffff\">ИЗХОД</text>\n      <rect x=\"0\" y=\"0\" style=\"fill:url(#linearGradient5048);stroke:none;stroke-width:1px\" id=\"e5_rectangle\" width=\"950\" height=\"35\" />\n      <text style=\"font-weight:bolder;font-size:25px;font-family:Arial;text-anchor:middle;fill:#ffffff\" x=\"475.32327\" y=\"26.105968\" id=\"header\" dx=\"0\" dy=\"0\">СУШИЛНЯ</text>\n      <rect x=\"605.62189\" y=\"268.39267\" style=\"fill:url(#btn_green);stroke:none;stroke-width:1px\" id=\"btn_baraban\" width=\"85\" height=\"30\" rx=\"5\" />\n      <rect style=\"fill:url(#linearGradient4954);fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" id=\"baraban\" filter=\"\" width=\"458.76797\" height=\"183.67805\" x=\"240.06296\" y=\"77.589935\" inkscape:label=\"#baraban\" />\n      <circle id=\"led_baraban\" cx=\"265.92737\" cy=\"100.14008\" style=\"fill:url(#led-red);stroke:none;stroke-width:1px\" r=\"10\" />\n      <path style=\"fill:url(#linearGradient4970);fill-opacity:1;stroke:#000000;stroke-width:0.99515629px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\" d=\"M 815.8723,59.262131 V 227.47419 l 30.3537,54.75823 h 53.15574 l 28.55847,-54.58178 V 59.26213 Z\" id=\"ciclon\" filter=\"\" />\n      <circle id=\"led_ciclon\" cx=\"850\" cy=\"89\" style=\"fill:url(#led-red);stroke:none\" r=\"10\" />\n      \n      <rect x=\"20\" y=\"0\" style=\"stroke:none;display:none\" id=\"reload_bar\" width=\"910\" height=\"55\" class=\"reload_anim reload_ind\"/>\n      <text style=\"font-weight:bolder;font-size:25px;font-family:Arial;text-anchor:middle;fill:#ffffff;display:none\" x=\"475.32327\" y=\"38\" id=\"reload_text\" dx=\"0\" dy=\"0\" class=\"reload_ind\">РЕСТАРТ НА ЦИКЪЛА</text>\n    </svg>\n</div>\n\n<style> \n    .reload_anim {\n        fill: #21618C;\n        animation-name: strobe;\n        animation-duration: 1s;\n        animation-iteration-count: infinite;\n        animation-direction: alternate;\n    }\n\n    @keyframes strobe {\n        from {fill: #21618C;}\n        to {fill: #2E86C1;}\n    }\n</style>\n\n<script>\n\n    function getIndexById(list, id){\n        for(var i = 0; i < list.length; i++){\n            if(list[i].id.includes(id)){\n                return i\n            }\n        }\n        return -1;\n    }\n\n    var baraban = $('#baraban'),\n        ciclon = $('#ciclon'),\n        temp_in = $('#temp_in'),\n        temp_out = $('#temp_out'),\n        leds = $(\"[id^=led_]\"),\n        reload_ind = $(\".reload_ind\"),\n        selection = $(\"svg>[filter]\");\n        \n    var scp = this.scope;\n    var state = {};\n    var reload_needed = false;\n    \n    var keys = [\"baraban\", \"ciclon\", \"shnek_in\", \"shnek_out\", \"shnek_main\"];\n    // var indexes = [];\n    \n    (function(scope) {\n        var reset;\n        scope.$watch('msg', function(msg) {\n            if (msg) {\n                if(\"temp_in\" in msg){\n                    temp_in.text(msg.temp_in);\n                    // console.log(`In: ${msg.temp_in}`);\n                }\n                if(\"temp_out\" in msg){\n                    temp_out.text(msg.temp_out);\n                    // console.log(`Out: ${msg.temp_out}`);\n                }\n                if(\"reset\" in msg){\n                    if(msg.reset){\n                        if(reload_needed){\n                            scope.send({\n                                state: state,\n                                topic: \"state\"\n                            });\n                        }\n                    }\n                }\n                if(\"state\" in msg){\n                    var ind = 0;\n                    if(msg.topic === \"init_state\"){\n                        state = JSON.parse(JSON.stringify(msg.state));\n                    }\n                    for (const [key, value] of Object.entries(msg.state)) {\n                        ind = getIndexById(leds, key);\n                        if(ind >= 0){\n                            if(value){\n                                leds[ind].style.fill = 'url(\"#led-green\")';\n                            }\n                            else{\n                                leds[ind].style.fill = 'url(\"#led-red\")';\n                            }\n                        }\n                    }\n                    reload_needed = false;\n                    reload_ind.hide();\n                    selection.attr(\"filter\", \"\");\n                }\n            }\n        });\n        scope.$watch('flow', function(flow) {\n            if (flow) {\n                console.log(flow);\n                let index = 0;\n                if(reset = flow.get(\"baraban\")){\n                    index = getIndexById(leds, keys[0]);\n                    if(reset){\n                        leds[index].style.fill = 'url(\"#led-green\")';\n                    }\n                    else{\n                        led1[index].style.fill = 'url(\"#led-red\")';\n                    }\n                    // temp_in.text(msg.temp_in);\n                    // console.log(`In: ${msg.temp_in}`);\n                }\n            }\n        });\n    })(scope);\n    \n    baraban.on('click', function(){\n        state.baraban ^= 1;\n        if(baraban.attr(\"filter\") === \"\"){\n            baraban.attr(\"filter\", \"url(#innershadow)\");\n        }\n        else{\n            baraban.attr(\"filter\", \"\");\n        }\n        if(!reload_needed){\n            reload_needed = !reload_needed;\n            reload_ind.show();\n        }\n        console.log(`State baraban ${state.baraban}`);\n    });\n    ciclon.on('click', function(){\n        state.ciclon ^= 1;\n        if(ciclon.attr(\"filter\") === \"\"){\n            ciclon.attr(\"filter\", \"url(#innershadow)\");\n        }\n        else{\n            ciclon.attr(\"filter\", \"\");\n        }\n        if(!reload_needed){\n            reload_needed = !reload_needed;\n            reload_ind.show();\n        }\n        console.log(`State ciclon ${state.ciclon}`);\n    });\n    \n    $(document).ready(function(){\n        \n    });\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":420,"y":3820,"wires":[["aec5e1b5.2d54e"]]},{"id":"aec5e1b5.2d54e","type":"function","z":"15a060b.ba89d9f","name":"set flow vars","func":"if(msg.topic === \"state\"){\n    for(const item of Object.keys(msg.state)){\n        flow.set(item, msg.state[item]);\n        // msg.state[item] = flow.get(item);\n    }\n    // if(\"baraban\" in msg.state){\n    //     flow.set('baraban', msg.state.baraban);\n    //     // msg.baraban = flow.get('baraban');\n    // }\n    // if(\"ciclone\" in msg){\n    //     flow.set('ciclone', msg.state.ciclone);\n    //     // msg.ciclone = flow.set('ciclone')\n    // }\n    return msg;\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":590,"y":3820,"wires":[["74cdaa01.710ae4"]]},{"id":"1a23aacd.3329c5","type":"inject","z":"15a060b.ba89d9f","name":"reset","props":[{"p":"reset","v":"true","vt":"bool"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":290,"y":3900,"wires":[["74cdaa01.710ae4"]]},{"id":"cface1d8.134c6","type":"function","z":"15a060b.ba89d9f","name":"init","func":"msg.state = {\n    baraban: flow.get(\"baraban\"),\n    ciclon: flow.get(\"ciclon\")\n};\nmsg.topic = \"init_state\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":270,"y":3820,"wires":[["74cdaa01.710ae4"]]},{"id":"2273c976.2a4686","type":"inject","z":"15a060b.ba89d9f","name":"init","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":150,"y":3820,"wires":[["cface1d8.134c6"]]},{"id":"8c42d853.3e44f8","type":"ui_group","z":"","name":"Статус","tab":"8fcdacb0.5daf5","order":1,"disp":false,"width":"18","collapse":false},{"id":"8fcdacb0.5daf5","type":"ui_tab","z":"","name":"Сушилня","icon":"whatshot","order":2,"disabled":false,"hidden":false}]
bartbutenaers commented 3 years ago

I feel how my issue report is slowly phasing into a feature request.

Well ideas - that are generally usable for more users - are always welcome! But due to my lack of free time, make sure to describe shortly why/what/how a new feature would look like.

I will use my DXF files from my layout plans to turn them into SVG in scale

Ah ok, I assume the &quot; will get be inserted somewhere in that process ...

What I want is when particular graphic is clicked a specific SVG filter to be applied so that it mimics the behaviour of a toggle button.

I assume you mean this part of your template node code:

    ciclon.on('click', function(){
        state.ciclon ^= 1;
        if(ciclon.attr("filter") === ""){
            ciclon.attr("filter", "url(#innershadow)");
        }
        else{
            ciclon.attr("filter", "");
        }
    });

Indeed the SVG node doesn't work currently with client side event handling:

  1. You can specify an event for an svg shape.
  2. As soon as the specified shape is being clicked, the info will be send to the server where an output message will be send.
  3. That output message can be used as input message again, e.g. to activate the filter (via e.g. a function node).
  4. The information from that input message will be send to the client again.
  5. The filter will be applied to the specified shape on the client.

But of course then you will get an entire roundtrip (client -> server -> client), which will introduce delays which might not be acceptable in your case.

Currently it is not possible to specify javascript code in the config screen, which needs to be executed as event handler on the client side. And it is currently also not possible to trigger an addEventListener via an input message, where you can specify the javascript inside the input message.

It is not clear to me if that is your feature request?? Or something else??

byunchov commented 3 years ago

I was going to suggest you to add the ability to insert JS code in the config screen for the client side. My second suggestion is targeted more towards this "toggle button" effect wich I think if you are trying to implement without some JS blueprint makes it hard to work with (not that it's impossible to do so). I don't know what could be handy for all, but in my use case the ability to have some kind of interaction via JS would be appreciated. Not quite sure it wolud be of much help for you, though. Just sharing my opinion. Unfotunately, it's hard for me to describe what's inside my head.

bartbutenaers commented 3 years ago

Yes I can understand that this might be interesting, but not sure how to proceed with it.

My time is up for today ...

bartbutenaers commented 3 years ago

Hi Bozhidar,

Of course you can simply add you event handler inside your SVG source:

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <circle cx="30" cy="30" r="20" fill="blue" cursor="pointer" onclick="alert('Click event handled on the client ...')" />
</svg>

However then you need to add it again, every time you have edited your SVG (using an external SVG editor).

I have created last night a "client-side-event-handle" branch, that allows setting a client-side event handler via input messages (similar to the server-side event handlers). You can install that branch like this (from within your .node-red folder) if you want:

npm install bartbutenaers/node-red-contrib-ui-svg#client-side-event-handler

However I have no idea yet how to integrate such functionality into the "Events" tabsheet. When I create a custom typedinput (for Javascript), it looks very confusing (since the column headers don't match the content of the columns anymore):

image

Moreover the Javascript field is way too small, and there is no syntax highlighting...

bartbutenaers commented 3 years ago

@byunchov , I'm not sure whether you still use this SVG node, or the template node. I found a way to implement the Javascript event handlers. The beta version will now be discussed on the Node-RED forum. If you want to test it, please let me know whether it works correctly.

byunchov commented 3 years ago

Thank you for updating me, @bartbutenaers ! I'm sorry for the delay but I got side tracked with several different projects and tottally forgot to write you back. I'll give the beta version a try ASAP. Have a nice day!

bartbutenaers commented 3 years ago

Version 2.1.0 is now published on NPM.