Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Nov 16, 2020. It is now read-only.

phyunsj/node-red-dashboard-custom-widget

Repository files navigation

node-red-custom-widget 1

Follow Creating your own UI widget for a Node-RED dashboard using an external charting library guide, create ui-widget with external source Table With Embedded Line Chart - D3 Example

ui-template code snippet

<!-- external libraries -->
<script src="http://d3js.org/d3.v3.min.js"></script> 
<link rel="stylesheet" href="http://billmill.org/css/style.css" /> 
<style>
 ...
</style>
<script>
;(function(scope) {
 scope.msgReady = ... // ng-if 
 
 var timer = setInterval(function() { //check that D3 libs are loaded, if not wait
 if (!window.d3) return;
 clearInterval(timer);
 
 scope.$watch('msg', function (msg) {
 if (msg) {
 
 d3.select("#datatable").selectAll("*").remove(); // Clear D3 nodes
 var rows = msg.payload;
 ...
 d3.select // D3 libs
 ...
 
 } // if (msg) 
 }); // scope.$watch()
 
 }, 3000); // setInterval:3 secs for spinner demo
})(scope);
</script>

In Action

Flow

[{"id":"18d60280.ae51ce","type":"ui_template","z":"4ca6d44e.16a0ec","group":"1444eda7.0423a2","name":"D3 Chart Widget","order":0,"width":"16","height":"16","format":"<!-- external links -->\n<script src=\"http://d3js.org/d3.v3.min.js\"></script> \n<link rel=\"stylesheet\" href=\"http://billmill.org/css/style.css\" /> \n<style>\n<!-- D3 table style --> \ntable {\n border-collapse: collapse;\n}\nth {\n border-bottom: 2px solid #ddd;\n padding: 8px;\n font-weight: bold;\n}\ntd {\n padding: 8px;\n border-top: 1px solid #ddd;\n}\n#chart {\n padding: 0px;\n}\n.xaxislabel {\n font-size: 9px;\n}\n\n<!--spinner style --> \n\n/* Center the loader */\n#loader {\n position: absolute;\n left: 50%;\n top: 50%;\n z-index: 1;\n width: 150px;\n height: 150px;\n margin: -75px 0 0 -75px;\n border: 16px solid #f3f3f3;\n border-radius: 50%;\n border-top: 16px solid #3498db;\n width: 120px;\n height: 120px;\n -webkit-animation: spin 2s linear infinite;\n animation: spin 2s linear infinite;\n}\n\n@-webkit-keyframes spin {\n 0% { -webkit-transform: rotate(0deg); }\n 100% { -webkit-transform: rotate(360deg); }\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n/* Add animation to \"page content\" */\n.animate-bottom {\n position: relative;\n -webkit-animation-name: animatebottom;\n -webkit-animation-duration: 1s;\n animation-name: animatebottom;\n animation-duration: 1s\n}\n\n@-webkit-keyframes animatebottom {\n from { bottom:-100px; opacity:0 } \n to { bottom:0px; opacity:1 }\n}.spinner {\n margin: 100px auto;\n width: 50px;\n height: 40px;\n text-align: center;\n font-size: 10px;\n}\n\n.spinner > div {\n background-color: #333;\n height: 100%;\n width: 6px;\n display: inline-block;\n \n -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;\n animation: sk-stretchdelay 1.2s infinite ease-in-out;\n}\n\n.spinner .rect2 {\n -webkit-animation-delay: -1.1s;\n animation-delay: -1.1s;\n}\n\n.spinner .rect3 {\n -webkit-animation-delay: -1.0s;\n animation-delay: -1.0s;\n}\n\n.spinner .rect4 {\n -webkit-animation-delay: -0.9s;\n animation-delay: -0.9s;\n}\n\n.spinner .rect5 {\n -webkit-animation-delay: -0.8s;\n animation-delay: -0.8s;\n}\n\n@-webkit-keyframes sk-stretchdelay {\n 0%, 40%, 100% { -webkit-transform: scaleY(0.4) } \n 20% { -webkit-transform: scaleY(1.0) }\n}\n\n@keyframes sk-stretchdelay {\n 0%, 40%, 100% { \n transform: scaleY(0.4);\n -webkit-transform: scaleY(0.4);\n } 20% { \n transform: scaleY(1.0);\n -webkit-transform: scaleY(1.0);\n }\n}\n\n@keyframes animatebottom { \n from{ bottom:-100px; opacity:0 } \n to{ bottom:0; opacity:1 }\n}\n</style>\n<h3 align=\"center\">Table With Embedded Line Chart </h3>\n<div id=\"datatable\" ng-if=\"msgReady\" ng-style=\"msgStyle\" ></div>\n<div class=\"spinner\" ng-if=\"!msgReady\">\n <div class=\"rect1\"></div>\n <div class=\"rect2\"></div>\n <div class=\"rect3\"></div>\n <div class=\"rect4\"></div>\n <div class=\"rect5\"></div>\n</div>\n\n<script>\n\n;(function(scope) {\n \n scope.msgReady = false;\n scope.msgStyle = { 'height':'700px' };\n \n \n var timer = setInterval(function() { //check that D3 libs are loaded, if not wait\n if (!window.d3) return;\n clearInterval(timer);\n \n scope.msgReady = true; \n scope.$watch('msg', function (msg) { //watch for an incoming NR msg\n \n if (msg) {\n \n d3.select(\"#datatable\").selectAll(\"*\").remove();\n \n var rows = msg.payload;\n \n var table = d3.select(\"#datatable\").append(\"table\");\n thead = table.append(\"thead\");\n tbody = table.append(\"tbody\");\n\n thead.append(\"th\").text(\"Date\");\n thead.append(\"th\").text(\"Opponent\");\n thead.append(\"th\").text(\"Result\");\n thead.append(\"th\").text(\"Rating\");\n thead.append(\"th\").text(\"\");\n\n var tr = tbody.selectAll(\"tr\")\n .data(rows)\n .enter().append(\"tr\");\n\n var td = tr.selectAll(\"td\")\n .data(function(d) { return [d.dt, d.opp, d.result, d.mu]; })\n .enter().append(\"td\")\n .text(function(d) { return d; });\n\n var width = 80,mx = 10, radius = 2,\n height = d3.select(\"table\")[0][0].clientHeight;\n\n // Now add the chart column\n d3.select(\"#datatable tbody tr\").append(\"td\")\n .attr(\"id\", \"chart\")\n .attr(\"width\", width + \"px\")\n .attr(\"rowspan\", rows.length);\n\n var chart = d3.select(\"#chart\").append(\"svg\")\n .attr(\"class\", \"chart\")\n .attr(\"width\", width)\n .attr(\"height\", height);\n\n var maxMu = 0;\n var minMu = Number.MAX_VALUE;\n for (i=0; i < rows.length; i++) {\n if (rows[i].mu > maxMu) { maxMu = rows[i].mu; }\n if (rows[i].mu < minMu) { minMu = rows[i].mu; }\n }\n\n var dates = rows.map(function(t) { return t.dt; });\n\n var xscale = d3.scale.linear()\n .domain([minMu, maxMu])\n .range([mx, width-mx])\n .nice();\n\n var yscale = d3.scale.ordinal()\n .domain(dates)\n .rangeBands([0, height]);\n\n chart.selectAll(\".xaxislabel\")\n .data(xscale.ticks(2))\n .enter().append(\"text\")\n .attr(\"class\", \"xaxislabel\")\n .attr(\"x\", function(d) { return xscale(d); })\n .attr(\"y\", 10)\n .attr(\"text-anchor\", \"middle\")\n .text(String)\n\n chart.selectAll(\".xaxistick\")\n .data(xscale.ticks(2))\n .enter().append(\"line\")\n .attr(\"x1\", function(d) { return xscale(d); })\n .attr(\"x2\", function(d) { return xscale(d); })\n .attr(\"y1\", 10)\n .attr(\"y2\", height)\n .attr(\"stroke\", \"#eee\")\n .attr(\"stroke-width\", 1);\n\n chart.selectAll(\".line\")\n .data(rows)\n .enter().append(\"line\")\n .attr(\"x1\", function(d) { return xscale(d.mu); })\n .attr(\"y1\", function(d) { return yscale(d.dt) + yscale.rangeBand()/2; })\n .attr(\"x2\", function(d,i) { return rows[i+1] ? xscale(rows[i+1].mu) : xscale(d.mu); })\n .attr(\"y2\", function(d,i) { return rows[i+1] ? yscale(rows[i+1].dt) + yscale.rangeBand()/2 : yscale(d.dt) + yscale.rangeBand()/2; })\n .attr(\"stroke\", \"#777\")\n .attr(\"stroke-width\", 1);\n\n var pt = chart.selectAll(\".pt\")\n .data(rows)\n .enter().append(\"g\")\n .attr(\"class\", \"pt\")\n .attr(\"transform\", function(d) { return \"translate(\" + xscale(d.mu) + \",\" + (yscale(d.dt) + yscale.rangeBand()/2) + \")\"; });\n\n pt.append(\"circle\")\n .attr(\"cx\", 0)\n .attr(\"cy\", 0)\n .attr(\"r\", radius)\n .attr(\"opacity\", .5)\n .attr(\"fill\", \"#ff0000\");\n\n } // if\n }); // watch\n\n\n }, 3000); // close out the setInterval \n // 3 secs for spinner demo\n\n})(scope);\n\n</script>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":602.3003234863281,"y":356.8004741668701,"wires":[[]]},{"id":"8f387961.25fc58","type":"inject","z":"4ca6d44e.16a0ec","name":"","topic":"","payload":"","payloadType":"date","repeat":"3","crontab":"","once":true,"onceDelay":0.1,"x":152.3000030517578,"y":359.066725730896,"wires":[["d3ce3d90.6b48f"]]},{"id":"d3ce3d90.6b48f","type":"function","z":"4ca6d44e.16a0ec","name":"random csv","func":"\n\nvar msg = {};\nmsg.topic = \"Line Chart\";\n\nvar count = Math.floor(Math.random() * 2);\n\nswitch( count ) {\n\ncase 0 :\nmsg.payload ='mu,sigma,opp,result,date\\n'+\n'1662.30921255,290.31746003,FC Augsburg,1,2013年08月10日T00:00:00\\n'+\n'1710.31516965,260.486532706,TSV Eintracht Braunschweig,1,2013年08月18日T00:00:00\\n'+\n'1833.29188401,225.673906004,Werder Bremen,1,2013年08月23日T00:00:00\\n'+\n'1857.5300504,214.245325191,Eintracht Frankfurt,1,2013年09月01日T00:00:00\\n'+\n'1875.73400724,205.674370682,Hamburg SV,1,2013年09月14日T00:00:00\\n'+\n'1784.18071145,185.716271567,Napoli,0,2013年09月18日T00:00:00\\n'+\n'1718.35291449,179.834040342,Nurnberg,0.5,2013年09月21日T00:00:00\\n'+\n'1740.98858679,171.270511287,SC Freiburg,1,2013年09月28日T00:00:00\\n'+\n'1786.58260116,157.720707589,Marseille,1,2013年10月01日T00:00:00\\n'+\n'1693.24904256,150.534136974,Borussia Monchengladbach,0,2013年10月05日T00:00:00\\n'+\n'1730.8689396,141.113609063,Hannover 96,1,2013年10月19日T00:00:00\\n'+\n'1796.86111973,134.157129861,Arsenal,1,2013年10月22日T00:00:00\\n'+\n'1815.9808198,129.14679898,Schalke 04,1,2013年10月26日T00:00:00\\n'+\n'1827.35641274,126.152133747,VfB Stuttgart,1,2013年11月01日T00:00:00\\n'+\n'1792.11402645,120.045882423,Arsenal,0,2013年11月06日T00:00:00';\nbreak;\ncase 1 :\nmsg.payload ='mu,sigma,opp,result,date\\n'+\n'1762.30921255,290.31746003,FC Augsburg,1,2013年08月10日T00:00:00\\n'+\n'1719.31516965,260.486532706,TSV Eintracht Braunschweig,1,2013年08月18日T00:00:00\\n'+\n'1633.29188401,225.673906004,Werder Bremen,1,2013年08月23日T00:00:00\\n'+\n'1897.5300504,214.245325191,Eintracht Frankfurt,1,2013年09月01日T00:00:00\\n'+\n'1775.73400724,205.674370682,Hamburg SV,1,2013年09月14日T00:00:00\\n'+\n'1884.18071145,185.716271567,Napoli,0,2013年09月18日T00:00:00\\n'+\n'1818.35291449,179.834040342,Nurnberg,0.5,2013年09月21日T00:00:00\\n'+\n'1840.98858679,171.270511287,SC Freiburg,1,2013年09月28日T00:00:00\\n'+\n'1686.58260116,157.720707589,Marseille,1,2013年10月01日T00:00:00\\n'+\n'1793.24904256,150.534136974,Borussia Monchengladbach,0,2013年10月05日T00:00:00\\n'+\n'1830.8689396,141.113609063,Hannover 96,1,2013年10月19日T00:00:00\\n'+\n'1896.86111973,134.157129861,Arsenal,1,2013年10月22日T00:00:00\\n'+\n'1615.9808198,129.14679898,Schalke 04,1,2013年10月26日T00:00:00\\n'+\n'1727.35641274,126.152133747,VfB Stuttgart,1,2013年11月01日T00:00:00\\n'+\n'1892.11402645,120.045882423,Arsenal,0,2013年11月06日T00:00:00';\nbreak;\ndefault :\nmsg.payload ='mu,sigma,opp,result,date\\n'+\n'1682.30921255,290.31746003,FC Augsburg,1,2013年08月10日T00:00:00\\n'+\n'1810.31516965,260.486532706,TSV Eintracht Braunschweig,1,2013年08月18日T00:00:00\\n'+\n'1733.29188401,225.673906004,Werder Bremen,1,2013年08月23日T00:00:00\\n'+\n'1657.5300504,214.245325191,Eintracht Frankfurt,1,2013年09月01日T00:00:00\\n'+\n'1775.73400724,205.674370682,Hamburg SV,1,2013年09月14日T00:00:00\\n'+\n'1884.18071145,185.716271567,Napoli,0,2013年09月18日T00:00:00\\n'+\n'1818.35291449,179.834040342,Nurnberg,0.5,2013年09月21日T00:00:00\\n'+\n'1640.98858679,171.270511287,SC Freiburg,1,2013年09月28日T00:00:00\\n'+\n'1786.58260116,157.720707589,Marseille,1,2013年10月01日T00:00:00\\n'+\n'1893.24904256,150.534136974,Borussia Monchengladbach,0,2013年10月05日T00:00:00\\n'+\n'1730.8689396,141.113609063,Hannover 96,1,2013年10月19日T00:00:00\\n'+\n'1796.86111973,134.157129861,Arsenal,1,2013年10月22日T00:00:00\\n'+\n'1815.9808198,129.14679898,Schalke 04,1,2013年10月26日T00:00:00\\n'+\n'1847.35641274,126.152133747,VfB Stuttgart,1,2013年11月01日T00:00:00\\n'+\n'1792.11402645,120.045882423,Arsenal,0,2013年11月06日T00:00:00';\n\nbreak;\n}\nreturn msg;","outputs":1,"noerr":0,"x":337.3001174926758,"y":342.866943359375,"wires":[["e66191ed.19ddc"]]},{"id":"e66191ed.19ddc","type":"function","z":"4ca6d44e.16a0ec","name":"csv2json","func":"\nvar csv = [];\nvar lines=msg.payload.split(\"\\n\");\nvar headers=lines[0].split(\",\");\nfor(var i=1;i<lines.length;i++){\n var obj = {};\n var currentline=lines[i].split(\",\");\n\tfor(var j=0;j<headers.length;j++) obj[headers[j]] = currentline[j];\n\tcsv.push(obj);\n}\n\nvar result = [];\nfor(var i=1; i<csv.length;i++){\n csv[i].mu = parseFloat(csv[i].mu).toFixed(1);\n csv[i].sigma = parseFloat(csv[i].sigma).toFixed(1);\n csv[i].dt = new Date(Date.parse(csv[i].date));\n\n var res = parseFloat(csv[i].result);\n if (res < .5) {\n csv[i].result = \"loss\";\n } else if (res > .5) {\n csv[i].result = \"win\";\n } else {\n csv[i].result = \"draw\";\n }\n result.push(csv[i]);\n}\nmsg.payload = result;\n\nreturn msg;","outputs":1,"noerr":0,"x":429.3000793457031,"y":400.2669153213501,"wires":[["18d60280.ae51ce"]]},{"id":"1444eda7.0423a2","type":"ui_group","z":"","name":"","tab":"6bb5e46c.104aec","order":1,"disp":true,"width":"16","collapse":false},{"id":"6bb5e46c.104aec","type":"ui_tab","z":"","name":"UI Widget","icon":"dashboard","order":3}]

node-red-custom-widget 2 : Modal PIN Dialog

The original work : Security Pin Dialog UI Template for Node-Red-Dashboard by Daniel Lando

Flow Changes :

  • Lock Door -> Display a PIN dialog
  • Display a PIN dialog if pin_error
  • Display a PIN dialog if the session is expired

In Action

Flow

[{"id":"c6907ba1.f042a8","type":"ui_button","z":"24c9024.86a84fe","name":"","group":"5c14d060.b73e7","order":0,"width":0,"height":0,"passthru":false,"label":"Lock Door","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"show","x":270.0000915527344,"y":280.2501029968262,"wires":[["fbd1ef60.16855"]]},{"id":"d26fc943.b99f08","type":"function","z":"24c9024.86a84fe","name":"verify_pin","func":"var pins = [\"2136\"];\nvar verified = false;\n\nfor(var i=0;i<pins.length;i++){\n if(msg.passcode == pins[i]){\n verified = true;\n break;\n }\n}\n\nmsg.verified = verified;\n\nreturn msg;","outputs":1,"noerr":0,"x":513.6250953674316,"y":279.25004482269287,"wires":[["f3e13fa.ea9cac"]]},{"id":"f3e13fa.ea9cac","type":"switch","z":"24c9024.86a84fe","name":"check","property":"verified","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"false","repair":false,"outputs":2,"x":626.1251335144043,"y":389.25010681152344,"wires":[["8080576b.049568"],["c7ddbcb1.48df5"]]},{"id":"5a70443c.a2120c","type":"ui_toast","z":"24c9024.86a84fe","position":"top right","displayTime":"3","outputs":0,"ok":"OK","cancel":"","topic":"","name":"","x":988.1250839233398,"y":444.25010681152344,"wires":[]},{"id":"8080576b.049568","type":"function","z":"24c9024.86a84fe","name":"pin_ok","func":"var msg2 = {};\nmsg2.topic = \"Pin successfully verified!\";\nmsg2.payload = \"\";\n \nvar start = Date.now();\n\nflow.set(\"session\", start);\n\nreturn [msg, msg2];","outputs":"2","noerr":0,"x":733.1250762939453,"y":270.25002670288086,"wires":[["356c9605.1471fa"],["5a70443c.a2120c"]]},{"id":"c7ddbcb1.48df5","type":"function","z":"24c9024.86a84fe","name":"pin_error","func":" msg.topic = \"Wrong PIN\";\n msg.payload = \"\";\n \nreturn [msg, { topic : 'show', payload : true } ];","outputs":2,"noerr":0,"x":737.1251373291016,"y":544.2502136230469,"wires":[["5a70443c.a2120c"],["fbd1ef60.16855"]]},{"id":"fbd1ef60.16855","type":"ui_template","z":"24c9024.86a84fe","group":"5266d204.35555c","name":"Pin_Unlock","order":0,"width":"0","height":"0","format":"<div ng-init=\"init()\" id=\"pin_insert\" class=\"dialog\">\n \n <div class=\"dialog_content\">\n \n <div class=\"dialog_header\">\n <!-- <span ng-click=\"closeDialog()\" class=\"close\">&times;</span> -->\n <h2>PIN</h2>\n </div>\n \n <div class=\"dialog_body\">\n\n <div layout=\"row\" layout-align=\"center\">\n <div class=\"number_placeholder\">\n {{passcode.substring(0, 1)}}\n </div>\n <div class=\"number_placeholder\">\n {{passcode.substring(1, 2)}}\n </div>\n <div class=\"number_placeholder\">\n {{passcode.substring(2, 3)}}\n </div>\n <div class=\"number_placeholder\">\n {{passcode.substring(3, 4)}}\n </div>\n </div>\n \n <div layout=\"column\" layout-align=\"center\" style=\"margin-top: 50px\">\n <div layout=\"row\" layout-align=\"center\">\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(1)\">1</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(2)\">2</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(3)\">3</md-button>\n </div>\n </div>\n <div layout=\"row\" layout-align=\"center\">\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(4)\">4</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(5)\">5</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(6)\">6</md-button>\n </div>\n </div>\n <div layout=\"row\" layout-align=\"center\">\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(7)\">7</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(8)\">8</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(9)\">9</md-button>\n </div>\n </div>\n <div layout=\"row\" layout-align=\"center\">\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"confirm()\">\n <ng-md-icon icon=\"done\" style=\"color:#fff;\"></ng-md-icon>\n </md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"add(0)\">0</md-button>\n </div>\n <div class=\"number_box\">\n <md-button class=\"md-raised\" ng-click=\"delete()\">\n <ng-md-icon icon=\"arrow_back\" style=\"color:#fff;\"></ng-md-icon>\n </md-button>\n </div>\n </div>\n </div> \n \n </div> <!--dialog_body-->\n </div> <!--dialog_content-->\n</div> <!--dialog-->\n\n\n<style>\n\n/* The Dialog (background) */\n.dialog {\n display: none; /* Hidden by default */\n position: fixed; /* Stay in place */\n z-index: 9999; /* Sit on top */\n left: 0;\n top: 0;\n width: 100%; /* Full width */\n height: 100%; /* Full height */\n overflow: auto; /* Enable scroll if needed */\n background-color: rgb(0,0,0); /* Fallback color */\n background-color: rgba(0,0,0,0.4); /* Black w/ opacity */\n -webkit-transform: translateZ(0px);\n -webkit-transform: translate3d(0,0,0);\n -webkit-perspective: 1000;\n}\n\n.dialog_content {\n position: absolute;\n background-color: #fff;\n margin-top: 10%;\n margin-left: 40%;\n padding: 0;\n width: 345px;\n box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);\n -webkit-animation-name: animatetop;\n animation-name: animatetop;\n animation-duration: 0.4s;\n}\n\n/* Media query for smartphones (to Fix?) */\n@media only screen and (min-device-width : 375px) and (max-device-width : 667px) { \n .dialog_content {\n margin-top: 5%;\n margin-left: 5%;\n}\n}\n\n/* Add Animation */\n@-webkit-keyframes animatetop {\n from {top: -300px; opacity: 0} \n to {top: 0; opacity: 1}\n}\n\n@keyframes animatetop {\n from {top: -300px; opacity: 0}\n to {top: 0; opacity: 1}\n}\n\n/* Dialog Header */\n.dialog_header {\n padding: 2px 16px;\n background-color: #03A9F4;\n color: white;\n}\n\n/* Dialog Body */\n.dialog_body {padding: 16px 16px;}\n\n/* The Close Button */\n.close {\n color: #fff;\n float: right;\n font-size: 28px;\n font-weight: bold;\n}\n\n.close:hover,\n.close:focus {\n color: #1565C0;\n text-decoration: none;\n cursor: pointer;\n}\n\n/* __ */\n.number_placeholder{\n width: 50px;\n height: 34px;\n margin: 10px;\n font-size: 20pt;\n text-align: center;\n border-bottom: 1px solid black;\n}\n\n/* Number container */\n.number_box{\n margin: 10px;\n}\n\n/* Buttons style */\n.md-button.md-default-theme.md-raised, .md-button.md-raised{\n min-height: 50px;\n min-width: 50px;\n font-weight: bold;\n margin: 0px 10px 10px 0px;\n box-shadow: 4px 4px 6px 0 #dadada;\n background-color: #29B6F6;\n color: #fff;\n}\n\n.md-button.md-default-theme.md-raised:not([disabled]):hover, .md-button.md-raised:not([disabled]):hover{\n background-color: #03A9F4;\n}\n\n.btn1 {\n color : rgb(49, 46, 46);\n background-color: rgba(255, 222, 121, 0.96);\n border-radius: 10px 0 0 10px;\n font-size: 16px;\n}\n\n.btn1:not([disabled]):hover {\n background-color: rgba(107, 103, 91, 0.96);\n color: white;\n}\n\n.btn1[disabled] {\n color : rgb(187, 187, 187);\n background-color: rgba(230, 230, 229, 0.96);\n}\n\n</style>\n\n<script>\n\n/**\n * pin_dialog.js\n * Node-Red UI template for Node-Red Dashboard. \n * Custom dialog that asks for a PIN to allow actions\n * Enjoy it :). \n * -- Daniel\n *\n *\n * @license The Unlicense, http://unlicense.org/\n * @version 0.1\n * @author Daniel Lando, https://github.com/robertsLando\n * @updated 2017年03月08日\n * @link ----\n *\n *\n */\n\nvar dialog;\n\n/* ==== */\n(function(scope) {\n \n scope.passcode = \"\";\n scope.payload = \"\";\n scope.inited = false;\n \n scope.init = function() {\n scope.passcode = \"\";\n //Hide the md-panel\n $('#pin_insert').parent().parent().css(\"display\", \"none\");\n //This trick make it works on smartphones too :)\n dialog = $('#pin_insert').detach();\n dialog.appendTo(document.body);\n \n scope.showDialog();\n }\n \n scope.showDialog = function() {\n dialog.css(\"display\", \"block\");\n }\n \n scope.closeDialog = function(){\n dialog.css(\"display\", \"none\");\n }\n \n scope.add = function(value) {\n if(scope.passcode.length < 4) {\n scope.passcode = scope.passcode + value;\n if(scope.passcode.length == 4) {\n console.log(\"The four digit code was entered\");\n \n }\n }\n }\n \n scope.delete = function() {\n if(scope.passcode.length > 0) {\n scope.passcode = scope.passcode.substring(0, scope.passcode.length - 1);\n }\n }\n \n scope.confirm = function() {\n if(scope.passcode.length == 4) {\n scope.send({passcode: scope.passcode, payload : scope.payload});\n scope.closeDialog();\n scope.passcode = \"\";\n scope.payload = \"\";\n }\n }\n\n scope.$watch('msg', function(data) {\n if(data && data.topic){\n switch(data.topic){\n case \"show\":\n if(scope.inited){\n scope.payload = data.payload;\n scope.showDialog();\n }\n else\n scope.inited = true;\n break;\n case \"close\": \n scope.closeDialog(); \n break;\n }\n }\n });\n})(scope);\n\n</script>\n","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":406.37511444091797,"y":384.2143335342407,"wires":[["d26fc943.b99f08"]]},{"id":"356c9605.1471fa","type":"debug","z":"24c9024.86a84fe","name":"do_Something","active":true,"tosidebar":true,"console":false,"complete":"payload","x":972.8754043579102,"y":343.0001449584961,"wires":[]},{"id":"ab9dba5f.33f0e8","type":"comment","z":"24c9024.86a84fe","name":"ping_error triggers showdialog()","info":"","x":810.1666717529297,"y":638.0001573562622,"wires":[]},{"id":"b482cf1c.5d0e4","type":"comment","z":"24c9024.86a84fe","name":"Always display PIN dialog","info":"","x":403.1667175292969,"y":340.0000400543213,"wires":[]},{"id":"6576ab0c.489c34","type":"inject","z":"24c9024.86a84fe","name":"","topic":"","payload":"","payloadType":"date","repeat":"3","crontab":"","once":true,"onceDelay":0.1,"x":292.1666793823242,"y":683.2000637054443,"wires":[["939a219f.625c2"]]},{"id":"939a219f.625c2","type":"function","z":"24c9024.86a84fe","name":"Session Timeout > 10 secs","func":"\nvar start = flow.get(\"session\") || 0;\nvar timeOut = 10000; // 10 secs\n\nif ( start == 0 ) {\n msg = [ null, { payload : 'in-active'}];\n} else {\n var millis = Date.now() - start;\n if ( millis > timeOut) { // 10 secs for \n flow.set(\"session\", 0) ;\n msg = [ { topic : 'show', payload : true}, { payload : 'terminated'}];\n } else {\n msg = [ null, { payload : 'active'}];\n \n }\n}\nreturn msg;","outputs":2,"noerr":0,"x":511.1666717529297,"y":683.3333358764648,"wires":[["fbd1ef60.16855"],["7c989aed.556ba4"]]},{"id":"7c989aed.556ba4","type":"debug","z":"24c9024.86a84fe","name":"TimeOut !!!","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":778.1666717529297,"y":691.2669258117676,"wires":[]},{"id":"180cffa8.069a5","type":"comment","z":"24c9024.86a84fe","name":"10 secs for this demo","info":"","x":510.1666564941406,"y":638.3333530426025,"wires":[]},{"id":"c3f2941c.a42b88","type":"comment","z":"24c9024.86a84fe","name":"To-do-List : Display Secure Dashboard","info":"","x":1029.1667022705078,"y":293.3333282470703,"wires":[]},{"id":"5c14d060.b73e7","type":"ui_group","z":"","name":"Secure","tab":"4ee5fa32.77b934","disp":true,"width":"6"},{"id":"5266d204.35555c","type":"ui_group","z":"","name":"pin","tab":"4ee5fa32.77b934","disp":false,"width":"1"},{"id":"4ee5fa32.77b934","type":"ui_tab","z":"","name":"Home","icon":"home","order":1}]

FYI

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /