Guest User

NodeRED GET nodes reply body

a guest
Apr 21st, 2017
1,623
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2.  
  3.     <!--
  4.       Copyright JS Foundation and other contributors, http://js.foundation
  5.      
  6.       Licensed under the Apache License, Version 2.0 (the "License");
  7.       you may not use this file except in compliance with the License.
  8.       You may obtain a copy of the License at
  9.      
  10.       http://www.apache.org/licenses/LICENSE-2.0
  11.      
  12.       Unless required by applicable law or agreed to in writing, software
  13.       distributed under the License is distributed on an "AS IS" BASIS,
  14.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.       See the License for the specific language governing permissions and
  16.       limitations under the License.
  17.     -->
  18.      
  19.     <script type="text/x-red" data-template-name="sentiment">
  20.         <div class="form-row">
  21.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  22.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  23.         </div>
  24.     </script>
  25.      
  26.      
  27.      
  28.     <script type="text/javascript">
  29.         RED.nodes.registerType('sentiment',{
  30.             category: 'analysis-function',
  31.             color:"#E6E0F8",
  32.             defaults: {
  33.                 name: {value:""},
  34.             },
  35.             inputs:1,
  36.             outputs:1,
  37.             icon: "arrow-in.png",
  38.             label: function() {
  39.                 return this.name||"sentiment";
  40.             },
  41.             labelStyle: function() {
  42.                 return this.name?"node_label_italic":"";
  43.             }
  44.         });
  45.     </script>
  46.     <script type="text/x-red" data-help-name="sentiment">
  47.         <p>Analyses the <code>msg.payload</code> and adds a <code>msg.sentiment</code> object
  48.         that contains the resulting AFINN-111 sentiment score as <code>msg.sentiment.score</code>.</p>
  49.         <p>A score greater than zero is positive and less than zero is negative.</p>
  50.         <p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
  51.         <p>An object of word score overrides can be supplied as <code>msg.overrides</code>.</p>
  52.         <p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_blank">the Sentiment docs here</a>.</p>
  53.     </script><!--
  54.       Copyright JS Foundation and other contributors, http://js.foundation
  55.      
  56.       Licensed under the Apache License, Version 2.0 (the "License");
  57.       you may not use this file except in compliance with the License.
  58.       You may obtain a copy of the License at
  59.      
  60.       http://www.apache.org/licenses/LICENSE-2.0
  61.      
  62.       Unless required by applicable law or agreed to in writing, software
  63.       distributed under the License is distributed on an "AS IS" BASIS,
  64.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  65.       See the License for the specific language governing permissions and
  66.       limitations under the License.
  67.     -->
  68.      
  69.     <script type="text/x-red" data-template-name="inject">
  70.         <div class="form-row">
  71.             <label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
  72.             <input type="text" id="node-input-payload" style="width:70%">
  73.             <input type="hidden" id="node-input-payloadType">
  74.         </div>
  75.      
  76.         <div class="form-row">
  77.             <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  78.             <input type="text" id="node-input-topic">
  79.         </div>
  80.      
  81.         <div class="form-row">
  82.             <label for=""><i class="fa fa-repeat"></i> <span data-i18n="inject.label.repeat"></span></label>
  83.             <select id="inject-time-type-select">
  84.                 <option value="none" data-i18n="inject.none"></option>
  85.                 <option value="interval" data-i18n="inject.interval"></option>
  86.                 <option value="interval-time" data-i18n="inject.interval-time"></option>
  87.                 <option value="time" data-i18n="inject.time"></option>
  88.             </select>
  89.             <input type="hidden" id="node-input-repeat">
  90.             <input type="hidden" id="node-input-crontab">
  91.         </div>
  92.      
  93.         <div class="form-row inject-time-row hidden" id="inject-time-row-interval">
  94.             <span data-i18n="inject.every"></span>
  95.             <input id="inject-time-interval-count" class="inject-time-count" value="1"></input>
  96.             <select style="width: 100px" id="inject-time-interval-units">
  97.                 <option value="s" data-i18n="inject.seconds"></option>
  98.                 <option value="m" data-i18n="inject.minutes"></option>
  99.                 <option value="h" data-i18n="inject.hours"></option>
  100.             </select><br/>
  101.         </div>
  102.      
  103.         <div class="form-row inject-time-row hidden" id="inject-time-row-interval-time">
  104.             <span data-i18n="inject.every"></span> <select style="width:90px" id="inject-time-interval-time-units" class="inject-time-int-count" value="1">
  105.                 <option value="1">1</option>
  106.                 <option value="2">2</option>
  107.                 <option value="3">3</option>
  108.                 <option value="4">4</option>
  109.                 <option value="5">5</option>
  110.                 <option value="6">6</option>
  111.                 <option value="10">10</option>
  112.                 <option value="12">12</option>
  113.                 <option value="15">15</option>
  114.                 <option value="20">20</option>
  115.                 <option value="30">30</option>
  116.                 <option value="0">60</option>
  117.             </select> <span data-i18n="inject.minutes"></span><br/>
  118.             <span data-i18n="inject.between"></span> <select id="inject-time-interval-time-start" class="inject-time-times"></select>
  119.             <span data-i18n="inject.and"></span> <select id="inject-time-interval-time-end" class="inject-time-times"></select><br/>
  120.             <div id="inject-time-interval-time-days" class="inject-time-days">
  121.                 <div style="display: inline-block; vertical-align: top;margin-right:5px;" data-i18n="inject.on">on</div>
  122.                 <div style="display:inline-block;">
  123.                     <div>
  124.                         <label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
  125.                         <label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
  126.                         <label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
  127.                     </div>
  128.                     <div>
  129.                         <label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
  130.                         <label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
  131.                         <label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
  132.                     </div>
  133.                     <div>
  134.                         <label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
  135.                     </div>
  136.                 </div>
  137.             </div>
  138.         </div>
  139.      
  140.         <div class="form-row inject-time-row hidden" id="inject-time-row-time">
  141.             <span data-i18n="inject.at"></span> <input id="inject-time-time" value="12:00"></input><br/>
  142.             <div id="inject-time-time-days" class="inject-time-days">
  143.                 <div style="display: inline-block; vertical-align: top;margin-right: 5px;">on </div>
  144.                 <div style="display:inline-block;">
  145.                     <div>
  146.                         <label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
  147.                         <label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
  148.                         <label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
  149.                     </div>
  150.                     <div>
  151.                         <label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
  152.                         <label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
  153.                         <label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
  154.                     </div>
  155.                     <div>
  156.                         <label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
  157.                     </div>
  158.                 </div>
  159.             </div>
  160.         </div>
  161.      
  162.         <div class="form-row" id="node-once">
  163.             <label>&nbsp;</label>
  164.             <input type="checkbox" id="node-input-once" style="display: inline-block; width: auto; vertical-align: top;">
  165.             <label for="node-input-once" style="width: 70%;" data-i18n="inject.onstart"></label>
  166.         </div>
  167.      
  168.         <div class="form-row">
  169.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  170.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  171.         </div>
  172.      
  173.         <div class="form-tips" data-i18n="[html]inject.tip"></div>
  174.     </script>
  175.     <style>
  176.         .inject-time-row {
  177.             padding-left: 110px;
  178.         }
  179.         .inject-time-row select {
  180.             margin: 3px 0;
  181.         }
  182.         .inject-time-days label {
  183.             -webkit-user-select: none;
  184.             -khtml-user-select: none;
  185.             -moz-user-select: none;
  186.             -ms-user-select: none;
  187.             user-select: none;
  188.             vertical-align: top;
  189.             width: 100px;
  190.         }
  191.          .inject-time-days input {
  192.              width: auto;
  193.          }
  194.         .inject-time-times {
  195.             width: 90px;
  196.         }
  197.         #inject-time-time {
  198.             width: 75px;
  199.         }
  200.         .inject-time-count {
  201.             width: 40px !important;
  202.         }
  203.     </style>
  204.      
  205.      
  206.     <script type="text/javascript">
  207.         RED.nodes.registerType('inject',{
  208.             category: 'input',
  209.             color:"#a6bbcf",
  210.             defaults: {
  211.                 name: {value:""},
  212.                 topic: {value:""},
  213.                 payload: {value:"", validate: RED.validators.typedInput("payloadType")},
  214.                 payloadType: {value:"date"},
  215.                 repeat: {value:""},
  216.                 crontab: {value:""},
  217.                 once: {value:false}
  218.             },
  219.             inputs:0,
  220.             outputs:1,
  221.             icon: "inject.png",
  222.             label: function() {
  223.                 var suffix = "";
  224.                 // if fire once then add small indication
  225.                 if (this.once) {
  226.                     suffix = " ยน";
  227.                 }
  228.                 // but replace with repeat one if set to repeat
  229.                 if (this.repeat || this.crontab) {
  230.                     suffix = " โ†ป";
  231.                 }
  232.                 if (this.name) {
  233.                     return this.name+suffix;
  234.                 } else if (this.payloadType === "string" ||
  235.                         this.payloadType === "str" ||
  236.                         this.payloadType === "num" ||
  237.                         this.payloadType === "bool" ||
  238.                         this.payloadType === "json") {
  239.                     if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) {
  240.                         return this.topic + ":" + this.payload+suffix;
  241.                     } else if (this.payload.length > 0 && this.payload.length < 24) {
  242.                         return this.payload+suffix;
  243.                     } else {
  244.                         return this._("inject.inject")+suffix;
  245.                     }
  246.                 } else if (this.payloadType === 'date') {
  247.                     if ((this.topic !== "") && (this.topic.length <= 16)) {
  248.                         return this.topic + ":" + this._("inject.timestamp")+suffix;
  249.                     } else {
  250.                         return this._("inject.timestamp")+suffix;
  251.                     }
  252.                 } else if (this.payloadType === 'flow' && this.payload.length < 19) {
  253.                     return 'flow.'+this.payload+suffix;
  254.                 } else if (this.payloadType === 'global' && this.payload.length < 17) {
  255.                     return 'global.'+this.payload+suffix;
  256.                 } else {
  257.                     return this._("inject.inject")+suffix;
  258.                 }
  259.             },
  260.             labelStyle: function() {
  261.                 return this.name?"node_label_italic":"";
  262.             },
  263.             oneditprepare: function() {
  264.                 if (this.payloadType == null) {
  265.                     if (this.payload == "") {
  266.                         this.payloadType = "date";
  267.                     } else {
  268.                         this.payloadType = "str";
  269.                     }
  270.                 } else if (this.payloadType === 'string' || this.payloadType === 'none') {
  271.                     this.payloadType = "str";
  272.                 }
  273.                 $("#node-input-payloadType").val(this.payloadType);
  274.      
  275.                 $("#node-input-payload").typedInput({
  276.                     default: 'str',
  277.                     typeField: $("#node-input-payloadType"),
  278.                     types:['flow','global','str','num','bool','json','date']
  279.                 });
  280.      
  281.                 $("#inject-time-type-select").change(function() {
  282.                     $("#node-input-crontab").val('');
  283.                     var id = $("#inject-time-type-select").val();
  284.                     $(".inject-time-row").hide();
  285.                     $("#inject-time-row-"+id).show();
  286.                     if ((id == "none") || (id == "interval")) {
  287.                         $("#node-once").show();
  288.                     }
  289.                     else {
  290.                         $("#node-once").hide();
  291.                         $("#node-input-once").prop('checked', false);
  292.                     }
  293.                 });
  294.      
  295.                 $(".inject-time-times").each(function() {
  296.                     for (var i=0;i<24;i++) {
  297.                         var l = (i<10?"0":"")+i+":00";
  298.                         $(this).append($("<option></option>").val(i).text(l));
  299.                     }
  300.                 });
  301.                 $("<option></option>").val(24).text("00:00").appendTo("#inject-time-interval-time-end");
  302.                 $("#inject-time-interval-time-start").change(function() {
  303.                     var start = Number($("#inject-time-interval-time-start").val());
  304.                     var end = Number($("#inject-time-interval-time-end").val());
  305.                     $("#inject-time-interval-time-end option").remove();
  306.                     for (var i=start+1;i<25;i++) {
  307.                         var l = (i<10?"0":"")+i+":00";
  308.                         if (i==24) {
  309.                             l = "00:00";
  310.                         }
  311.                         var opt = $("<option></option>").val(i).text(l).appendTo("#inject-time-interval-time-end");
  312.                         if (i === end) {
  313.                             opt.attr("selected","selected");
  314.                         }
  315.                     }
  316.                 });
  317.      
  318.                 $(".inject-time-count").spinner({
  319.                     //max:60,
  320.                     min:1
  321.                 });
  322.      
  323.                 $.widget( "ui.injecttimespinner", $.ui.spinner, {
  324.                     options: {
  325.                         // seconds
  326.                         step: 60 * 1000,
  327.                         // hours
  328.                         page: 60
  329.                     },
  330.                     _parse: function( value ) {
  331.                         if ( typeof value === "string" ) {
  332.                             // already a timestamp
  333.                             if ( Number( value ) == value ) {
  334.                                 return Number( value );
  335.                             }
  336.                             var p = value.split(":");
  337.                             var offset = new Date().getTimezoneOffset();
  338.                             return ((Number(p[0])*60)+Number(p[1])+offset)*60*1000;
  339.                         }
  340.                         return value;
  341.                     },
  342.                     _format: function( value ) {
  343.                         var d = new Date(value);
  344.                         var h = d.getHours();
  345.                         var m = d.getMinutes();
  346.                         return ((h < 10)?"0":"")+h+":"+((m < 10)?"0":"")+m;
  347.                     }
  348.                 });
  349.      
  350.                 $("#inject-time-time").injecttimespinner();
  351.      
  352.                 var repeattype = "none";
  353.                 if (this.repeat != "" && this.repeat != 0) {
  354.                     repeattype = "interval";
  355.                     var r = "s";
  356.                     var c = this.repeat;
  357.                     if (this.repeat % 60 === 0) { r = "m"; c = c/60; }
  358.                     if (this.repeat % 1440 === 0) { r = "h"; c = c/60; }
  359.                     $("#inject-time-interval-count").val(c);
  360.                     $("#inject-time-interval-units").val(r);
  361.                     $("#inject-time-interval-days").prop("disabled","disabled");
  362.                 } else if (this.crontab) {
  363.                     var cronparts = this.crontab.split(" ");
  364.                     var days = cronparts[4];
  365.                     if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
  366.                         repeattype = "time";
  367.                         // Fixed time
  368.                         var time = cronparts[1]+":"+cronparts[0];
  369.                         $("#inject-time-time").val(time);
  370.                         $("#inject-time-type-select").val("s");
  371.                         if (days == "*") {
  372.                             $("#inject-time-time-days input[type=checkbox]").prop("checked",true);
  373.                         } else {
  374.                             $("#inject-time-time-days input[type=checkbox]").removeAttr("checked");
  375.                             days.split(",").forEach(function(v) {
  376.                                 $("#inject-time-time-days [value=" + v + "]").prop("checked", true);
  377.                             });
  378.                         }
  379.                     } else {
  380.                         repeattype = "interval-time";
  381.                         // interval - time period
  382.                         var minutes = cronparts[0].slice(2);
  383.                         if (minutes === "") { minutes = "0"; }
  384.                         $("#inject-time-interval-time-units").val(minutes);
  385.                         if (days == "*") {
  386.                             $("#inject-time-interval-time-days input[type=checkbox]").prop("checked",true);
  387.                         } else {
  388.                             $("#inject-time-interval-time-days input[type=checkbox]").removeAttr("checked");
  389.                             days.split(",").forEach(function(v) {
  390.                                 $("#inject-time-interval-time-days [value=" + v + "]").prop("checked", true);
  391.                             });
  392.                         }
  393.                         var time = cronparts[1];
  394.                         var timeparts = time.split(",");
  395.                         var start;
  396.                         var end;
  397.                         if (timeparts.length == 1) {
  398.                             // 0 or 0-10
  399.                             var hours = timeparts[0].split("-");
  400.                             if (hours.length == 1) {
  401.                                 if (hours[0] === "") {
  402.                                     start = "0";
  403.                                     end = "0";
  404.                                 }
  405.                                 else {
  406.                                     start = hours[0];
  407.                                     end = Number(hours[0])+1;
  408.                                 }
  409.                             } else {
  410.                                 start = hours[0];
  411.                                 end = Number(hours[1])+1;
  412.                             }
  413.                         } else {
  414.                             // 23,0 or 17-23,0-10 or 23,0-2 or 17-23,0
  415.                             var startparts = timeparts[0].split("-");
  416.                             start = startparts[0];
  417.      
  418.                             var endparts = timeparts[1].split("-");
  419.                             if (endparts.length == 1) {
  420.                                 end = Number(endparts[0])+1;
  421.                             } else {
  422.                                 end = Number(endparts[1])+1;
  423.                             }
  424.                         }
  425.                         $("#inject-time-interval-time-end").val(end);
  426.                         $("#inject-time-interval-time-start").val(start);
  427.      
  428.                     }
  429.                 } else {
  430.                     $("#inject-time-type-select").val("none");
  431.                 }
  432.      
  433.                 $(".inject-time-row").hide();
  434.                 $("#inject-time-type-select").val(repeattype);
  435.                 $("#inject-time-row-"+repeattype).show();
  436.      
  437.                 $("#node-input-payload").typedInput('type',this.payloadType);
  438.      
  439.                 $("#inject-time-type-select").change();
  440.                 $("#inject-time-interval-time-start").change();
  441.      
  442.             },
  443.             oneditsave: function() {
  444.                 var repeat = "";
  445.                 var crontab = "";
  446.                 var type = $("#inject-time-type-select").val();
  447.                 if (type == "none") {
  448.                     // nothing
  449.                 } else if (type == "interval") {
  450.                     var count = $("#inject-time-interval-count").val();
  451.                     var units = $("#inject-time-interval-units").val();
  452.                     if (units == "s") {
  453.                         repeat = count;
  454.                     } else {
  455.                         if (units == "m") {
  456.                             //crontab = "*/"+count+" * * * "+days;
  457.                             repeat = count * 60;
  458.                         } else if (units == "h") {
  459.                             //crontab = "0 */"+count+" * * "+days;
  460.                             repeat = count * 60 * 60;
  461.                         }
  462.                     }
  463.                 } else if (type == "interval-time") {
  464.                     repeat = "";
  465.                     var count = $("#inject-time-interval-time-units").val();
  466.                     var startTime = Number($("#inject-time-interval-time-start").val());
  467.                     var endTime = Number($("#inject-time-interval-time-end").val());
  468.                     var days = $('#inject-time-interval-time-days  input[type=checkbox]:checked').map(function(_, el) {
  469.                         return $(el).val()
  470.                     }).get();
  471.                     if (days.length == 0) {
  472.                         crontab = "";
  473.                     } else {
  474.                         if (days.length == 7) {
  475.                             days="*";
  476.                         } else {
  477.                             days = days.join(",");
  478.                         }
  479.                         var timerange = "";
  480.                         if (endTime == 0) {
  481.                             timerange = startTime+"-23";
  482.                         } else if (startTime+1 < endTime) {
  483.                             timerange = startTime+"-"+(endTime-1);
  484.                         } else if (startTime+1 == endTime) {
  485.                             timerange = startTime;
  486.                         } else {
  487.                             var startpart = "";
  488.                             var endpart = "";
  489.                             if (startTime == 23) {
  490.                                 startpart = "23";
  491.                             } else {
  492.                                 startpart = startTime+"-23";
  493.                             }
  494.                             if (endTime == 1) {
  495.                                 endpart = "0";
  496.                             } else {
  497.                                 endpart = "0-"+(endTime-1);
  498.                             }
  499.                             timerange = startpart+","+endpart;
  500.                         }
  501.                         if (count === "0") {
  502.                             crontab = count+" "+timerange+" * * "+days;
  503.                         } else {
  504.                             crontab = "*/"+count+" "+timerange+" * * "+days;
  505.                         }
  506.                     }
  507.                 } else if (type == "time") {
  508.                     var time = $("#inject-time-time").val();
  509.                     var days = $('#inject-time-time-days  input[type=checkbox]:checked').map(function(_, el) {
  510.                         return $(el).val()
  511.                     }).get();
  512.                     if (days.length == 0) {
  513.                         crontab = "";
  514.                     } else {
  515.                         if (days.length == 7) {
  516.                             days="*";
  517.                         } else {
  518.                             days = days.join(",");
  519.                         }
  520.                         var parts = time.split(":");
  521.                         repeat = "";
  522.                         crontab = parts[1]+" "+parts[0]+" * * "+days;
  523.                     }
  524.                 }
  525.      
  526.                 $("#node-input-repeat").val(repeat);
  527.                 $("#node-input-crontab").val(crontab);
  528.             },
  529.             button: {
  530.                 onclick: function() {
  531.                     var label = (this.name||this.payload).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
  532.                     if (this.payloadType === "date") { label = this._("inject.timestamp"); }
  533.                     if (this.payloadType === "none") { label = this._("inject.blank"); }
  534.                     var node = this;
  535.                     $.ajax({
  536.                         url: "inject/"+this.id,
  537.                         type:"POST",
  538.                         success: function(resp) {
  539.                             RED.notify(node._("inject.success",{label:label}),"success");
  540.                         },
  541.                         error: function(jqXHR,textStatus,errorThrown) {
  542.                             if (jqXHR.status == 404) {
  543.                                 RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.not-deployed")}),"error");
  544.                             } else if (jqXHR.status == 500) {
  545.                                 RED.notify(node._("common.notification.error",{message:node._("inject.errors.failed")}),"error");
  546.                             } else if (jqXHR.status == 0) {
  547.                                 RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.no-response")}),"error");
  548.                             } else {
  549.                                 RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:jqXHR.status,message:textStatus})}),"error");
  550.                             }
  551.                         }
  552.                     });
  553.                 }
  554.             }
  555.         });
  556.      
  557.     </script>
  558.     <script type="text/x-red" data-help-name="inject">
  559.         <p>Pressing the button on the left side of the node allows a message on a topic
  560.            to be injected into the flow.</p>
  561.         <p>The payload defaults to the current time in millisecs since 1970, but can
  562.            also be set to various other javascript types.</p>
  563.         <p>The repeat function allows the payload to be sent on the required schedule.</p>
  564.         <p>The <i>Inject once at start</i> option actually waits a short interval before firing
  565.            to give other nodes a chance to instantiate properly.</p>
  566.         <p>The <i>Flow</i> and <i>Global</i> options allow one to inject a flow or global
  567.             context value.
  568.         </p>
  569.         <p><b>Note: </b>"Interval between times" and "at a specific time" uses cron.
  570.            This means that 20 minutes will be at the next hour, 20 minutes past and
  571.            40 minutes past - not in 20 minutes time. If you want every 20 minutes
  572.            from now - use the "interval" option.</p>
  573.         <p><b>Note: </b>all string input is escaped. To add a carriage return to a string
  574.         you should use a following function.</p>
  575.     </script><!--
  576.       Copyright JS Foundation and other contributors, http://js.foundation
  577.      
  578.       Licensed under the Apache License, Version 2.0 (the "License");
  579.       you may not use this file except in compliance with the License.
  580.       You may obtain a copy of the License at
  581.      
  582.       http://www.apache.org/licenses/LICENSE-2.0
  583.      
  584.       Unless required by applicable law or agreed to in writing, software
  585.       distributed under the License is distributed on an "AS IS" BASIS,
  586.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  587.       See the License for the specific language governing permissions and
  588.       limitations under the License.
  589.     -->
  590.      
  591.     <script type="text/x-red" data-template-name="catch">
  592.         <div class="form-row">
  593.             <label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
  594.             <select id="node-input-scope-select">
  595.                 <option value="all" data-i18n="catch.scope.all"></option>
  596.                 <option value="target" data-i18n="catch.scope.selected"></options>
  597.             </select>
  598.         </div>
  599.         <div class="form-row node-input-target-row" style="display: none;">
  600.             <div id="node-input-catch-target-container-div" style="min-height: 100px;position: relative;   box-sizing: border-box; border-radius: 2px; height: 180px;  border: 1px solid #ccc;overflow:hidden; ">
  601.                 <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">
  602.                   <input type="checkbox" data-i18n="[title]catch.label.selectAll" id="node-input-target-node-checkbox-all" style="width: 30px; margin: 0 2px 1px 2px;">
  603.                   <div style="display: inline-block;"><a id="node-input-target-sort-label" href="#" data-i18n="[title]catch.label.sortByLabel"><span data-i18n="catch.label.node"></span> <i class="node-input-catch-sort-label-a fa fa-caret-down"></i><i class="node-input-catch-sort-label-d fa fa-caret-up"></i></a></div>
  604.                   <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-target-sort-type" href="#" data-i18n="[title]catch.label.sortByType"><i class="node-input-catch-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-catch-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="catch.label.type"></span></a></div>
  605.                 </div>
  606.                 <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">
  607.                     <ul id="node-input-catch-target-container" style=" list-style-type:none; margin: 0;"></ul>
  608.                 </div>
  609.             </div>
  610.         </div>
  611.         <div class="form-row">
  612.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  613.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  614.         </div>
  615.     </script>
  616.      
  617.     <style>
  618.     #node-input-catch-target-container {
  619.         position: relative;
  620.     }
  621.     #node-input-catch-target-container li {
  622.         padding: 2px 5px;
  623.         background: none;
  624.         font-size: 0.8em;
  625.         margin:0;
  626.         white-space: nowrap;
  627.     }
  628.     #node-input-catch-target-container li label {
  629.         margin-bottom: 0;
  630.         width: 100%;
  631.     }
  632.     #node-input-catch-target-container li label input {
  633.         vertical-align: top;
  634.         width:15px;
  635.         margin-right: 10px;
  636.     }
  637.     #node-input-catch-target-container li:hover,
  638.     #node-input-catch-target-container li:hover .node-input-target-node-sublabel {
  639.         background: #f0f0f0;
  640.     }
  641.     .node-input-target-node-sublabel {
  642.         position:absolute;
  643.         right: 0px;
  644.         padding-right: 10px;
  645.         padding-left: 10px;
  646.         font-size: 0.8em;
  647.         background: #fbfbfb;
  648.     }
  649.     </style>
  650.     <script type="text/javascript">
  651.         RED.nodes.registerType('catch',{
  652.             category: 'input',
  653.             color:"#e49191",
  654.             defaults: {
  655.                 name: {value:""},
  656.                 scope: {value:null}
  657.             },
  658.             inputs:0,
  659.             outputs:1,
  660.             icon: "alert.png",
  661.             label: function() {
  662.                 return this.name||(this.scope?this._("catch.catchNodes",{number:this.scope.length}):this._("catch.catch"));
  663.             },
  664.             labelStyle: function() {
  665.                 return this.name?"node_label_italic":"";
  666.             },
  667.             oneditprepare: function() {
  668.                 var nodeList = $("#node-input-catch-target-container");
  669.                 var node = this;
  670.                 this.resize = function() {
  671.                     var rows = $("#dialog-form>div:not(.node-input-target-row)");
  672.                     var height = $("#dialog-form").height();
  673.                     for (var i=0;i<rows.size();i++) {
  674.                         height -= $(rows[i]).outerHeight(true);
  675.                     }
  676.                     var editorRow = $("#dialog-form>div.node-input-target-row");
  677.                     height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  678.                     $("#node-input-catch-target-container-div").css("height",height+"px");
  679.                 }
  680.      
  681.                 function createNodeList() {
  682.                     var scope = node.scope || [];
  683.                     nodeList.empty();
  684.      
  685.                     var candidateNodes = RED.nodes.filterNodes({z:node.z});
  686.                     var allChecked = true;
  687.      
  688.                     candidateNodes.forEach(function(n) {
  689.                         if (n.id === node.id) {
  690.                             return;
  691.                         }
  692.                         var isChecked = scope.indexOf(n.id) !== -1;
  693.      
  694.                         allChecked = allChecked && isChecked;
  695.      
  696.                         var container = $('<li/>',{class:"node-input-target-node"});
  697.                         var row = $('<label/>',{for:"node-input-target-node-"+n.id}).appendTo(container);
  698.                         $('<input>',{type:"checkbox",class:"node-input-target-node-checkbox",id:"node-input-target-node-"+n.id})
  699.                             .data('node-id',n.id)
  700.                             .prop('checked', isChecked)
  701.                             .appendTo(row);
  702.                         container.on('mouseover',function(e) {
  703.                             n.highlighted = true;
  704.                             n.dirty = true;
  705.                             RED.view.redraw();
  706.                         });
  707.                         container.on('mouseout',function(e) {
  708.                             n.highlighted = false;
  709.                             n.dirty = true;
  710.                             RED.view.redraw();
  711.                         });
  712.                         var labelSpan = $('<span>');
  713.                         var nodeDef = RED.nodes.getType(n.type);
  714.                         var label;
  715.                         var sublabel;
  716.                         if (nodeDef) {
  717.                             var l = nodeDef.label;
  718.                             label = (typeof l === "function" ? l.call(n) : l)||"";
  719.                             sublabel = n.type;
  720.                             if (sublabel.indexOf("subflow:") === 0) {
  721.                                 var subflowId = sublabel.substring(8);
  722.                                 var subflow = RED.nodes.subflow(subflowId);
  723.                                 sublabel = "subflow : "+subflow.name;
  724.                             }
  725.                         }
  726.                         if (!nodeDef || !label) {
  727.                             label = n.type;
  728.                         }
  729.                         $('<span>',{class:"node-input-target-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
  730.                         if (sublabel) {
  731.                             $('<span>',{class:"node-input-target-node-sublabel"}).text(sublabel).appendTo(row);
  732.                         }
  733.      
  734.                         container.appendTo(nodeList);
  735.                     });
  736.      
  737.                     $(".node-input-target-node-checkbox").change(function() {
  738.                         if (!this.checked) {
  739.                             $("#node-input-target-node-checkbox-all").prop('checked',false);
  740.                         }
  741.                     });
  742.      
  743.                     $("#node-input-target-node-checkbox-all").prop('checked',allChecked);
  744.      
  745.                     sortNodeList('label');
  746.                 }
  747.      
  748.                 function sortNodeList(sortOn) {
  749.                     var currentSort = nodeList.data('currentSort');
  750.                     var currentSortOrder = nodeList.data('currentSortOrder');
  751.      
  752.                     if (!currentSort) {
  753.                         currentSort = sortOn;
  754.                         currentSortOrder = 'a';
  755.                     } else {
  756.                         if (currentSort === sortOn) {
  757.                             currentSortOrder = (currentSortOrder === 'a'?'d':'a');
  758.                         } else {
  759.                             currentSortOrder = 'a';
  760.                         }
  761.                         currentSort = sortOn;
  762.                     }
  763.                     nodeList.data('currentSort',currentSort);
  764.                     nodeList.data('currentSortOrder',currentSortOrder);
  765.      
  766.                     $("#node-input-catch-target-container-div .fa").hide();
  767.                     $(".node-input-catch-sort-"+currentSort+"-"+currentSortOrder).show();
  768.      
  769.      
  770.                     var items = nodeList.find("li").get();
  771.                     items.sort(function(a,b) {
  772.                         var labelA = $(a).find(".node-input-target-node-"+currentSort).text().toLowerCase();
  773.                         var labelB = $(b).find(".node-input-target-node-"+currentSort).text().toLowerCase();
  774.                         if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
  775.                         if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
  776.                         return 0;
  777.                     });
  778.                     $.each(items, function(i, li){
  779.                         nodeList.append(li);
  780.                     });
  781.                 }
  782.                 $("#node-input-target-sort-label").click(function(e) {
  783.                     e.preventDefault();
  784.                     sortNodeList('label');
  785.                 });
  786.      
  787.                 $("#node-input-target-sort-type").click(function(e) {
  788.                     e.preventDefault();
  789.                     sortNodeList('sublabel')
  790.                 });
  791.                 $("#node-input-target-node-checkbox-all").change(function() {
  792.                     $(".node-input-target-node-checkbox").prop('checked',this.checked);
  793.                 })
  794.      
  795.      
  796.      
  797.                 $("#node-input-scope-select").change(function(e) {
  798.                     var scope = $(this).val();
  799.                     if (scope === "target") {
  800.                         createNodeList();
  801.                         $(".node-input-target-row").show();
  802.                     } else {
  803.                         $(".node-input-target-row").hide();
  804.                     }
  805.                     node.resize();
  806.                 });
  807.                 if (this.scope == null) {
  808.                     $("#node-input-scope-select").val("all");
  809.                 } else {
  810.                     $("#node-input-scope-select").val("target");
  811.                 }
  812.                 $("#node-input-scope-select").change();
  813.             },
  814.             oneditsave: function() {
  815.                 var scope = $("#node-input-scope-select").val();
  816.                 if (scope === 'all') {
  817.                     this.scope = null;
  818.                 } else {
  819.                     var node = this;
  820.                     node.scope = [];
  821.                     $(".node-input-target-node-checkbox").each(function(n) {
  822.                         if ($(this).prop("checked")) {
  823.                             node.scope.push($(this).data('node-id'));
  824.                         }
  825.                     })
  826.                 }
  827.             },
  828.             oneditresize: function(size) {
  829.                 this.resize();
  830.             }
  831.         });
  832.     </script>
  833.     <script type="text/x-red" data-help-name="catch">
  834.         <p>Catch errors thrown by nodes on the same tab.</p>
  835.         <p>If a node throws a error whilst handling a message, the flow will typically
  836.            halt. This node can be used to catch those errors and handle them with a
  837.            dedicated flow.</p>
  838.         <p>The node will catch errors thrown by any node on the same tab. If there
  839.            are multiple catch nodes on a tab, they will all get triggered.</p>
  840.         <p>If an error is thrown within a subflow, the error will get handled by any
  841.            catch nodes within the subflow. If none exists, the error is propagated
  842.            up to the tab the subflow instance is on.</p>
  843.         <p>The message sent by this node will be the original message if the node that
  844.            threw the error provided it. The message will have an <code>error</code>
  845.            property with the following attributes:
  846.            <ul>
  847.             <li><code>message</code> : the error message</li>
  848.             <li><code>source.id</code> : the id of the node that threw the error</li>
  849.             <li><code>source.type</code> : the type of the node that threw the error</li>
  850.             <li><code>source.name</code> : the name, if set, of the node that threw the error</li>
  851.            </ul>
  852.        </p>
  853.        <p>If the message already had a <code>error</code> property, it is copied to <code>_error</code>.</p>
  854.     </script><!--
  855.       Copyright JS Foundation and other contributors, http://js.foundation
  856.      
  857.       Licensed under the Apache License, Version 2.0 (the "License");
  858.       you may not use this file except in compliance with the License.
  859.       You may obtain a copy of the License at
  860.      
  861.       http://www.apache.org/licenses/LICENSE-2.0
  862.      
  863.       Unless required by applicable law or agreed to in writing, software
  864.       distributed under the License is distributed on an "AS IS" BASIS,
  865.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  866.       See the License for the specific language governing permissions and
  867.       limitations under the License.
  868.     -->
  869.      
  870.     <script type="text/x-red" data-template-name="status">
  871.         <div class="form-row">
  872.             <label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
  873.             <select id="node-input-scope-select">
  874.                 <option value="all" data-i18n="status.scope.all"></option>
  875.                 <option value="target" data-i18n="status.scope.selected"></options>
  876.             </select>
  877.         </div>
  878.         <div class="form-row node-input-target-row" style="display: none;">
  879.             <div id="node-input-status-target-container-div" style=" min-height: 100px;position: relative;   box-sizing: border-box; border-radius: 2px; height: 180px;  border: 1px solid #ccc;overflow:hidden; ">
  880.                 <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">
  881.                   <input type="checkbox" data-i18n="[title]status.label.selectAll" id="node-input-target-node-checkbox-all" style="width: 30px; margin: 0 2px 1px 2px;">
  882.                   <div style="display: inline-block;"><a id="node-input-target-sort-label" href="#" data-i18n="[title]status.label.sortByLabel"><span data-i18n="status.label.node"></span> <i class="node-input-status-sort-label-a fa fa-caret-down"></i><i class="node-input-status-sort-label-d fa fa-caret-up"></i></a></div>
  883.                   <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-target-sort-type" href="#" data-i18n="[title]status.label.sortByType"><i class="node-input-status-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-status-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="status.label.type"></span></a></div>
  884.                 </div>
  885.                 <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">
  886.                     <ul id="node-input-status-target-container" style=" list-style-type:none; margin: 0;"></ul>
  887.                 </div>
  888.             </div>
  889.         </div>
  890.         <div class="form-row">
  891.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  892.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  893.         </div>
  894.     </script>
  895.      
  896.     <style>
  897.     #node-input-status-target-container {
  898.         position: relative;
  899.     }
  900.     #node-input-status-target-container li {
  901.         padding: 2px 5px;
  902.         background: none;
  903.         font-size: 0.8em;
  904.         margin:0;
  905.         white-space: nowrap;
  906.     }
  907.     #node-input-status-target-container li label {
  908.         margin-bottom: 0;
  909.         width: 100%;
  910.     }
  911.     #node-input-status-target-container li label input {
  912.         vertical-align: top;
  913.         width:15px;
  914.         margin-right: 10px;
  915.     }
  916.     #node-input-status-target-container li:hover,
  917.     #node-input-status-target-container li:hover .node-input-target-node-sublabel {
  918.         background: #f0f0f0;
  919.     }
  920.     .node-input-target-node-sublabel {
  921.         position:absolute;
  922.         right: 0px;
  923.         padding-right: 10px;
  924.         padding-left: 10px;
  925.         font-size: 0.8em;
  926.         background: #fbfbfb;
  927.     }
  928.     </style>
  929.     <script type="text/javascript">
  930.         RED.nodes.registerType('status',{
  931.             category: 'input',
  932.             color:"#c0edc0",
  933.             defaults: {
  934.                 name: {value:""},
  935.                 scope: {value:null}
  936.             },
  937.             inputs:0,
  938.             outputs:1,
  939.             icon: "alert.png",
  940.             label: function() {
  941.                 return this.name||(this.scope?this._("status.statusNodes",{number:this.scope.length}):this._("status.status"));
  942.             },
  943.             labelStyle: function() {
  944.                 return this.name?"node_label_italic":"";
  945.             },
  946.             oneditprepare: function() {
  947.                 var nodeList = $("#node-input-status-target-container");
  948.                 var node = this;
  949.                 this.resize = function() {
  950.                     var rows = $("#dialog-form>div:not(.node-input-target-row)");
  951.                     var height = $("#dialog-form").height();
  952.                     for (var i=0;i<rows.size();i++) {
  953.                         height -= $(rows[i]).outerHeight(true);
  954.                     }
  955.                     var editorRow = $("#dialog-form>div.node-input-target-row");
  956.                     height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  957.                     $("#node-input-status-target-container-div").css("height",height+"px");
  958.                 }
  959.      
  960.                 function createNodeList() {
  961.                     var scope = node.scope || [];
  962.                     nodeList.empty();
  963.      
  964.                     var candidateNodes = RED.nodes.filterNodes({z:node.z});
  965.                     var allChecked = true;
  966.      
  967.                     candidateNodes.forEach(function(n) {
  968.                         if (n.id === node.id) {
  969.                             return;
  970.                         }
  971.                         var isChecked = scope.indexOf(n.id) !== -1;
  972.      
  973.                         allChecked = allChecked && isChecked;
  974.      
  975.                         var container = $('<li/>',{class:"node-input-target-node"});
  976.                         var row = $('<label/>',{for:"node-input-target-node-"+n.id}).appendTo(container);
  977.                         $('<input>',{type:"checkbox",class:"node-input-target-node-checkbox",id:"node-input-target-node-"+n.id})
  978.                             .data('node-id',n.id)
  979.                             .prop('checked', isChecked)
  980.                             .appendTo(row);
  981.                         container.on('mouseover',function(e) {
  982.                             n.highlighted = true;
  983.                             n.dirty = true;
  984.                             RED.view.redraw();
  985.                         });
  986.                         container.on('mouseout',function(e) {
  987.                             n.highlighted = false;
  988.                             n.dirty = true;
  989.                             RED.view.redraw();
  990.                         });
  991.                         var labelSpan = $('<span>');
  992.                         var nodeDef = RED.nodes.getType(n.type);
  993.                         var label;
  994.                         var sublabel;
  995.                         if (nodeDef) {
  996.                             var l = nodeDef.label;
  997.                             label = (typeof l === "function" ? l.call(n) : l)||"";
  998.                             sublabel = n.type;
  999.                             if (sublabel.indexOf("subflow:") === 0) {
  1000.                                 var subflowId = sublabel.substring(8);
  1001.                                 var subflow = RED.nodes.subflow(subflowId);
  1002.                                 sublabel = "subflow : "+subflow.name;
  1003.                             }
  1004.                         }
  1005.                         if (!nodeDef || !label) {
  1006.                             label = n.type;
  1007.                         }
  1008.                         $('<span>',{class:"node-input-target-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
  1009.                         if (sublabel) {
  1010.                             $('<span>',{class:"node-input-target-node-sublabel"}).text(sublabel).appendTo(row);
  1011.                         }
  1012.      
  1013.                         container.appendTo(nodeList);
  1014.                     });
  1015.      
  1016.                     $(".node-input-target-node-checkbox").change(function() {
  1017.                         if (!this.checked) {
  1018.                             $("#node-input-target-node-checkbox-all").prop('checked',false);
  1019.                         }
  1020.                     });
  1021.      
  1022.                     $("#node-input-target-node-checkbox-all").prop('checked',allChecked);
  1023.      
  1024.                     sortNodeList('label');
  1025.                 }
  1026.      
  1027.                 function sortNodeList(sortOn) {
  1028.                     var currentSort = nodeList.data('currentSort');
  1029.                     var currentSortOrder = nodeList.data('currentSortOrder');
  1030.      
  1031.                     if (!currentSort) {
  1032.                         currentSort = sortOn;
  1033.                         currentSortOrder = 'a';
  1034.                     } else {
  1035.                         if (currentSort === sortOn) {
  1036.                             currentSortOrder = (currentSortOrder === 'a'?'d':'a');
  1037.                         } else {
  1038.                             currentSortOrder = 'a';
  1039.                         }
  1040.                         currentSort = sortOn;
  1041.                     }
  1042.                     nodeList.data('currentSort',currentSort);
  1043.                     nodeList.data('currentSortOrder',currentSortOrder);
  1044.      
  1045.                     $("#node-input-status-target-container-div .fa").hide();
  1046.                     $(".node-input-status-sort-"+currentSort+"-"+currentSortOrder).show();
  1047.      
  1048.      
  1049.                     var items = nodeList.find("li").get();
  1050.                     items.sort(function(a,b) {
  1051.                         var labelA = $(a).find(".node-input-target-node-"+currentSort).text().toLowerCase();
  1052.                         var labelB = $(b).find(".node-input-target-node-"+currentSort).text().toLowerCase();
  1053.                         if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
  1054.                         if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
  1055.                         return 0;
  1056.                     });
  1057.                     $.each(items, function(i, li){
  1058.                         nodeList.append(li);
  1059.                     });
  1060.                 }
  1061.                 $("#node-input-target-sort-label").click(function(e) {
  1062.                     e.preventDefault();
  1063.                     sortNodeList('label');
  1064.                 });
  1065.      
  1066.                 $("#node-input-target-sort-type").click(function(e) {
  1067.                     e.preventDefault();
  1068.                     sortNodeList('sublabel')
  1069.                 });
  1070.                 $("#node-input-target-node-checkbox-all").change(function() {
  1071.                     $(".node-input-target-node-checkbox").prop('checked',this.checked);
  1072.                 })
  1073.      
  1074.                 $("#node-input-scope-select").change(function(e) {
  1075.                     var scope = $(this).val();
  1076.                     if (scope === "target") {
  1077.                         createNodeList();
  1078.                         $(".node-input-target-row").show();
  1079.                     } else {
  1080.                         $(".node-input-target-row").hide();
  1081.                     }
  1082.                     node.resize();
  1083.                 });
  1084.                 if (this.scope == null) {
  1085.                     $("#node-input-scope-select").val("all");
  1086.                 } else {
  1087.                     $("#node-input-scope-select").val("target");
  1088.                 }
  1089.                 $("#node-input-scope-select").change();
  1090.             },
  1091.             oneditsave: function() {
  1092.                 var scope = $("#node-input-scope-select").val();
  1093.                 if (scope === 'all') {
  1094.                     this.scope = null;
  1095.                 } else {
  1096.                     var node = this;
  1097.                     node.scope = [];
  1098.                     $(".node-input-target-node-checkbox").each(function(n) {
  1099.                         if ($(this).prop("checked")) {
  1100.                             node.scope.push($(this).data('node-id'));
  1101.                         }
  1102.                     })
  1103.      
  1104.                 }
  1105.             },
  1106.             oneditresize: function(size) {
  1107.                 this.resize();
  1108.             }
  1109.         });
  1110.     </script>
  1111.     <script type="text/x-red" data-help-name="status">
  1112.         <p>Send status messages from other nodes on the same tab.</p>
  1113.         <p>The message sent by this node will have a <code>status</code> property
  1114.         with the following attributes:
  1115.             <ul>
  1116.                 <li><code>text</code> : the status text</li>
  1117.                 <li><code>source.type</code> : the type of the node that reported status</li>
  1118.                 <li><code>source.id</code> : the id of the node that reported status</li>
  1119.                 <li><code>source.name</code> : the name, if set, of the node that reported status</li>
  1120.      
  1121.             </ul>
  1122.        </p>
  1123.     </script><!--
  1124.       Copyright JS Foundation and other contributors, http://js.foundation
  1125.      
  1126.       Licensed under the Apache License, Version 2.0 (the "License");
  1127.       you may not use this file except in compliance with the License.
  1128.       You may obtain a copy of the License at
  1129.      
  1130.       http://www.apache.org/licenses/LICENSE-2.0
  1131.      
  1132.       Unless required by applicable law or agreed to in writing, software
  1133.       distributed under the License is distributed on an "AS IS" BASIS,
  1134.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1135.       See the License for the specific language governing permissions and
  1136.       limitations under the License.
  1137.     -->
  1138.      
  1139.     <script type="text/x-red" data-template-name="debug">
  1140.         <div class="form-row">
  1141.             <label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
  1142.             <input id="node-input-typed-complete" type="text" style="width: 70%">
  1143.             <input id="node-input-complete" type="hidden">
  1144.         </div>
  1145.         <div class="form-row">
  1146.             <label for="node-input-console"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
  1147.             <select type="text" id="node-input-console" style="display: inline-block; width: 250px; vertical-align: top;">
  1148.                 <option value="false" data-i18n="debug.debtab"></option>
  1149.                 <option value="true" data-i18n="debug.tabcon"></option>
  1150.             </select>
  1151.         </div>
  1152.         <div class="form-row">
  1153.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1154.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1155.         </div>
  1156.     </script>
  1157.      
  1158.      
  1159.     <script src="debug/view/debug-utils.js"></script>
  1160.      
  1161.     <script type="text/javascript">
  1162.     (function() {
  1163.         var subWindow = null;
  1164.         RED.nodes.registerType('debug',{
  1165.             category: 'output',
  1166.             defaults: {
  1167.                 name: {value:""},
  1168.                 active: {value:true},
  1169.                 console: {value:"false"},
  1170.                 complete: {value:"false", required:true}
  1171.             },
  1172.             label: function() {
  1173.                 var suffix = "";
  1174.                 if (this.console === true || this.console === "true") { suffix = " โ‡ฒ"; }
  1175.                 if (this.complete === true || this.complete === "true") {
  1176.                     return (this.name||"msg") + suffix;
  1177.                 } else {
  1178.                     return (this.name || "msg." + ((!this.complete || this.complete === "false") ? "payload" : this.complete)) + suffix;
  1179.                 }
  1180.             },
  1181.             labelStyle: function() {
  1182.                 return this.name?"node_label_italic":"";
  1183.             },
  1184.             color:"#87a980",
  1185.             inputs:1,
  1186.             outputs:0,
  1187.             icon: "debug.png",
  1188.             align: "right",
  1189.             button: {
  1190.                 toggle: "active",
  1191.                 onclick: function() {
  1192.                     var label = this.name||"debug";
  1193.                     var node = this;
  1194.                     $.ajax({
  1195.                         url: "debug/"+this.id+"/"+(this.active?"enable":"disable"),
  1196.                         type: "POST",
  1197.                         success: function(resp, textStatus, xhr) {
  1198.                             if (xhr.status == 200) {
  1199.                                 RED.notify(node._("debug.notification.activated",{label:label}),"success");
  1200.                             } else if (xhr.status == 201) {
  1201.                                 RED.notify(node._("debug.notification.deactivated",{label:label}),"success");
  1202.                             }
  1203.                         },
  1204.                         error: function(jqXHR,textStatus,errorThrown) {
  1205.                             if (jqXHR.status == 404) {
  1206.                                 RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.not-deployed")}),"error");
  1207.                             } else if (jqXHR.status == 0) {
  1208.                                 RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.no-response")}),"error");
  1209.                             } else {
  1210.                                 RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:err.status,message:err.response})}),"error");
  1211.                             }
  1212.                         }
  1213.                     });
  1214.                 }
  1215.             },
  1216.             onpaletteadd: function() {
  1217.      
  1218.                 var options = {
  1219.                     messageMouseEnter: function(sourceId) {
  1220.                         if (sourceId) {
  1221.                             var n = RED.nodes.node(sourceId);
  1222.                             if (n) {
  1223.                                 n.highlighted = true;
  1224.                                 n.dirty = true;
  1225.                             }
  1226.                             RED.view.redraw();
  1227.                         }
  1228.                     },
  1229.                     messageMouseLeave: function(sourceId) {
  1230.                         if (sourceId) {
  1231.                             var n = RED.nodes.node(sourceId);
  1232.                             if (n) {
  1233.                                 n.highlighted = false;
  1234.                                 n.dirty = true;
  1235.                             }
  1236.                             RED.view.redraw();
  1237.                         }
  1238.                     },
  1239.                     messageSourceClick: function(sourceId) {
  1240.                         RED.view.reveal(sourceId);
  1241.                     },
  1242.                     clear: function() {
  1243.                         RED.nodes.eachNode(function(node) {
  1244.                             node.highlighted = false;
  1245.                             node.dirty = true;
  1246.                         });
  1247.                         RED.view.redraw();
  1248.                     }
  1249.                 }
  1250.      
  1251.                 var uiComponents = RED.debug.init(options);
  1252.      
  1253.                 RED.sidebar.addTab({
  1254.                     id: "debug",
  1255.                     label: this._("debug.sidebar.label"),
  1256.                     name: this._("debug.sidebar.name"),
  1257.                     content: uiComponents.content,
  1258.                     toolbar: uiComponents.footer,
  1259.                     enableOnEdit: true
  1260.                 });
  1261.                 RED.actions.add("core:show-debug-tab",function() { RED.sidebar.show('debug')});
  1262.      
  1263.                 var that = this;
  1264.                 RED._debug = function(msg) {
  1265.                     that.handleDebugMessage("",{
  1266.                         name:"debug",
  1267.                         msg:msg
  1268.                     });
  1269.                 }
  1270.      
  1271.                 this.refreshMessageList = function() {
  1272.                     RED.debug.refreshMessageList(RED.workspaces.active());
  1273.                     if (subWindow) {
  1274.                         try {
  1275.                             subWindow.postMessage({event:"workspaceChange",activeWorkspace:RED.workspaces.active()},"*")
  1276.                         } catch(err) {
  1277.                             console.log(err);
  1278.                         }
  1279.                     }
  1280.                 }
  1281.      
  1282.                 this.handleDebugMessage = function(t,o) {
  1283.                     var sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z);
  1284.                     if (sourceNode) {
  1285.                         o._source = {id:sourceNode.id,z:sourceNode.z,name:sourceNode.name};
  1286.      
  1287.                     }
  1288.                     RED.debug.handleDebugMessage(o);
  1289.                     if (subWindow) {
  1290.                         try {
  1291.                             subWindow.postMessage({event:"message",msg:o},"*")
  1292.                         } catch(err) {
  1293.                             console.log(err);
  1294.                         }
  1295.                     }
  1296.                 };
  1297.                 RED.comms.subscribe("debug",this.handleDebugMessage);
  1298.      
  1299.                 RED.events.on("workspace:change", this.refreshMessageList);
  1300.      
  1301.                 $("#debug-tab-open").click(function(e) {
  1302.                     e.preventDefault();
  1303.                     subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
  1304.                     subWindow.onload = function() {
  1305.                         subWindow.postMessage({event:"workspaceChange",activeWorkspace:RED.workspaces.active()},"*");
  1306.                     }
  1307.                 });
  1308.      
  1309.                 $(window).unload(function() {
  1310.                     if (subWindow) {
  1311.                         try {
  1312.                             subWindow.close()
  1313.                         } catch(err) {
  1314.                             console.log(err);
  1315.                         }
  1316.                     }
  1317.                 });
  1318.      
  1319.                 this.handleWindowMessage = function(evt) {
  1320.                     var msg = evt.data;
  1321.                     if (msg.event === "mouseEnter") {
  1322.                         options.messageMouseEnter(msg.id);
  1323.                     } else if (msg.event === "mouseLeave") {
  1324.                         options.messageMouseLeave(msg.id);
  1325.                     } else if (msg.event === "mouseClick") {
  1326.                         options.messageSourceClick(msg.id);
  1327.                     } else if (msg.event === "clear") {
  1328.                         options.clear();
  1329.                     }
  1330.                 }
  1331.                 window.addEventListener('message',this.handleWindowMessage);
  1332.             },
  1333.             onpaletteremove: function() {
  1334.                 RED.comms.unsubscribe("debug",this.handleDebugMessage);
  1335.                 RED.sidebar.removeTab("debug");
  1336.                 RED.events.off("workspace:change", this.refreshMessageList);
  1337.                 window.removeEventListener("message",this.handleWindowMessage);
  1338.                 RED.actions.remove("core:show-debug");
  1339.      
  1340.                 delete RED._debug;
  1341.             },
  1342.             oneditprepare: function() {
  1343.                 $("#node-input-typed-complete").typedInput({types:['msg', {value:"full",label:RED._("node-red:debug.msgobj"),hasValue:false}]});
  1344.                 if (this.complete === "true" || this.complete === true) {
  1345.                     // show complete message object
  1346.                     $("#node-input-typed-complete").typedInput('type','full');
  1347.                 } else {
  1348.                     var property = (!this.complete||(this.complete === "false")) ? "payload" : this.complete+"";
  1349.                     $("#node-input-typed-complete").typedInput('type','msg');
  1350.                     $("#node-input-typed-complete").typedInput('value',property);
  1351.                 }
  1352.                 $("#node-input-typed-complete").on('change',function() {
  1353.                     if ($("#node-input-typed-complete").typedInput('type') === 'msg'
  1354.                         &&
  1355.                         $("#node-input-typed-complete").typedInput('value') === ''
  1356.                     ) {
  1357.                         $("#node-input-typed-complete").typedInput('value','payload');
  1358.                     }
  1359.                 });
  1360.             },
  1361.             oneditsave: function() {
  1362.                 var type = $("#node-input-typed-complete").typedInput('type');
  1363.                 if (type === 'full') {
  1364.                     $("#node-input-complete").val("true");
  1365.                 } else {
  1366.                     $("#node-input-complete").val($("#node-input-typed-complete").typedInput('value'));
  1367.                 }
  1368.             }
  1369.      
  1370.         });
  1371.     })();
  1372.     </script>
  1373.     <script type="text/x-red" data-help-name="debug">
  1374.         <p>The Debug node can be connected to the output of any node. It can be used to display the output of any message
  1375.         property in the debug tab of the sidebar. The default is to display <code>msg.payload</code>.</p>
  1376.         <p>Each message will also display the timestamp, <code>msg.topic</code> and the type of property chosen to output.</p>
  1377.         <p>The sidebar can be accessed under the options drop-down in the top right corner.</p>
  1378.         <p>The button to the right of the node will toggle its output on and off so you can de-clutter the debug window.</p>
  1379.         <p>If the payload is an object or buffer it will be stringified first for display and indicate that by saying "(Object)" or "(Buffer)".</p>
  1380.         <p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p>
  1381.         <p>Optionally can show the complete <code>msg</code> object, and send messages to the console log (โ‡ถ).</p>
  1382.         <p>In addition any calls to node.warn or node.error will appear here.</p>
  1383.     </script><!--
  1384.       Copyright JS Foundation and other contributors, http://js.foundation
  1385.      
  1386.       Licensed under the Apache License, Version 2.0 (the "License");
  1387.       you may not use this file except in compliance with the License.
  1388.       You may obtain a copy of the License at
  1389.      
  1390.       http://www.apache.org/licenses/LICENSE-2.0
  1391.      
  1392.       Unless required by applicable law or agreed to in writing, software
  1393.       distributed under the License is distributed on an "AS IS" BASIS,
  1394.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1395.       See the License for the specific language governing permissions and
  1396.       limitations under the License.
  1397.     -->
  1398.      
  1399.     <script type="text/x-red" data-template-name="link in">
  1400.         <div class="form-row">
  1401.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1402.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1403.         </div>
  1404.         <div class="form-row node-input-link-row"></div>
  1405.     </script>
  1406.     <script type="text/x-red" data-template-name="link out">
  1407.         <div class="form-row">
  1408.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1409.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1410.         </div>
  1411.         <div class="form-row node-input-link-row"></div>
  1412.     </script>
  1413.      
  1414.      
  1415.     <style>
  1416.     #node-input-link-container {
  1417.         position: relative;
  1418.     }
  1419.     #node-input-link-container li {
  1420.         padding: 2px 5px;
  1421.         background: none;
  1422.         font-size: 0.8em;
  1423.         margin:0;
  1424.         white-space: nowrap;
  1425.     }
  1426.     #node-input-link-container li label {
  1427.         margin-bottom: 0;
  1428.         width: 100%;
  1429.     }
  1430.     #node-input-link-container li label input {
  1431.         vertical-align: top;
  1432.         width:15px;
  1433.         margin-right: 10px;
  1434.     }
  1435.     #node-input-link-container li:hover,
  1436.     #node-input-link-container li:hover .node-input-target-node-sublabel {
  1437.         background: #f0f0f0;
  1438.     }
  1439.     .node-input-link-node-sublabel {
  1440.         position:absolute;
  1441.         right: 0px;
  1442.         padding-right: 10px;
  1443.         padding-left: 10px;
  1444.         font-size: 0.8em;
  1445.     }
  1446.     </style>
  1447.      
  1448.     <script type="text/javascript">
  1449.     (function() {
  1450.      
  1451.         function sortNodeList(nodeList,sortOn,sortOnSecond) {
  1452.             var currentSort = nodeList.data('currentSort');
  1453.             var currentSortOrder = nodeList.data('currentSortOrder');
  1454.      
  1455.             if (!currentSort) {
  1456.                 currentSort = sortOn;
  1457.                 currentSortOrder = 'a';
  1458.             } else {
  1459.                 if (currentSort === sortOn) {
  1460.                     currentSortOrder = (currentSortOrder === 'a'?'d':'a');
  1461.                 } else {
  1462.                     currentSortOrder = 'a';
  1463.                 }
  1464.                 currentSort = sortOn;
  1465.             }
  1466.             nodeList.data('currentSort',currentSort);
  1467.             nodeList.data('currentSortOrder',currentSortOrder);
  1468.      
  1469.             $("#node-input-link-container-div .fa").hide();
  1470.             $(".node-input-link-sort-"+currentSort+"-"+currentSortOrder).show();
  1471.      
  1472.      
  1473.             var items = nodeList.find("li").get();
  1474.             items.sort(function(a,b) {
  1475.                 var labelA = $(a).find(".node-input-link-node-"+currentSort).text().toLowerCase();
  1476.                 var labelB = $(b).find(".node-input-link-node-"+currentSort).text().toLowerCase();
  1477.                 if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
  1478.                 if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
  1479.      
  1480.                 if (sortOnSecond) {
  1481.                     labelA = $(a).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
  1482.                     labelB = $(b).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
  1483.                     if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
  1484.                     if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
  1485.                 }
  1486.                 return 0;
  1487.             });
  1488.             $.each(items, function(i, li){
  1489.                 nodeList.append(li);
  1490.             });
  1491.         }
  1492.         function onEditPrepare(node,targetType) {
  1493.             if (!node.links) {
  1494.                 node.links = [];
  1495.             }
  1496.             node.oldLinks = [];
  1497.      
  1498.             $('<div id="node-input-link-container-div" style="min-height: 100px;position: relative;   box-sizing: border-box; border-radius: 2px; height: 180px;  border: 1px solid #ccc;overflow:hidden; ">'+
  1499.                 '    <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
  1500.                 '        <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]link.label.sortByLabel"><span data-i18n="link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
  1501.                 '        <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="link.label.type">flow</span></a></div>'+
  1502.                 '    </div>'+
  1503.                 '    <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
  1504.                 '        <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
  1505.                 '    </div>'+
  1506.                 '</div>').appendTo('.node-input-link-row');
  1507.      
  1508.             var nodeList = $("#node-input-link-container");
  1509.             var candidateNodes = RED.nodes.filterNodes({type:targetType});
  1510.             var inSubflow = (RED.nodes.subflow(node.z) != null);
  1511.             candidateNodes.forEach(function(n) {
  1512.                 if (inSubflow) {
  1513.                     if (n.z !== node.z) {
  1514.                         return;
  1515.                     }
  1516.                 } else {
  1517.                     if (RED.nodes.subflow(n.z)!=null) {
  1518.                         return;
  1519.                     }
  1520.                 }
  1521.                 var isChecked = false;
  1522.      
  1523.                 isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
  1524.      
  1525.                 if (isChecked) {
  1526.                     node.oldLinks.push(n.id);
  1527.                 }
  1528.      
  1529.                 var container = $('<li/>',{class:"node-input-link-node"});
  1530.                 var row = $('<label/>',{for:"node-input-link-node-"+n.id}).appendTo(container);
  1531.                 $('<input>',{type:"checkbox",class:"node-input-link-node-checkbox",id:"node-input-link-node-"+n.id})
  1532.                     .data('node-id',n.id)
  1533.                     .prop('checked', isChecked)
  1534.                     .appendTo(row);
  1535.                 container.on('mouseover',function(e) {
  1536.                     n.highlighted = true;
  1537.                     n.dirty = true;
  1538.                     RED.view.redraw();
  1539.                 });
  1540.                 container.on('mouseout',function(e) {
  1541.                     n.highlighted = false;
  1542.                     n.dirty = true;
  1543.                     RED.view.redraw();
  1544.                 });
  1545.                 var labelSpan = $('<span>');
  1546.                 var label = n.name||n.id;
  1547.                 var sublabel;
  1548.                 var tab = RED.nodes.workspace(n.z);
  1549.                 if (tab) {
  1550.                     sublabel = tab.label||tab.id;
  1551.                 } else {
  1552.                     tab = RED.nodes.subflow(n.z);
  1553.                     sublabel = "subflow : "+tab.name;
  1554.                 }
  1555.                 $('<span>',{class:"node-input-link-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
  1556.                 if (sublabel) {
  1557.                     $('<span>',{class:"node-input-link-node-sublabel"}).text(sublabel).appendTo(row);
  1558.                 }
  1559.                 container.appendTo(nodeList);
  1560.             });
  1561.      
  1562.             sortNodeList(nodeList,'sublabel','label');
  1563.      
  1564.             $("#node-input-link-sort-label").click(function(e) {
  1565.                 e.preventDefault();
  1566.                 sortNodeList(nodeList,'label');
  1567.             });
  1568.      
  1569.             $("#node-input-link-sort-type").click(function(e) {
  1570.                 e.preventDefault();
  1571.                 sortNodeList(nodeList,'sublabel');
  1572.             });
  1573.         }
  1574.      
  1575.         function resizeNodeList() {
  1576.             var rows = $("#dialog-form>div:not(.node-input-link-row)");
  1577.             var height = $("#dialog-form").height();
  1578.             for (var i=0;i<rows.size();i++) {
  1579.                 height -= $(rows[i]).outerHeight(true);
  1580.             }
  1581.             var editorRow = $("#dialog-form>div.node-input-link-row");
  1582.             height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  1583.             $("#node-input-link-container-div").css("height",height+"px");
  1584.         }
  1585.      
  1586.         function onEditSave(node) {
  1587.             node.links = [];
  1588.             $(".node-input-link-node-checkbox").each(function(n) {
  1589.                 if ($(this).prop("checked")) {
  1590.                     node.links.push($(this).data('node-id'));
  1591.                 }
  1592.             })
  1593.             node.oldLinks.sort();
  1594.             node.links.sort();
  1595.             var nodeMap = {};
  1596.             var length = Math.max(node.oldLinks.length,node.links.length);
  1597.             for (var i=0;i<length;i++) {
  1598.                 if (i<node.oldLinks.length) {
  1599.                     nodeMap[node.oldLinks[i]] = nodeMap[node.oldLinks[i]]||{};
  1600.                     nodeMap[node.oldLinks[i]].old = true;
  1601.                 }
  1602.                 if (i<node.links.length) {
  1603.                     nodeMap[node.links[i]] = nodeMap[node.links[i]]||{};
  1604.                     nodeMap[node.links[i]].new = true;
  1605.                 }
  1606.             }
  1607.             var n;
  1608.             for (var id in nodeMap) {
  1609.                 if (nodeMap.hasOwnProperty(id)) {
  1610.                     n = RED.nodes.node(id);
  1611.                     if (n) {
  1612.                         if (nodeMap[id].old && !nodeMap[id].new) {
  1613.                             // Removed id
  1614.                             i = n.links.indexOf(node.id);
  1615.                             if (i > -1) {
  1616.                                 n.links.splice(i,1);
  1617.                             }
  1618.                         } else if (!nodeMap[id].old && nodeMap[id].new){
  1619.                             // Added id
  1620.                             i = n.links.indexOf(id);
  1621.                             if (i === -1) {
  1622.                                 n.links.push(node.id);
  1623.                             }
  1624.                         }
  1625.                     }
  1626.                 }
  1627.             }
  1628.         }
  1629.      
  1630.         function onAdd() {
  1631.             for (var i=0;i<this.links.length;i++) {
  1632.                 var n = RED.nodes.node(this.links[i]);
  1633.                 if (n.links.indexOf(this.id) === -1) {
  1634.                     n.links.push(this.id);
  1635.                 }
  1636.             }
  1637.         }
  1638.      
  1639.         RED.nodes.registerType('link in',{
  1640.             category: 'input',
  1641.             color:"#ddd",//"#87D8CF",
  1642.             defaults: {
  1643.                 name: {value:""},
  1644.                 links: { value: [] }
  1645.             },
  1646.             inputs:0,
  1647.             outputs:1,
  1648.             icon: "link-out.png",
  1649.             label: function() {
  1650.                 return this.name||this._("link.linkIn");
  1651.             },
  1652.             labelStyle: function() {
  1653.                 return this.name?"node_label_italic":"";
  1654.             },
  1655.             oneditprepare: function() {
  1656.                 onEditPrepare(this,"link out");
  1657.             },
  1658.             oneditsave: function() {
  1659.                 onEditSave(this);
  1660.             },
  1661.             onadd: onAdd,
  1662.             oneditresize: resizeNodeList
  1663.         });
  1664.      
  1665.         RED.nodes.registerType('link out',{
  1666.             category: 'output',
  1667.             color:"#ddd",//"#87D8CF",
  1668.             defaults: {
  1669.                 name: {value:""},
  1670.                 links: { value: []}
  1671.             },
  1672.             align:"right",
  1673.             inputs:1,
  1674.             outputs:0,
  1675.             icon: "link-out.png",
  1676.             label: function() {
  1677.                 return this.name||this._("link.linkOut");
  1678.             },
  1679.             labelStyle: function() {
  1680.                 return this.name?"node_label_italic":"";
  1681.             },
  1682.             oneditprepare: function() {
  1683.                 onEditPrepare(this,"link in");
  1684.             },
  1685.             oneditsave: function() {
  1686.                 onEditSave(this);
  1687.             },
  1688.             onadd: onAdd,
  1689.             oneditresize: resizeNodeList
  1690.         });
  1691.     })();
  1692.     </script>
  1693.     <script type="text/x-red" data-help-name="link in">
  1694.         <p>Create virtual wires between flows.</p>
  1695.         <p>The node can be connected to any <b>link out</b> node that exists on any tab.
  1696.            Once connected, they behave as if they were wired together.</p>
  1697.         <p>The wires between link nodes are only displayed when a link node is selected.
  1698.            If there are any wires to other tabs, a virtual node is show that can be clicked
  1699.            on to jump to the appropriate tab.</p>
  1700.         <p>Links cannot be created going into, or out of, a subflow.</p>
  1701.     </script><script type="text/x-red" data-help-name="link out">
  1702.         <p>Create virtual wires between flows.</p>
  1703.         <p>The node can be connected to any <b>link in</b> node that exists on any tab.
  1704.            Once connected, they behave as if they were wired together.</p>
  1705.         <p>The wires between link nodes are only displayed when a link node is selected.
  1706.            If there are any wires to other tabs, a virtual node is show that can be clicked
  1707.            on to jump to the appropriate tab.</p>
  1708.         <p>Links cannot be created going into, or out of, a subflow.</p>
  1709.     </script><!--
  1710.       Copyright JS Foundation and other contributors, http://js.foundation
  1711.      
  1712.       Licensed under the Apache License, Version 2.0 (the "License");
  1713.       you may not use this file except in compliance with the License.
  1714.       You may obtain a copy of the License at
  1715.      
  1716.       http://www.apache.org/licenses/LICENSE-2.0
  1717.      
  1718.       Unless required by applicable law or agreed to in writing, software
  1719.       distributed under the License is distributed on an "AS IS" BASIS,
  1720.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1721.       See the License for the specific language governing permissions and
  1722.       limitations under the License.
  1723.     -->
  1724.      
  1725.     <script type="text/x-red" data-template-name="exec">
  1726.         <div class="form-row">
  1727.             <label for="node-input-command"><i class="fa fa-file"></i> <span data-i18n="exec.label.command"></span></label>
  1728.             <input type="text" id="node-input-command" data-i18n="[placeholder]exec.label.command">
  1729.         </div>
  1730.         <div class="form-row">
  1731.             <label><i class="fa fa-plus"></i> <span data-i18n="exec.label.append"></span></label>
  1732.             <input type="checkbox" id="node-input-addpay" style="display: inline-block; width: auto; vertical-align: top;">
  1733.             &nbsp;msg.payload
  1734.         </div>
  1735.         <div class="form-row">
  1736.             <label for="node-input-append"> </label>
  1737.             <input type="text" id="node-input-append" data-i18n="[placeholder]exec.placeholder.extraparams">
  1738.         </div>
  1739.         <div class="form-row">
  1740.             <label>&nbsp;</label>
  1741.             <input type="checkbox" id="node-input-useSpawn" placeholder="spawn" style="display: inline-block; width: auto; vertical-align: top;">
  1742.             <label for="node-input-useSpawn" style="width:70%;"><span data-i18n="exec.spawn"></span></label>
  1743.         </div>
  1744.         <div class="form-row">
  1745.             <label for="node-input-timer"><i class="fa fa-clock-o"></i> <span data-i18n="exec.label.timeout"></span></label>
  1746.             <input type="text" id="node-input-timer" style="width:65px;" data-i18n="[placeholder]exec.label.timeoutplace"> seconds
  1747.         </div>
  1748.         <div class="form-row">
  1749.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1750.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1751.         </div>
  1752.     </script>
  1753.      
  1754.      
  1755.      
  1756.     <script type="text/javascript">
  1757.         RED.nodes.registerType('exec',{
  1758.             category: 'advanced-function',
  1759.             color:"darksalmon",
  1760.             defaults: {
  1761.                 command: {value:"",required:true},
  1762.                 addpay: {value:true},
  1763.                 append: {value:""},
  1764.                 useSpawn: {value:""},
  1765.                 timer: {value:""},
  1766.                 name: {value:""}
  1767.             },
  1768.             inputs:1,
  1769.             outputs:3,
  1770.             icon: "arrow-in.png",
  1771.             align: "right",
  1772.             label: function() {
  1773.                 return this.name||this.command;
  1774.             },
  1775.             labelStyle: function() {
  1776.                 return this.name?"node_label_italic":"";
  1777.             }
  1778.         });
  1779.     </script>
  1780.     <script type="text/x-red" data-help-name="exec">
  1781.         <p>Calls out to a system command.<br/></p>
  1782.         <p>Provides 3 outputs: stdout, stderr, and return code.</p>
  1783.         <p>By default uses the <code>exec</code> system call which calls the command, then gets a callback
  1784.         on completion, returning the complete result in one message, along with any errors.</p>
  1785.         <p>Optionally can use <code>spawn</code> instead, which returns the output from stdout and stderr
  1786.         as the command runs (usually one line at a time). On completion it then returns a return code
  1787.         (on the 3rd output).</p>
  1788.         <p>The optional append gets added to the command after <code>msg.payload</code> - so you can do
  1789.         things like pipe the result to another command.</p>
  1790.         <p>Commands or parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p>
  1791.         <p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p>
  1792.         <p>The blue status icon will be visible while the node is active.</p>
  1793.         <p>If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
  1794.     </script><!--
  1795.       Copyright JS Foundation and other contributors, http://js.foundation
  1796.      
  1797.       Licensed under the Apache License, Version 2.0 (the "License");
  1798.       you may not use this file except in compliance with the License.
  1799.       You may obtain a copy of the License at
  1800.      
  1801.       http://www.apache.org/licenses/LICENSE-2.0
  1802.      
  1803.       Unless required by applicable law or agreed to in writing, software
  1804.       distributed under the License is distributed on an "AS IS" BASIS,
  1805.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1806.       See the License for the specific language governing permissions and
  1807.       limitations under the License.
  1808.     -->
  1809.      
  1810.     <script type="text/x-red" data-template-name="function">
  1811.         <div class="form-row">
  1812.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1813.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1814.         </div>
  1815.         <div class="form-row" style="margin-bottom: 0px;">
  1816.             <label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="function.label.function"></span></label>
  1817.             <input type="hidden" id="node-input-func" autofocus="autofocus">
  1818.             <input type="hidden" id="node-input-noerr">
  1819.         </div>
  1820.         <div class="form-row node-text-editor-row">
  1821.             <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
  1822.         </div>
  1823.         <div class="form-row">
  1824.             <label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
  1825.             <input id="node-input-outputs" style="width: 60px;" value="1">
  1826.         </div>
  1827.         <div class="form-tips"><span data-i18n="function.tip"></span></div>
  1828.     </script>
  1829.      
  1830.      
  1831.      
  1832.     <script type="text/javascript">
  1833.         RED.nodes.registerType('function',{
  1834.             color:"#fdd0a2",
  1835.             category: 'function',
  1836.             defaults: {
  1837.                 name: {value:""},
  1838.                 func: {value:"\nreturn msg;"},
  1839.                 outputs: {value:1},
  1840.                 noerr: {value:0,required:true,validate:function(v){ return ((!v) || (v === 0)) ? true : false; }}
  1841.             },
  1842.             inputs:1,
  1843.             outputs:1,
  1844.             icon: "function.png",
  1845.             label: function() {
  1846.                 return this.name;
  1847.             },
  1848.             oneditprepare: function() {
  1849.                 var that = this;
  1850.                 $( "#node-input-outputs" ).spinner({
  1851.                     min:1
  1852.                 });
  1853.      
  1854.                 this.editor = RED.editor.createEditor({
  1855.                     id: 'node-input-func-editor',
  1856.                     mode: 'ace/mode/javascript',
  1857.                     value: $("#node-input-func").val(),
  1858.                     globals: {
  1859.                         msg:true,
  1860.                         context:true,
  1861.                         RED: true,
  1862.                         util: true,
  1863.                         flow: true,
  1864.                         global: true,
  1865.                         console: true,
  1866.                         Buffer: true,
  1867.                         setTimeout: true,
  1868.                         clearTimeout: true,
  1869.                         setInterval: true,
  1870.                         clearInterval: true
  1871.                     }
  1872.                 });
  1873.      
  1874.                 RED.library.create({
  1875.                     url:"functions", // where to get the data from
  1876.                     type:"function", // the type of object the library is for
  1877.                     editor:this.editor, // the field name the main text body goes to
  1878.                     mode:"ace/mode/javascript",
  1879.                     fields:['name','outputs']
  1880.                 });
  1881.                 this.editor.focus();
  1882.             },
  1883.             oneditsave: function() {
  1884.                 var annot = this.editor.getSession().getAnnotations();
  1885.                 this.noerr = 0;
  1886.                 $("#node-input-noerr").val(0);
  1887.                 for (var k=0; k < annot.length; k++) {
  1888.                     //console.log(annot[k].type,":",annot[k].text, "on line", annot[k].row);
  1889.                     if (annot[k].type === "error") {
  1890.                         $("#node-input-noerr").val(annot.length);
  1891.                         this.noerr = annot.length;
  1892.                     }
  1893.                 }
  1894.                 $("#node-input-func").val(this.editor.getValue());
  1895.                 delete this.editor;
  1896.             },
  1897.             oneditresize: function(size) {
  1898.                 var rows = $("#dialog-form>div:not(.node-text-editor-row)");
  1899.                 var height = $("#dialog-form").height();
  1900.                 for (var i=0;i<rows.size();i++) {
  1901.                     height -= $(rows[i]).outerHeight(true);
  1902.                 }
  1903.                 var editorRow = $("#dialog-form>div.node-text-editor-row");
  1904.                 height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  1905.                 $(".node-text-editor").css("height",height+"px");
  1906.                 this.editor.resize();
  1907.             }
  1908.         });
  1909.     </script>
  1910.     <script type="text/x-red" data-help-name="function">
  1911.         <p>A function block where you can write code to do more interesting things.</p>
  1912.         <p>The message is passed in as a JavaScript object called <code>msg</code>.</p>
  1913.         <p>By convention it will have a <code>msg.payload</code> property containing
  1914.            the body of the message.</p>
  1915.         <h4>Logging and Error Handling</h4>
  1916.         <p>To log any information, or report an error, the following functions are available:</p>
  1917.             <ul>
  1918.                 <li><code>node.log("Log")</code></li>
  1919.                 <li><code>node.warn("Warning")</code></li>
  1920.                 <li><code>node.error("Error")</code></li>
  1921.             </ul>
  1922.         </p>
  1923.         <p>The Catch node can also be used to handle errors. To invoke a Catch node,
  1924.         pass <code>msg</code> as a second argument to <code>node.error</code>:</p>
  1925.         <pre>node.error("Error",msg)</pre>
  1926.         <h4>Sending messages</h4>
  1927.         <p>The function can either return the messages it wants to pass on to the next nodes
  1928.         in the flow, or can call <code>node.send(messages)</code>.</p>
  1929.         <p>It can return/send:</p>
  1930.         <ul>
  1931.           <li>a single message object - passed to nodes connected to the first output</li>
  1932.           <li>an array of message objects - passed to nodes connected to the corresponding outputs</li>
  1933.         </ul>
  1934.         <p>If any element of the array is itself an array of messages, multiple
  1935.               messages are sent to the corresponding output.</p>
  1936.         <p>If null is returned, either by itself or as an element of the array, no
  1937.               message is passed on.</p>
  1938.         <p>See the <a target="_blank" href="http://nodered.org/docs/writing-functions.html">online documentation</a> for more help.</p>
  1939.      
  1940.     </script><!--
  1941.       Copyright JS Foundation and other contributors, http://js.foundation
  1942.      
  1943.       Licensed under the Apache License, Version 2.0 (the "License");
  1944.       you may not use this file except in compliance with the License.
  1945.       You may obtain a copy of the License at
  1946.      
  1947.       http://www.apache.org/licenses/LICENSE-2.0
  1948.      
  1949.       Unless required by applicable law or agreed to in writing, software
  1950.       distributed under the License is distributed on an "AS IS" BASIS,
  1951.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1952.       See the License for the specific language governing permissions and
  1953.       limitations under the License.
  1954.     -->
  1955.      
  1956.     <script type="text/x-red" data-template-name="template">
  1957.         <div class="form-row">
  1958.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  1959.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  1960.         </div>
  1961.         <div class="form-row">
  1962.             <label for="node-input-field"><i class="fa fa-edit"></i> <span data-i18n="template.label.property"></span></label>
  1963.             <input type="text" id="node-input-field" placeholder="payload" style="width:250px;">
  1964.             <input type="hidden" id="node-input-fieldType">
  1965.         </div>
  1966.         <div class="form-row" style="position: relative; margin-bottom: 0px;">
  1967.             <label for="node-input-template"><i class="fa fa-file-code-o"></i> <span data-i18n="template.label.template"></span></label>
  1968.             <input type="hidden" id="node-input-template" autofocus="autofocus">
  1969.             <div style="position: absolute; right:0;display:inline-block; text-align: right; font-size: 0.8em;">
  1970.                 <span data-i18n="template.label.format"></span>:
  1971.                 <select id="node-input-format" style="width:110px; font-size: 10px !important;  height: 24px; padding:0;">
  1972.                     <option value="handlebars">mustache</option>
  1973.                     <option value="html">HTML</option>
  1974.                     <option value="json">JSON</option>
  1975.                     <option value="javascript">Javascript</option>
  1976.                     <option value="css">CSS</option>
  1977.                     <option value="markdown">Markdown</option>
  1978.                     <option value="yaml">YAML</option>
  1979.                     <option value="text">none</option>
  1980.                 </select>
  1981.             </div>
  1982.         </div>
  1983.         <div class="form-row node-text-editor-row">
  1984.             <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-template-editor" ></div>
  1985.         </div>
  1986.         <div class="form-row">
  1987.             <label for="node-input-syntax"><i class="fa fa-code"></i> <span data-i18n="template.label.syntax"></span></label>
  1988.             <select id="node-input-syntax" style="width:180px;">
  1989.                 <option value="mustache" data-i18n="template.label.mustache"></option>
  1990.                 <option value="plain" data-i18n="template.label.plain"></option>
  1991.             </select>
  1992.         </div>
  1993.     </script>
  1994.      
  1995.      
  1996.      
  1997.     <script type="text/javascript">
  1998.         RED.nodes.registerType('template',{
  1999.             color:"rgb(243, 181, 103)",
  2000.             category: 'function',
  2001.             defaults: {
  2002.                 name: {value:""},
  2003.                 field: {value:"payload", validate: RED.validators.typedInput("fieldType")},
  2004.                 fieldType: {value:"msg"},
  2005.                 format: {value:"handlebars"},
  2006.                 syntax: {value:"mustache"},
  2007.                 template: {value:"This is the payload: {{payload}} !"},
  2008.             },
  2009.             inputs:1,
  2010.             outputs:1,
  2011.             icon: "template.png",
  2012.             label: function() {
  2013.                 return this.name;
  2014.             },
  2015.             oneditprepare: function() {
  2016.                 var that = this;
  2017.                 if (!this.fieldType) {
  2018.                     this.fieldType = 'msg';
  2019.                 }
  2020.                 if (!this.syntax) {
  2021.                     this.syntax = 'mustache';
  2022.                     $("#node-input-syntax").val(this.syntax);
  2023.                 }
  2024.                 $("#node-input-field").typedInput({
  2025.                     default: 'msg',
  2026.                     types: ['msg','flow','global'],
  2027.                     typeField: $("#node-input-fieldType")
  2028.                 });
  2029.      
  2030.                 this.editor = RED.editor.createEditor({
  2031.                     id: 'node-input-template-editor',
  2032.                     mode: 'ace/mode/html',
  2033.                     value: $("#node-input-template").val()
  2034.                 });
  2035.                 RED.library.create({
  2036.                     url:"functions", // where to get the data from
  2037.                     type:"function", // the type of object the library is for
  2038.                     editor:that.editor, // the field name the main text body goes to
  2039.                     fields:['name','outputs']
  2040.                 });
  2041.                 this.editor.focus();
  2042.      
  2043.                 $("#node-input-format").change(function() {
  2044.                     var mod = "ace/mode/"+$("#node-input-format").val();
  2045.                     that.editor.getSession().setMode({
  2046.                        path: mod,
  2047.                        v: Date.now()
  2048.                     })
  2049.                 });
  2050.             },
  2051.             oneditsave: function() {
  2052.                 $("#node-input-template").val(this.editor.getValue())
  2053.                 delete this.editor;
  2054.             },
  2055.             oneditresize: function(size) {
  2056.                 var rows = $("#dialog-form>div:not(.node-text-editor-row)");
  2057.                 var height = $("#dialog-form").height();
  2058.                 for (var i=0;i<rows.size();i++) {
  2059.                     height -= $(rows[i]).outerHeight(true);
  2060.                 }
  2061.                 var editorRow = $("#dialog-form>div.node-text-editor-row");
  2062.                 height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  2063.                 $(".node-text-editor").css("height",height+"px");
  2064.                 this.editor.resize();
  2065.             }
  2066.         });
  2067.     </script>
  2068.     <script type="text/x-red" data-help-name="template">
  2069.         <p>Sets a property based on the provided template.</p>
  2070.         <p>By default this uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>
  2071.         format, but this can be switched off if required.</p>
  2072.         <p>For example, when a template of:
  2073.         <pre>Hello {{name}}. Today is {{date}}</pre>
  2074.         <p>receives a message containing:
  2075.         <pre>{
  2076.       name: "Fred",
  2077.       date: "Monday"
  2078.       payload: ...
  2079.     }</pre>
  2080.         <p>The resulting property will be:
  2081.         <pre>Hello Fred. Today is Monday</pre>
  2082.         <p>It is possible to use property from flow context or global context. Just use <code>{{flow.name}}</code> or <code>{{global.name}}</code>.
  2083.         <p>By default, mustache will escape any HTML entities in the values it substitutes.
  2084.            To prevent this, use <code>{{{triple}}}</code> braces.
  2085.     </script><!--
  2086.       Copyright JS Foundation and other contributors, http://js.foundation
  2087.      
  2088.       Licensed under the Apache License, Version 2.0 (the "License");
  2089.       you may not use this file except in compliance with the License.
  2090.       You may obtain a copy of the License at
  2091.      
  2092.       http://www.apache.org/licenses/LICENSE-2.0
  2093.      
  2094.       Unless required by applicable law or agreed to in writing, software
  2095.       distributed under the License is distributed on an "AS IS" BASIS,
  2096.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2097.       See the License for the specific language governing permissions and
  2098.       limitations under the License.
  2099.     -->
  2100.      
  2101.     <script type="text/x-red" data-template-name="delay">
  2102.      
  2103.         <div class="form-row">
  2104.             <label for="node-input-pauseType"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label>
  2105.             <select id="node-input-pauseType" style="width:270px !important">
  2106.                 <option value="delay" data-i18n="delay.delaymsg"></option>
  2107.                 <option value="random" data-i18n="delay.randomdelay"></option>
  2108.                 <option value="rate" data-i18n="delay.limitrate"></option>
  2109.                 <option value="queue" data-i18n="delay.fairqueue"></option>
  2110.                 <option value="timed" data-i18n="delay.timedqueue"></option>
  2111.             </select>
  2112.         </div>
  2113.         <div id="delay-details" class="form-row">
  2114.             <label for="node-input-timeout"><i class="fa fa-clock-o"></i> <span data-i18n="delay.for"></span></label>
  2115.             <input type="text" id="node-input-timeout" placeholder="Time" style="text-align:end; width:50px !important">
  2116.             <select id="node-input-timeoutUnits" style="width:200px !important">
  2117.               <option value="milliseconds" data-i18n="delay.milisecs"></option>
  2118.               <option value="seconds" data-i18n="delay.secs"></option>
  2119.               <option value="minutes" data-i18n="delay.mins"></option>
  2120.               <option value="hours" data-i18n="delay.hours"></option>
  2121.               <option value="days" data-i18n="delay.days"></option>
  2122.             </select>
  2123.         </div>
  2124.      
  2125.         <div id="rate-details" class="form-row">
  2126.             <label for="node-input-rate"><i class="fa fa-clock-o"></i> <span data-i18n="delay.rate"></span></label>
  2127.             <input type="text" id="node-input-rate" placeholder="1" style="text-align:end; width:30px !important">
  2128.             <label for="node-input-rateUnits"><span data-i18n="delay.msgper"></span></label>
  2129.             <input type="text" id="node-input-nbRateUnits" placeholder="1" style="text-align:end; width:30px !important">
  2130.             <select id="node-input-rateUnits" style="width:110px !important">
  2131.               <option value="second" data-i18n="delay.label.units.second.singular"></option>
  2132.               <option value="minute" data-i18n="delay.label.units.minute.singular"></option>
  2133.               <option value="hour" data-i18n="delay.label.units.hour.singular"></option>
  2134.               <option value="day" data-i18n="delay.label.units.day.singular"></option>
  2135.             </select>
  2136.             <br/>
  2137.             <div id="node-input-dr"><input style="margin: 20px 0 20px 100px; width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop"><span data-i18n="delay.dropmsg"></span></label></div>
  2138.         </div>
  2139.      
  2140.      
  2141.         <div id="random-details" class="form-row">
  2142.             <label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> <span data-i18n="delay.between"></span></label>
  2143.             <input type="text" id="node-input-randomFirst" placeholder="" style="text-align:end; width:30px !important">
  2144.             &nbsp;&nbsp;&amp;&nbsp;&nbsp;
  2145.             <input type="text" id="node-input-randomLast" placeholder="" style="text-align:end; width:30px !important">
  2146.             <select id="node-input-randomUnits" style="width:140px !important">
  2147.               <option value="milliseconds" data-i18n="delay.milisecs"></option>
  2148.               <option value="seconds" data-i18n="delay.secs"></option>
  2149.               <option value="minutes" data-i18n="delay.mins"></option>
  2150.               <option value="hours" data-i18n="delay.hours"></option>
  2151.               <option value="days" data-i18n="delay.days"></option>
  2152.             </select>
  2153.         </div>
  2154.      
  2155.         <div class="form-row">
  2156.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  2157.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  2158.         </div>
  2159.      
  2160.     </script>
  2161.      
  2162.      
  2163.      
  2164.     <script type="text/javascript">
  2165.         RED.nodes.registerType('delay',{
  2166.             category: 'function',
  2167.             color:"#E6E0F8",
  2168.             defaults: {
  2169.                 name: {value:""},
  2170.                 pauseType: {value:"delay", required:true},
  2171.                 timeout: {value:"5", required:true, validate:RED.validators.number()},
  2172.                 timeoutUnits: {value:"seconds"},
  2173.                 rate: {value:"1", required:true, validate:RED.validators.number()},
  2174.                 nbRateUnits: {value:"1", required:false, validate:RED.validators.regex(/\d+|/)},
  2175.                 rateUnits: {value: "second"},
  2176.                 randomFirst: {value:"1", required:true, validate:RED.validators.number()},
  2177.                 randomLast: {value:"5", required:true, validate:RED.validators.number()},
  2178.                 randomUnits: {value: "seconds"},
  2179.                 drop: {value:false}
  2180.             },
  2181.             inputs:1,
  2182.             outputs:1,
  2183.             icon: "timer.png",
  2184.             label: function() {
  2185.                 if (this.pauseType == "delay") {
  2186.                   var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
  2187.                   if (this.timeoutUnits == "milliseconds") { units = "ms"; }
  2188.                   return this.name||this._("delay.label.delay")+" "+this.timeout+" "+units;
  2189.                 } else if (this.pauseType == "rate") {
  2190.                   var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s";
  2191.                   return this.name||this._("delay.label.limit")+" "+this.rate+" msg/"+units;
  2192.                 } else if (this.pauseType == "random") {
  2193.                   return this.name || this._("delay.label.random");
  2194.                 }
  2195.                 else if (this.pauseType == "timed") {
  2196.                     var units = '';
  2197.                     if (this.nbRateUnits > 1) {
  2198.                         units = this.nbRateUnits + ' ' + this._("delay.label.units." + this.rateUnits + ".plural");
  2199.                     } else {
  2200.                         units = this._("delay.label.units." + this.rateUnits + ".singular");
  2201.                     }
  2202.                     return this.name || this.rate + " " + this._("delay.label.timed") + ' ' + units;
  2203.                 }
  2204.                 else {
  2205.                     var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s";
  2206.                     return this.name || this._("delay.label.queue")+" "+this.rate+" msg/"+units;
  2207.                 }
  2208.             },
  2209.             labelStyle: function() {
  2210.                 return this.name?"node_label_italic":"";
  2211.             },
  2212.             oneditprepare: function() {
  2213.               var node = this;
  2214.               $( "#node-input-timeout" ).spinner({min:1});
  2215.               $( "#node-input-rate" ).spinner({min:1});
  2216.               $( "#node-input-nbRateUnits" ).spinner({min:1});
  2217.      
  2218.               $( "#node-input-randomFirst" ).spinner({min:0});
  2219.               $( "#node-input-randomLast" ).spinner({min:1});
  2220.      
  2221.               $('.ui-spinner-button').click(function() {
  2222.                   $(this).siblings('input').change();
  2223.               });
  2224.      
  2225.               $( "#node-input-nbRateUnits" ).on('change keyup', function() {
  2226.                 var $this = $(this);
  2227.                 var val = parseInt($this.val());
  2228.                 var type = "singular";
  2229.                 if(val > 1) {
  2230.                     type = "plural";
  2231.                 }
  2232.                 if($this.attr("data-type") == type) {
  2233.                     return;
  2234.                 }
  2235.                 $this.attr("data-type", type);
  2236.                 $("#node-input-rateUnits option").each(function () {
  2237.                     var $option = $(this);
  2238.                     var key = "delay.label.units." + $option.val() + "." + type;
  2239.                     $option.attr('data-i18n', 'node-red:' + key);
  2240.                     $option.html(node._(key));
  2241.                 })
  2242.               });
  2243.      
  2244.      
  2245.      
  2246.               if (this.pauseType == "delay") {
  2247.                 $("#delay-details").show();
  2248.                 $("#rate-details").hide();
  2249.                 $("#random-details").hide();
  2250.                 $("#node-input-dr").hide();
  2251.               } else if (this.pauseType == "rate") {
  2252.                 $("#delay-details").hide();
  2253.                 $("#rate-details").show();
  2254.                 $("#random-details").hide();
  2255.                 $("#node-input-dr").show();
  2256.               } else if (this.pauseType == "random") {
  2257.                 $("#delay-details").hide();
  2258.                 $("#rate-details").hide();
  2259.                 $("#random-details").show();
  2260.                 $("#node-input-dr").hide();
  2261.               } else if (this.pauseType == "queue") {
  2262.                 $("#delay-details").hide();
  2263.                 $("#rate-details").show();
  2264.                 $("#random-details").hide();
  2265.                 $("#node-input-dr").hide();
  2266.               } else if (this.pauseType == "timed") {
  2267.                 $("#delay-details").hide();
  2268.                 $("#rate-details").show();
  2269.                 $("#random-details").hide();
  2270.                 $("#node-input-dr").hide();
  2271.               }
  2272.      
  2273.               if (!this.timeoutUnits) {
  2274.                 $("#node-input-timeoutUnits option").filter(function() {
  2275.                   return $(this).val() == 'seconds';
  2276.                 }).attr('selected', true);
  2277.               }
  2278.      
  2279.               if (!this.randomUnits) {
  2280.                 $("#node-input-randomUnits option").filter(function() {
  2281.                    return $(this).val() == 'seconds';
  2282.                 }).attr('selected', true);
  2283.               }
  2284.      
  2285.               $("#node-input-pauseType").on("change",function() {
  2286.                 if (this.value == "delay") {
  2287.                   $("#delay-details").show();
  2288.                   $("#rate-details").hide();
  2289.                   $("#random-details").hide();
  2290.                   $("#node-input-dr").hide();
  2291.                 } else if (this.value == "rate") {
  2292.                   $("#delay-details").hide();
  2293.                   $("#rate-details").show();
  2294.                   $("#random-details").hide();
  2295.                   $("#node-input-dr").show();
  2296.                   } else if (this.value == "random") {
  2297.                   $("#delay-details").hide();
  2298.                   $("#rate-details").hide();
  2299.                   $("#random-details").show();
  2300.                   $("#node-input-dr").hide();
  2301.                 } else if (this.value == "queue") {
  2302.                   $("#delay-details").hide();
  2303.                   $("#rate-details").show();
  2304.                   $("#random-details").hide();
  2305.                   $("#node-input-dr").hide();
  2306.                 } else if (this.value == "timed") {
  2307.                   $("#delay-details").hide();
  2308.                   $("#rate-details").show();
  2309.                   $("#random-details").hide();
  2310.                   $("#node-input-dr").hide();
  2311.                 }
  2312.               });
  2313.             }
  2314.         });
  2315.     </script>
  2316.     <script type="text/x-red" data-help-name="delay">
  2317.         <p>Introduces a delay into a flow or rate limits messages.</p>
  2318.         <p>The default delay is 5 seconds and rate limit of 1 msg/second, but both can be configured.</p>
  2319.         <p>When set to rate limit messages, they are spread across the configured time period. It can
  2320.         also be set to discard any intermediate messages that arrive.</p>
  2321.         <p>The "topic based fair queue" adds messages to a release queue tagged by their <code>msg.topic</code> property.
  2322.         At each "tick", derived from the rate, the next "topic" is released.
  2323.         Any messages arriving on the same topic before release replace those in that position in the queue.
  2324.         So each "topic" gets a turn - but the most recent value is always the one sent.</p>
  2325.         <p>The "timed release queue" adds messages to an array based on their <code>msg.topic</code> property.
  2326.         At each "tick", all the latest messages are released. Any new messages arriving before release will
  2327.         replace those of the same topic.</p>
  2328.     </script><!--
  2329.       Copyright JS Foundation and other contributors, http://js.foundation
  2330.      
  2331.       Licensed under the Apache License, Version 2.0 (the "License");
  2332.       you may not use this file except in compliance with the License.
  2333.       You may obtain a copy of the License at
  2334.      
  2335.       http://www.apache.org/licenses/LICENSE-2.0
  2336.      
  2337.       Unless required by applicable law or agreed to in writing, software
  2338.       distributed under the License is distributed on an "AS IS" BASIS,
  2339.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2340.       See the License for the specific language governing permissions and
  2341.       limitations under the License.
  2342.     -->
  2343.      
  2344.     <script type="text/x-red" data-template-name="trigger">
  2345.         <div class="form-row">
  2346.             <label data-i18n="trigger.send" for="node-input-op1"></label>
  2347.             <input type="hidden" id="node-input-op1type">
  2348.             <input style="width: 70%" type="text" id="node-input-op1">
  2349.         </div>
  2350.         <div class="form-row">
  2351.             <label data-i18n="trigger.then"></label>
  2352.             <select id="node-then-type" style="width:70%;">
  2353.                 <option value="block" data-i18n="trigger.wait-reset"></option>
  2354.                 <option value="wait" data-i18n="trigger.wait-for"></option>
  2355.             </select>
  2356.         </div>
  2357.         <div class="form-row node-type-wait">
  2358.             <label></label>
  2359.             <input type="text" id="node-input-duration" style="text-align:end; width:70px !important">
  2360.             <select id="node-input-units" style="width:140px !important">
  2361.                 <option value="ms" data-i18n="trigger.duration.ms"></option>
  2362.                 <option value="s" data-i18n="trigger.duration.s"></option>
  2363.                 <option value="min" data-i18n="trigger.duration.m"></option>
  2364.                 <option value="hr" data-i18n="trigger.duration.h"></option>
  2365.             </select>
  2366.         </div>
  2367.         <div class="form-row node-type-wait">
  2368.         <label></label>
  2369.             <input type="checkbox" id="node-input-extend" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
  2370.         </div>
  2371.         <div class="form-row node-type-wait">
  2372.             <label data-i18n="trigger.then-send"></label>
  2373.             <input type="hidden" id="node-input-op2type">
  2374.             <input style="width: 70%" type="text" id="node-input-op2">
  2375.         </div>
  2376.         <div class="form-row">
  2377.             <label data-i18n="trigger.label.reset" style="width:auto"></label>
  2378.             <div style="display:inline-block; width:70%;vertical-align:top">
  2379.             <ul>
  2380.                 <li data-i18n="trigger.label.resetMessage"></li>
  2381.                 <li><span data-i18n="trigger.label.resetPayload"></span> <input type="text" id="node-input-reset" style="width:150px" data-i18n="[placeholder]trigger.label.resetprompt"></li>
  2382.             </ul>
  2383.         </div>
  2384.         <br/>
  2385.         <div class="form-row">
  2386.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  2387.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></input>
  2388.         </div>
  2389.     </script>
  2390.      
  2391.      
  2392.      
  2393.     <script type="text/javascript">
  2394.         RED.nodes.registerType('trigger',{
  2395.             category: 'function',
  2396.             color:"#E6E0F8",
  2397.             defaults: {
  2398.                 op1: {value:"1", validate: RED.validators.typedInput("op1type")},
  2399.                 op2: {value:"0", validate: RED.validators.typedInput("op2type")},
  2400.                 op1type: {value:"val"},
  2401.                 op2type: {value:"val"},
  2402.                 duration: {value:"250",required:true,validate:RED.validators.number()},
  2403.                 extend: {value:"false"},
  2404.                 units: {value:"ms"},
  2405.                 reset: {value:""},
  2406.                 name: {value:""}
  2407.             },
  2408.             inputs:1,
  2409.             outputs:1,
  2410.             icon: "trigger.png",
  2411.             label: function() {
  2412.                 if (this.duration > 0) {
  2413.                     return this.name|| this._("trigger.label.trigger")+" "+this.duration+this.units;
  2414.                 }
  2415.                 else {
  2416.                     return this.name|| this._("trigger.label.trigger-block");
  2417.                 }
  2418.             },
  2419.             labelStyle: function() {
  2420.                 return this.name?"node_label_italic":"";
  2421.             },
  2422.             oneditprepare: function() {
  2423.                 $("#node-then-type").change(function() {
  2424.                     if ($(this).val() == "block") {
  2425.                         $(".node-type-wait").hide();
  2426.                     } else {
  2427.                         $(".node-type-wait").show();
  2428.                     }
  2429.                 });
  2430.      
  2431.                 if (this.op1type === 'val') {
  2432.                     $("#node-input-op1type").val('str');
  2433.                 }
  2434.                 if (this.op2type === 'val') {
  2435.                     $("#node-input-op2type").val('str');
  2436.                 }
  2437.      
  2438.                 var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
  2439.                 var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false}
  2440.      
  2441.                 var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false}
  2442.                 var optionLatestPayload = {value:"payl",label:this._("trigger.output.latest"),hasValue:false}
  2443.      
  2444.                 $("#node-input-op1").typedInput({
  2445.                     default: 'str',
  2446.                     typeField: $("#node-input-op1type"),
  2447.                     types:['flow','global','str','num','bool','json',
  2448.                         optionPayload,
  2449.                         optionNothing
  2450.                     ]
  2451.                 });
  2452.                 $("#node-input-op2").typedInput({
  2453.                     default: 'str',
  2454.                     typeField: $("#node-input-op2type"),
  2455.                     types:['flow','global','str','num','bool','json',
  2456.                         optionOriginalPayload,
  2457.                         optionLatestPayload,
  2458.                         optionNothing
  2459.                     ]
  2460.                 });
  2461.      
  2462.                 if (this.duration == "0") {
  2463.                     $("#node-then-type").val("block");
  2464.                 } else {
  2465.                     $("#node-then-type").val("wait");
  2466.                 }
  2467.                 $("#node-then-type").change();
  2468.      
  2469.                 if (this.extend === "true" || this.extend === true) {
  2470.                     $("#node-input-extend").prop("checked",true);
  2471.                 } else {
  2472.                     $("#node-input-extend").prop("checked",false);
  2473.                 }
  2474.      
  2475.             },
  2476.             oneditsave: function() {
  2477.                 if ($("#node-then-type").val() == "block") {
  2478.                     $("#node-input-duration").val("0");
  2479.                 }
  2480.      
  2481.             }
  2482.         });
  2483.     </script>
  2484.     <script type="text/x-red" data-help-name="trigger">
  2485.         <p>Creates two messages on the output separated by a timeout whenever <i>any</i> <code>msg</code> arrives on the input.</p>
  2486.         <p>For example, this can be used to toggle a Raspberry PI GPIO pin on and off.</p>
  2487.         <p>The two output states can be specified as can the duration of the timer.
  2488.         Either output can be set to a value, or templated from the inbound
  2489.         <code>msg</code> using mustache syntax. <pre>The payload is {{payload}}</pre></p>
  2490.         <p>If the <code>msg.payload</code> is an object then setting the output to
  2491.         <i>existing payload</i> will pass the complete payload object through.</p>
  2492.         <p>Optionally the timer can be extended by being retriggered... or not.</p>
  2493.         <p>By setting the first output to <i>nothing</i>, and selecting extend timer - a watchdog timer can be created.
  2494.         No output will happen as long as repeated inputs occur within the timeout period.</p>
  2495.         <p>Setting the timer to 0 creates an "infinite" timeout - the first output will happen but the second
  2496.         never will, and neither can the first be retriggered - so a true one shot.</p>
  2497.         <p>If a <code>msg.reset</code> property is present, or the <code>msg.payload</code>
  2498.         matches the optional reset value, any timeout currently in progress
  2499.         will be cleared and the second output will not happen.</p>
  2500.         <p>The blue status icon will be visible while the node is active.</p>
  2501.     </script><!--
  2502.       Copyright JS Foundation and other contributors, http://js.foundation
  2503.      
  2504.       Licensed under the Apache License, Version 2.0 (the "License");
  2505.       you may not use this file except in compliance with the License.
  2506.       You may obtain a copy of the License at
  2507.      
  2508.       http://www.apache.org/licenses/LICENSE-2.0
  2509.      
  2510.       Unless required by applicable law or agreed to in writing, software
  2511.       distributed under the License is distributed on an "AS IS" BASIS,
  2512.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2513.       See the License for the specific language governing permissions and
  2514.       limitations under the License.
  2515.     -->
  2516.      
  2517.     <script type="text/x-red" data-template-name="comment">
  2518.         <div class="form-row">
  2519.             <label for="node-input-name"><i class="fa fa-comment"></i> <span data-i18n="comment.label.title"></span></label>
  2520.             <input type="text" id="node-input-name">
  2521.         </div>
  2522.         <div class="form-row" style="margin-bottom: 0px;">
  2523.             <label for="node-input-info" style="width: 100% !important;"><i class="fa fa-comments"></i> <span data-i18n="comment.label.body"></span></label>
  2524.             <input type="hidden" id="node-input-info" autofocus="autofocus">
  2525.         </div>
  2526.         <div class="form-row node-text-editor-row">
  2527.             <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-info-editor"></div>
  2528.         </div>
  2529.         <div class="form-tips" data-i18n="[html]comment.tip"></div>
  2530.     </script>
  2531.      
  2532.      
  2533.      
  2534.     <script type="text/javascript">
  2535.         RED.nodes.registerType('comment',{
  2536.             category: 'function',
  2537.             color:"#ffffff",
  2538.             defaults: {
  2539.                 name: {value:""},
  2540.                 info: {value:""}
  2541.             },
  2542.             inputs:0,
  2543.             outputs:0,
  2544.             icon: "comment.png",
  2545.             label: function() {
  2546.                 return this.name||"";
  2547.             },
  2548.             labelStyle: function() {
  2549.                 return this.name?"node_label_italic":"";
  2550.             },
  2551.             info: function() {
  2552.                 return (this.name?"# "+this.name+"\n":"")+(this.info||"");
  2553.             },
  2554.             oneditprepare: function() {
  2555.                 var that = this;
  2556.                 this.editor = RED.editor.createEditor({
  2557.                     id: 'node-input-info-editor',
  2558.                     mode: 'ace/mode/markdown',
  2559.                     value: $("#node-input-info").val()
  2560.                 });
  2561.                 this.editor.focus();
  2562.             },
  2563.             oneditsave: function() {
  2564.                 $("#node-input-info").val(this.editor.getValue());
  2565.                 delete this.editor;
  2566.             },
  2567.             oneditresize: function(size) {
  2568.                 var rows = $("#dialog-form>div:not(.node-text-editor-row)");
  2569.                 var height = $("#dialog-form").height();
  2570.                 for (var i=0;i<rows.size();i++) {
  2571.                     height -= $(rows[i]).outerHeight(true);
  2572.                 }
  2573.                 var editorRow = $("#dialog-form>div.node-text-editor-row");
  2574.                 height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  2575.                 $(".node-text-editor").css("height",height+"px");
  2576.                 this.editor.resize();
  2577.             }
  2578.         });
  2579.     </script>
  2580.     <script type="text/x-red" data-help-name="comment">
  2581.         <p>A node you can use to add comments to your flows.</p>
  2582.     </script><!--
  2583.       Copyright JS Foundation and other contributors, http://js.foundation
  2584.      
  2585.       Licensed under the Apache License, Version 2.0 (the "License");
  2586.       you may not use this file except in compliance with the License.
  2587.       You may obtain a copy of the License at
  2588.      
  2589.       http://www.apache.org/licenses/LICENSE-2.0
  2590.      
  2591.       Unless required by applicable law or agreed to in writing, software
  2592.       distributed under the License is distributed on an "AS IS" BASIS,
  2593.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2594.       See the License for the specific language governing permissions and
  2595.       limitations under the License.
  2596.     -->
  2597.      
  2598.     <script type="text/x-red" data-template-name="unknown">
  2599.         <div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
  2600.     </script>
  2601.      
  2602.      
  2603.      
  2604.     <script type="text/javascript">
  2605.         RED.nodes.registerType('unknown',{
  2606.             category: 'unknown',
  2607.             color:"#fff0f0",
  2608.             defaults: {
  2609.                 name: {value:""}
  2610.             },
  2611.             inputs:1,
  2612.             outputs:1,
  2613.             icon: "",
  2614.             label: function() {
  2615.                 return "("+this.name+")"||this._("unknown.label.unknown");
  2616.             },
  2617.             labelStyle: function() {
  2618.                 return "node_label_unknown";
  2619.             }
  2620.         });
  2621.     </script>
  2622.     <script type="text/x-red" data-help-name="unknown">
  2623.         <p>This node is a type unknown to your installation of Node-RED.</p>
  2624.         <p><i>If you deploy with the node in this state, it's configuration will be preserved, but
  2625.         the flow will not start until the missing type is installed.</i></p>
  2626.         <p>It is possible this node type is already installed, but is missing a dependency. Check the Node-RED start-up log for
  2627.            any error messages associated with the missing node type. Use <b>npm install &lt;module&gt;</b> to install any missing modules
  2628.            and restart Node-RED and reimport the nodes.</p>
  2629.         <p>Otherwise, you should contact the author of the flow to obtain a copy of the missing node type.</p>
  2630.     </script><!--
  2631.       Copyright JS Foundation and other contributors, http://js.foundation
  2632.      
  2633.       Licensed under the Apache License, Version 2.0 (the "License");
  2634.       you may not use this file except in compliance with the License.
  2635.       You may obtain a copy of the License at
  2636.      
  2637.       http://www.apache.org/licenses/LICENSE-2.0
  2638.      
  2639.       Unless required by applicable law or agreed to in writing, software
  2640.       distributed under the License is distributed on an "AS IS" BASIS,
  2641.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2642.       See the License for the specific language governing permissions and
  2643.       limitations under the License.
  2644.     -->
  2645.      
  2646.     <script type="text/x-red" data-template-name="tls-config">
  2647.         <div class="form-row">
  2648.             <label style="width: 120px;" for="node-config-input-cert"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.cert"></span></label>
  2649.             <input style="width: 60%;" type="text" id="node-config-input-cert" data-i18n="[placeholder]tls.placeholder.cert">
  2650.         </div>
  2651.         <div class="form-row">
  2652.             <label style="width: 120px;" for="node-config-input-key"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.key"></span></label>
  2653.             <input style="width: 60%;" type="text" id="node-config-input-key" data-i18n="[placeholder]tls.placeholder.key">
  2654.         </div>
  2655.         <div class="form-row">
  2656.             <label style="width: 120px;" for="node-config-input-ca"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.ca"></span></label>
  2657.             <input style="width: 60%;" type="text" id="node-config-input-ca" data-i18n="[placeholder]tls.placeholder.ca">
  2658.         </div>
  2659.         <div class="form-row">
  2660.             <input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
  2661.             <label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="tls.label.verify-server-cert"></label>
  2662.         </div>
  2663.         <div class="form-row">
  2664.             <label style="width: 120px;" for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  2665.             <input style="width: 60%;" type="text" id="node-config-input-name" data-i18n="[placeholder]common.label.name">
  2666.         </div>
  2667.     </script>
  2668.      
  2669.      
  2670.      
  2671.     <script type="text/javascript">
  2672.         RED.nodes.registerType('tls-config',{
  2673.             category: 'config',
  2674.             defaults: {
  2675.                 name: {value:""},
  2676.                 cert: {value:"", validate: function(v) {
  2677.                     var currentKey = $("#node-config-input-key").val();
  2678.                     if (currentKey === undefined) {
  2679.                         currentKey = this.key;
  2680.                     }
  2681.                     return currentKey === '' || v != '';
  2682.                 }},
  2683.                 key: {value:"", validate: function(v) {
  2684.                     var currentCert = $("#node-config-input-cert").val();
  2685.                     if (currentCert === undefined) {
  2686.                         currentCert = this.cert;
  2687.                     }
  2688.                     return currentCert === '' || v != '';
  2689.                 }},
  2690.                 ca: {value:""},
  2691.                 verifyservercert: {value: true}
  2692.             },
  2693.             label: function() {
  2694.                 return this.name || this._("tls.tls");
  2695.             },
  2696.             labelStyle: function() {
  2697.                 return this.name?"node_label_italic":"";
  2698.             }
  2699.         });
  2700.     </script>
  2701.     <script type="text/x-red" data-help-name="tls-config">
  2702.         <p>Configuration options for TLS connections.</p>
  2703.     </script><!--
  2704.       Copyright JS Foundation and other contributors, http://js.foundation
  2705.       Licensed under the Apache License, Version 2.0 (the "License");
  2706.       you may not use this file except in compliance with the License.
  2707.       You may obtain a copy of the License at
  2708.       http://www.apache.org/licenses/LICENSE-2.0
  2709.       Unless required by applicable law or agreed to in writing, software
  2710.       distributed under the License is distributed on an "AS IS" BASIS,
  2711.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2712.       See the License for the specific language governing permissions and
  2713.       limitations under the License.
  2714.     -->
  2715.      
  2716.     <script type="text/x-red" data-template-name="mqtt in">
  2717.         <div class="form-row">
  2718.             <label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
  2719.             <input type="text" id="node-input-broker">
  2720.         </div>
  2721.         <div class="form-row">
  2722.             <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  2723.             <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
  2724.         </div>
  2725.         <div class="form-row">
  2726.             <label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
  2727.             <select id="node-input-qos" style="width:125px !important">
  2728.                 <option value="0">0</option>
  2729.                 <option value="1">1</option>
  2730.                 <option value="2">2</option>
  2731.             </select>
  2732.         </div>
  2733.         <div class="form-row">
  2734.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  2735.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  2736.         </div>
  2737.     </script>
  2738.      
  2739.      
  2740.      
  2741.     <script type="text/javascript">
  2742.         RED.nodes.registerType('mqtt in',{
  2743.             category: 'input',
  2744.             defaults: {
  2745.                 name: {value:""},
  2746.                 topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)},
  2747.                 qos: {value: "2"},
  2748.                 broker: {type:"mqtt-broker", required:true}
  2749.             },
  2750.             color:"#d8bfd8",
  2751.             inputs:0,
  2752.             outputs:1,
  2753.             icon: "bridge.png",
  2754.             label: function() {
  2755.                 return this.name||this.topic||"mqtt";
  2756.             },
  2757.             labelStyle: function() {
  2758.                 return this.name?"node_label_italic":"";
  2759.             },
  2760.             oneditprepare: function() {
  2761.                 if (this.qos === undefined) {
  2762.                     $("#node-input-qos").val("2");
  2763.                 }
  2764.             }
  2765.         });
  2766.     </script>
  2767.      
  2768.     <script type="text/x-red" data-template-name="mqtt out">
  2769.         <div class="form-row">
  2770.             <label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
  2771.             <input type="text" id="node-input-broker">
  2772.         </div>
  2773.         <div class="form-row">
  2774.             <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  2775.             <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
  2776.         </div>
  2777.         <div class="form-row">
  2778.             <label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
  2779.             <select id="node-input-qos" style="width:125px !important">
  2780.                 <option value=""></option>
  2781.                 <option value="0">0</option>
  2782.                 <option value="1">1</option>
  2783.                 <option value="2">2</option>
  2784.             </select>
  2785.             &nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;<span data-i18n="mqtt.retain"></span> &nbsp;<select id="node-input-retain" style="width:125px !important">
  2786.                 <option value=""></option>
  2787.                 <option value="false" data-i18n="mqtt.false"></option>
  2788.                 <option value="true" data-i18n="mqtt.true"></option>
  2789.             </select>
  2790.         </div>
  2791.         <div class="form-row">
  2792.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  2793.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  2794.         </div>
  2795.         <div class="form-tips"><span data-i18n="mqtt.tip"></span></div>
  2796.     </script>
  2797.      
  2798.      
  2799.      
  2800.     <script type="text/javascript">
  2801.         RED.nodes.registerType('mqtt out',{
  2802.             category: 'output',
  2803.             defaults: {
  2804.                 name: {value:""},
  2805.                 topic: {value:""},
  2806.                 qos: {value:""},
  2807.                 retain: {value:""},
  2808.                 broker: {type:"mqtt-broker", required:true}
  2809.             },
  2810.             color:"#d8bfd8",
  2811.             inputs:1,
  2812.             outputs:0,
  2813.             icon: "bridge.png",
  2814.             align: "right",
  2815.             label: function() {
  2816.                 return this.name||this.topic||"mqtt";
  2817.             },
  2818.             labelStyle: function() {
  2819.                 return this.name?"node_label_italic":"";
  2820.             }
  2821.         });
  2822.     </script>
  2823.      
  2824.     <script type="text/x-red" data-template-name="mqtt-broker">
  2825.         <div class="form-row">
  2826.             <ul style="background: #fff; min-width: 600px; margin-bottom: 20px;" id="node-config-mqtt-broker-tabs"></ul>
  2827.         </div>
  2828.         <div id="node-config-mqtt-broker-tabs-content" style="min-height: 170px;">
  2829.             <div id="mqtt-broker-tab-connection" style="display:none">
  2830.                 <div class="form-row node-input-broker">
  2831.                     <label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
  2832.                     <input class="input-append-left" type="text" id="node-config-input-broker" placeholder="e.g. localhost" style="width: 40%;" >
  2833.                     <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
  2834.                     <input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
  2835.                 </div>
  2836.                 <div class="form-row">
  2837.                     <input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
  2838.                     <label for="node-config-input-usetls" style="width: auto" data-i18n="mqtt.label.use-tls"></label>
  2839.                     <div id="node-config-row-tls" class="hide">
  2840.                         <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-config-input-tls"><span data-i18n="mqtt.label.tls-config"></span></label><input style="width: 300px;" type="text" id="node-config-input-tls">
  2841.                     </div>
  2842.                 </div>
  2843.                 <div class="form-row">
  2844.                     <label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
  2845.                     <input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid">
  2846.                 </div>
  2847.                 <div class="form-row">
  2848.                     <label for="node-config-input-keepalive" style="width: auto"><i class="fa fa-clock-o"></i> <span data-i18n="mqtt.label.keepalive"></span></label>
  2849.                     <input type="text" id="node-config-input-keepalive" style="width: 50px">
  2850.                     <input type="checkbox" id="node-config-input-cleansession" style="margin-left: 30px; height: 1em;display: inline-block; width: auto; vertical-align: middle;">
  2851.                     <label for="node-config-input-cleansession" style="width: auto;" data-i18n="mqtt.label.cleansession"></label>
  2852.                 </div>
  2853.                 <div class="form-row">
  2854.                     <input type="checkbox" id="node-config-input-compatmode" style="display: inline-block; width: auto; vertical-align: top;">
  2855.                     <label for="node-config-input-compatmode" style="width: auto;" data-i18n="mqtt.label.compatmode"></label>
  2856.                 </div>
  2857.             </div>
  2858.             <div id="mqtt-broker-tab-security" style="display:none">
  2859.                 <div class="form-row">
  2860.                     <label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
  2861.                     <input type="text" id="node-config-input-user">
  2862.                 </div>
  2863.                 <div class="form-row">
  2864.                     <label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
  2865.                     <input type="password" id="node-config-input-password">
  2866.                 </div>
  2867.             </div>
  2868.             <div id="mqtt-broker-tab-birth" style="display:none">
  2869.                 <div class="form-row">
  2870.                     <label for="node-config-input-birthTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  2871.                     <input type="text" id="node-config-input-birthTopic" data-i18n="[placeholder]mqtt.placeholder.birth-topic">
  2872.                 </div>
  2873.                 <div class="form-row">
  2874.                     <label for="node-config-input-birthQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
  2875.                     <select id="node-config-input-birthQos" style="width:125px !important">
  2876.                         <option value="0">0</option>
  2877.                         <option value="1">1</option>
  2878.                         <option value="2">2</option>
  2879.                     </select>
  2880.                     &nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;<span data-i18n="mqtt.retain"></span> &nbsp;<select id="node-config-input-birthRetain" style="width:125px !important">
  2881.                         <option value="false" data-i18n="mqtt.false"></option>
  2882.                         <option value="true" data-i18n="mqtt.true"></option>
  2883.                     </select>
  2884.                 </div>
  2885.                 <div class="form-row">
  2886.                     <label for="node-config-input-birthPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
  2887.                     <input type="text" id="node-config-input-birthPayload" data-i18n="[placeholder]common.label.payload">
  2888.                 </div>
  2889.             </div>
  2890.             <div id="mqtt-broker-tab-will" style="display:none">
  2891.                 <div class="form-row">
  2892.                     <label for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  2893.                     <input type="text" id="node-config-input-willTopic" data-i18n="[placeholder]mqtt.placeholder.will-topic">
  2894.                 </div>
  2895.                 <div class="form-row">
  2896.                     <label for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
  2897.                     <select id="node-config-input-willQos" style="width:125px !important">
  2898.                         <option value="0">0</option>
  2899.                         <option value="1">1</option>
  2900.                         <option value="2">2</option>
  2901.                     </select>
  2902.                     &nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;<span data-i18n="mqtt.retain"></span> &nbsp;<select id="node-config-input-willRetain" style="width:125px !important">
  2903.                         <option value="false" data-i18n="mqtt.false"></option>
  2904.                         <option value="true" data-i18n="mqtt.true"></option>
  2905.                     </select>
  2906.                 </div>
  2907.                 <div class="form-row">
  2908.                     <label for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
  2909.                     <input type="text" id="node-config-input-willPayload" data-i18n="[placeholder]common.label.payload">
  2910.                 </div>
  2911.             </div>
  2912.         </div>
  2913.     </script>
  2914.      
  2915.      
  2916.      
  2917.     <script type="text/javascript">
  2918.         RED.nodes.registerType('mqtt-broker',{
  2919.             category: 'config',
  2920.             defaults: {
  2921.                 broker: {value:"",required:true},
  2922.                 port: {value:1883,required:true,validate:RED.validators.number()},
  2923.                 tls: {type:"tls-config",required: false},
  2924.                 clientid: {value:"", validate: function(v) {
  2925.                         if ($("#node-config-input-clientid").length) {
  2926.                             // Currently editing the node
  2927.                             return $("#node-config-input-cleansession").is(":checked") || (v||"").length > 0;
  2928.                         } else {
  2929.                             return (this.cleansession===undefined || this.cleansession) || (v||"").length > 0;
  2930.                         }
  2931.                     }},
  2932.                 usetls: {value: false},
  2933.                 verifyservercert: { value: false},
  2934.                 compatmode: { value: true},
  2935.                 keepalive: {value:60,validate:RED.validators.number()},
  2936.                 cleansession: {value: true},
  2937.                 willTopic: {value:""},
  2938.                 willQos: {value:"0"},
  2939.                 willRetain: {value:false},
  2940.                 willPayload: {value:""},
  2941.                 birthTopic: {value:""},
  2942.                 birthQos: {value:"0"},
  2943.                 birthRetain: {value:false},
  2944.                 birthPayload: {value:""}
  2945.             },
  2946.             credentials: {
  2947.                 user: {type:"text"},
  2948.                 password: {type: "password"}
  2949.             },
  2950.             label: function() {
  2951.                 var b = this.broker;
  2952.                 if (b === "") { b = "undefined"; }
  2953.                 return (this.clientid?this.clientid+"@":"")+b+":"+this.port;
  2954.             },
  2955.             oneditprepare: function () {
  2956.                 var tabs = RED.tabs.create({
  2957.                     id: "node-config-mqtt-broker-tabs",
  2958.                     onchange: function(tab) {
  2959.                         $("#node-config-mqtt-broker-tabs-content").children().hide();
  2960.                         $("#" + tab.id).show();
  2961.                     }
  2962.                 });
  2963.                 tabs.addTab({
  2964.                     id: "mqtt-broker-tab-connection",
  2965.                     label: this._("mqtt.tabs-label.connection")
  2966.                 });
  2967.                 tabs.addTab({
  2968.                     id: "mqtt-broker-tab-security",
  2969.                     label: this._("mqtt.tabs-label.security")
  2970.                 });
  2971.                 tabs.addTab({
  2972.                     id: "mqtt-broker-tab-birth",
  2973.                     label: this._("mqtt.tabs-label.birth")
  2974.                 });
  2975.                 tabs.addTab({
  2976.                     id: "mqtt-broker-tab-will",
  2977.                     label: this._("mqtt.tabs-label.will")
  2978.                 });
  2979.                 setTimeout(function() { tabs.resize()},0);
  2980.                 if (typeof this.cleansession === 'undefined') {
  2981.                     this.cleansession = true;
  2982.                     $("#node-config-input-cleansession").prop("checked",true);
  2983.                 }
  2984.                 if (typeof this.usetls === 'undefined') {
  2985.                     this.usetls = false;
  2986.                     $("#node-config-input-usetls").prop("checked",false);
  2987.                 }
  2988.                 if (typeof this.compatmode === 'undefined') {
  2989.                     this.compatmode = true;
  2990.                     $("#node-config-input-compatmode").prop('checked', true);
  2991.                 }
  2992.                 if (typeof this.keepalive === 'undefined') {
  2993.                     this.keepalive = 15;
  2994.                     $("#node-config-input-keepalive").val(this.keepalive);
  2995.                 }
  2996.                 if (typeof this.willQos === 'undefined') {
  2997.                     this.willQos = "0";
  2998.                     $("#node-config-input-willQos").val("0");
  2999.                 }
  3000.                 if (typeof this.birthQos === 'undefined') {
  3001.                     this.birthQos = "0";
  3002.                     $("#node-config-input-birthQos").val("0");
  3003.                 }
  3004.      
  3005.                 function updateTLSOptions() {
  3006.                     if ($("#node-config-input-usetls").is(':checked')) {
  3007.                         $("#node-config-row-tls").show();
  3008.                     } else {
  3009.                         $("#node-config-row-tls").hide();
  3010.                     }
  3011.                 }
  3012.                 updateTLSOptions();
  3013.                 $("#node-config-input-usetls").on("click",function() {
  3014.                     updateTLSOptions();
  3015.                 });
  3016.                 var node = this;
  3017.                 function updateClientId() {
  3018.                     if ($("#node-config-input-cleansession").is(":checked")) {
  3019.                         $("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid"));
  3020.                     } else {
  3021.                         $("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid-nonclean"));
  3022.                     }
  3023.                     $("#node-config-input-clientid").change();
  3024.                 }
  3025.                 setTimeout(updateClientId,0);
  3026.                 $("#node-config-input-cleansession").on("click",function() {
  3027.                     updateClientId();
  3028.                 });
  3029.             },
  3030.             oneditsave: function() {
  3031.                 if (!$("#node-config-input-usetls").is(':checked')) {
  3032.                     $("#node-config-input-tls").val("");
  3033.                 }
  3034.             }
  3035.         });
  3036.     </script>
  3037.     <script type="text/x-red" data-help-name="mqtt in">
  3038.         <p>Connects to a broker and subscribes to the specified topic.</p>
  3039.         <p>Outputs a message with the properties:</p>
  3040.         <ul>
  3041.            <li><code>msg.topic</code></li>
  3042.            <li><code>msg.payload</code></li>
  3043.            <li><code>msg.qos</code></li>
  3044.            <li><code>msg.retain</code></li>
  3045.         </ul>
  3046.         <p><code>msg.payload</code> will be a String, unless it is detected as a binary buffer.</p>
  3047.     </script><script type="text/x-red" data-help-name="mqtt out">
  3048.         <p>Connects to a MQTT broker and publishes messages.</p>
  3049.         <p><code>msg.payload</code> is used as the payload of the published message.
  3050.         If it contains an Object it will be converted to JSON before being sent.
  3051.         </p>
  3052.         <p>The topic used can be configured in the node or, if left blank, can be set
  3053.            by <code>msg.topic</code>.</p>
  3054.         <p>Likewise the QoS and retain values can be configured in the node or, if left
  3055.            blank, set by <code>msg.qos</code> and <code>msg.retain</code> respectively.
  3056.            By default, messages are published at QoS 0 with the retain flag set to false.</p>
  3057.     </script><script type="text/x-red" data-help-name="mqtt-broker">
  3058.         <p>A minimum MQTT broker connection requires only a broker server address to be added to the default configuration.</p>
  3059.         <p>To secure the connection with SSL/TLS, a TLS Configuration must also be configured and selected.</p>
  3060.         <p>If you create a Client ID it must be unique to the broker you are connecting to.</p>
  3061.         <p>For more information about MQTT see the <a href="http://www.eclipse.org/paho/" target="_blank">Eclipse Paho</a> site.</p>
  3062.     </script><!--
  3063.       Copyright JS Foundation and other contributors, http://js.foundation
  3064.      
  3065.       Licensed under the Apache License, Version 2.0 (the "License");
  3066.       you may not use this file except in compliance with the License.
  3067.       You may obtain a copy of the License at
  3068.      
  3069.       http://www.apache.org/licenses/LICENSE-2.0
  3070.      
  3071.       Unless required by applicable law or agreed to in writing, software
  3072.       distributed under the License is distributed on an "AS IS" BASIS,
  3073.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3074.       See the License for the specific language governing permissions and
  3075.       limitations under the License.
  3076.     -->
  3077.      
  3078.     <script type="text/x-red" data-template-name="http in">
  3079.         <div class="form-row">
  3080.             <label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
  3081.             <select type="text" id="node-input-method" style="width:70%;">
  3082.             <option value="get">GET</option>
  3083.             <option value="post">POST</option>
  3084.             <option value="put">PUT</option>
  3085.             <option value="delete">DELETE</option>
  3086.             <option value="patch">PATCH</option>
  3087.             </select>
  3088.         </div>
  3089.         <div class="form-row">
  3090.             <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
  3091.             <input id="node-input-url" type="text" placeholder="/url">
  3092.         </div>
  3093.         <div class="form-row">
  3094.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3095.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3096.         </div>
  3097.         <div class="form-row row-swagger-doc">
  3098.             <label for="node-input-swaggerDoc"><i class="fa fa-file-text-o"></i> <span data-i18n="httpin.label.doc"></span></label>
  3099.             <input type="text" id="node-input-swaggerDoc">
  3100.         </div>
  3101.         <div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div>
  3102.     </script>
  3103.      
  3104.      
  3105.      
  3106.     <script type="text/x-red" data-template-name="http response">
  3107.         <div class="form-row">
  3108.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3109.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3110.         </div>
  3111.         <div class="form-tips"><span data-i18n="[html]httpin.tip.res"></span></div>
  3112.     </script>
  3113.      
  3114.      
  3115.      
  3116.     <script type="text/javascript">
  3117.         RED.nodes.registerType('http in',{
  3118.             category: 'input',
  3119.             color:"rgb(231, 231, 174)",
  3120.             defaults: {
  3121.                 name: {value:""},
  3122.                 url: {value:"",required:true},
  3123.                 method: {value:"get",required:true},
  3124.                 swaggerDoc: {type:"swagger-doc", required:false}
  3125.             },
  3126.             inputs:0,
  3127.             outputs:1,
  3128.             icon: "white-globe.png",
  3129.             label: function() {
  3130.                 if (this.name) {
  3131.                     return this.name;
  3132.                 } else if (this.url) {
  3133.                     var root = RED.settings.httpNodeRoot;
  3134.                     if (root.slice(-1) != "/") {
  3135.                         root = root+"/";
  3136.                     }
  3137.                     if (this.url.charAt(0) == "/") {
  3138.                         root += this.url.slice(1);
  3139.                     } else {
  3140.                         root += this.url;
  3141.                     }
  3142.                     return "["+this.method+"] "+root;
  3143.                 } else {
  3144.                     return "http";
  3145.                 }
  3146.             },
  3147.             labelStyle: function() {
  3148.                 return this.name?"node_label_italic":"";
  3149.             },
  3150.             oneditprepare: function() {
  3151.                 var root = RED.settings.httpNodeRoot;
  3152.                 if (root.slice(-1) == "/") {
  3153.                     root = root.slice(0,-1);
  3154.                 }
  3155.                 if (root == "") {
  3156.                     $("#node-input-tip").hide();
  3157.                 } else {
  3158.                     $("#node-input-path").html(root);
  3159.                     $("#node-input-tip").show();
  3160.                 }
  3161.                 if(!RED.nodes.getType("swagger-doc")){
  3162.                     $('.row-swagger-doc').hide();
  3163.                 }
  3164.             }
  3165.      
  3166.         });
  3167.      
  3168.         RED.nodes.registerType('http response',{
  3169.             category: 'output',
  3170.             color:"rgb(231, 231, 174)",
  3171.             defaults: {
  3172.                 name: {value:""}
  3173.             },
  3174.             inputs:1,
  3175.             outputs:0,
  3176.             align: "right",
  3177.             icon: "white-globe.png",
  3178.             label: function() {
  3179.                 return this.name||"http";
  3180.             },
  3181.             labelStyle: function() {
  3182.                 return this.name?"node_label_italic":"";
  3183.             }
  3184.         });
  3185.     </script>
  3186.     <script type="text/x-red" data-help-name="http in">
  3187.         <p>Provides an input node for http requests, allowing the creation of simple web services.</p>
  3188.         <p>The resulting message has the following properties:
  3189.             <ul>
  3190.                 <li>msg.req : <a href="http://expressjs.com/api.html#req">http request</a></li>
  3191.                 <li>msg.res : <a href="http://expressjs.com/api.html#res">http response</a></li>
  3192.             </ul>
  3193.         </p>
  3194.         <p>For POST/PUT requests, the body is available under <code>msg.req.body</code>. This
  3195.            uses the <a href="http://expressjs.com/api.html#bodyParser">Express bodyParser middleware</a> to parse the content to a JSON object.
  3196.         </p>
  3197.         <p>
  3198.            By default, this expects the body of the request to be url encoded:
  3199.            <pre>foo=bar&amp;this=that</pre>
  3200.         </p>
  3201.         <p>
  3202.            To send JSON encoded data to the node, the content-type header of the request must be set to
  3203.            <code>application/json</code>.
  3204.         </p>
  3205.         <p>
  3206.            <b>Note: </b>This node does not send any response to the http request.
  3207.            This should be done with a subsequent HTTP Response node.
  3208.         </p>
  3209.      
  3210.     </script><script type="text/x-red" data-help-name="http response">
  3211.         <p>Sends responses back to http requests received from an HTTP Input node.</p>
  3212.         <p>The response can be customised using the following message properties:</p>
  3213.         <ul>
  3214.             <li><code>payload</code> is sent as the body of the response</li>
  3215.             <li><code>statusCode</code>, if set, is used as the response status code (default: 200)</li>
  3216.             <li><code>headers</code>, if set, should be an object containing field/value
  3217.             pairs to be added as response headers.</li>
  3218.             <li><code>cookies</code>, if set, can be used to set or delete cookies.
  3219.         </ul>
  3220.         <h3>Cookie handling</h3>
  3221.         <p>The <code>cookies</code> property must be an object of name/value pairs.
  3222.         The value can be either a string to set the value of the cookie with default
  3223.         options, or it can be an object of options.<p>
  3224.         <p>The following example sets two cookies - one called <code>name</code> with
  3225.         a value of <code>nick</code>, the other called <code>session</code> with a
  3226.         value of <code>1234</code> and an expiry set to 15 minutes.</p>
  3227.         <pre>
  3228.     msg.cookies = {
  3229.         name: 'nick',
  3230.         session: {
  3231.             value: '1234',
  3232.             maxAge: 900000
  3233.         }
  3234.     }</pre>
  3235.         <p>The valid options include:</p>
  3236.         <ul>
  3237.         <li><code>domain</code> - (String) domain name for the cookie</li>
  3238.         <li><code>expires</code> - (Date) expiry date in GMT. If not specified or set to 0, creates a session cookie</li>
  3239.         <li><code>maxAge</code> - (String) expiry date as relative to the current time in milliseconds</li>
  3240.         <li><code>path</code> - (String) path for the cookie. Defaults to /</li>
  3241.         <li><code>value</code> - (String) the value to use for the cookie</li>
  3242.         </ul>
  3243.         <p>To delete a cookie, set its <code>value</code> to <code>null</code>.</p>
  3244.      
  3245.     </script><!--
  3246.       Copyright JS Foundation and other contributors, http://js.foundation
  3247.      
  3248.       Licensed under the Apache License, Version 2.0 (the "License");
  3249.       you may not use this file except in compliance with the License.
  3250.       You may obtain a copy of the License at
  3251.      
  3252.       http://www.apache.org/licenses/LICENSE-2.0
  3253.      
  3254.       Unless required by applicable law or agreed to in writing, software
  3255.       distributed under the License is distributed on an "AS IS" BASIS,
  3256.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3257.       See the License for the specific language governing permissions and
  3258.       limitations under the License.
  3259.     -->
  3260.      
  3261.     <script type="text/x-red" data-template-name="http request">
  3262.         <div class="form-row">
  3263.             <label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
  3264.             <select type="text" id="node-input-method" style="width:70%;">
  3265.             <option value="GET">GET</option>
  3266.             <option value="POST">POST</option>
  3267.             <option value="PUT">PUT</option>
  3268.             <option value="DELETE">DELETE</option>
  3269.             <option value="use" data-i18n="httpin.setby"></option>
  3270.             </select>
  3271.         </div>
  3272.         <div class="form-row">
  3273.             <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
  3274.             <input id="node-input-url" type="text" placeholder="http://">
  3275.         </div>
  3276.      
  3277.         <div class="form-row">
  3278.             <input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
  3279.             <label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label>
  3280.             <div id="node-row-tls" class="hide">
  3281.                 <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls">
  3282.             </div>
  3283.         </div>
  3284.      
  3285.         <div class="form-row">
  3286.             <input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;">
  3287.             <label for="node-input-useAuth" style="width: 70%;"><span data-i18n="httpin.basicauth"></span></label>
  3288.             <div style="margin-left: 20px" class="node-input-useAuth-row hide">
  3289.                 <div class="form-row">
  3290.                     <label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
  3291.                     <input type="text" id="node-input-user">
  3292.                 </div>
  3293.                 <div class="form-row">
  3294.                     <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
  3295.                     <input type="password" id="node-input-password">
  3296.                 </div>
  3297.             </div>
  3298.         </div>
  3299.      
  3300.      
  3301.         <div class="form-row">
  3302.             <label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label>
  3303.             <select type="text" id="node-input-ret" style="width:70%;">
  3304.             <option value="txt" data-i18n="httpin.utf8"></option>
  3305.             <option value="bin" data-i18n="httpin.binary"></option>
  3306.             <option value="obj" data-i18n="httpin.json"></option>
  3307.             </select>
  3308.         </div>
  3309.         <div class="form-row">
  3310.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3311.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3312.         </div>
  3313.         <div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div>
  3314.     </script>
  3315.      
  3316.      
  3317.      
  3318.     <script type="text/javascript">
  3319.         RED.nodes.registerType('http request',{
  3320.             category: 'function',
  3321.             color:"rgb(231, 231, 174)",
  3322.             defaults: {
  3323.                 name: {value:""},
  3324.                 method:{value:"GET"},
  3325.                 ret: {value:"txt"},
  3326.                 url:{value:""},
  3327.                 tls: {type:"tls-config",required: false}
  3328.             },
  3329.             credentials: {
  3330.                 user: {type:"text"},
  3331.                 password: {type: "password"}
  3332.             },
  3333.             inputs:1,
  3334.             outputs:1,
  3335.             icon: "white-globe.png",
  3336.             label: function() {
  3337.                 return this.name||this._("httpin.httpreq");
  3338.             },
  3339.             labelStyle: function() {
  3340.                 return this.name?"node_label_italic":"";
  3341.             },
  3342.             oneditprepare: function() {
  3343.                 $("#node-input-useAuth").change(function() {
  3344.                     if ($(this).is(":checked")) {
  3345.                         $(".node-input-useAuth-row").show();
  3346.                     } else {
  3347.                         $(".node-input-useAuth-row").hide();
  3348.                         $('#node-input-user').val('');
  3349.                         $('#node-input-password').val('');
  3350.                     }
  3351.                 });
  3352.                 if (this.credentials.user || this.credentials.has_password) {
  3353.                     $('#node-input-useAuth').prop('checked', true);
  3354.                 } else {
  3355.                     $('#node-input-useAuth').prop('checked', false);
  3356.                 }
  3357.                 $("#node-input-useAuth").change();
  3358.      
  3359.                 function updateTLSOptions() {
  3360.                     if ($("#node-input-usetls").is(':checked')) {
  3361.                         $("#node-row-tls").show();
  3362.                     } else {
  3363.                         $("#node-row-tls").hide();
  3364.                     }
  3365.                 }
  3366.                 if (this.tls) {
  3367.                     $('#node-input-usetls').prop('checked', true);
  3368.                 } else {
  3369.                     $('#node-input-usetls').prop('checked', false);
  3370.                 }
  3371.                 updateTLSOptions();
  3372.                 $("#node-input-usetls").on("click",function() {
  3373.                     updateTLSOptions();
  3374.                 });
  3375.                 $("#node-input-ret").change(function() {
  3376.                     if ($("#node-input-ret").val() === "obj") {
  3377.                         $("#tip-json").show();
  3378.                     } else {
  3379.                         $("#tip-json").hide();
  3380.                     }
  3381.                 });
  3382.             },
  3383.             oneditsave: function() {
  3384.                 if (!$("#node-input-usetls").is(':checked')) {
  3385.                     $("#node-input-tls").val("_ADD_");
  3386.                 }
  3387.             }
  3388.         });
  3389.     </script>
  3390.     <script type="text/x-red" data-help-name="http request">
  3391.         <p>Provides a node for making http requests.</p>
  3392.         <p>The URL and HTTP method can be configured in the node, if they are left blank they should be set in an incoming message on <code>msg.url</code> and <code>msg.method</code>:</p>
  3393.         <ul>
  3394.             <li><code>url</code>, if set, is used as the url of the request. Must start with http: or https:</li>
  3395.             <li><code>method</code>, if set, is used as the HTTP method of the request.
  3396.             Must be one of <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> or <code>DELETE</code> (default: GET)</li>
  3397.             <li><code>headers</code>, if set, should be an object containing field/value
  3398.             pairs to be added as request headers</li>
  3399.             <li><code>payload</code> is sent as the body of the request</li>
  3400.         </ul>
  3401.         <p>When configured within the node, the URL property can contain <a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache-style</a> tags. These allow the
  3402.         url to be constructed using values of the incoming message. For example, if the url is set to
  3403.         <code>example.com/{{{topic}}}</code>, it will have the value of <code>msg.topic</code> automatically inserted.
  3404.         Using {{{...}}} prevents mustache from escaping characters like / & etc.</p>
  3405.         <p>
  3406.         The output message contains the following properties:
  3407.         <ul>
  3408.             <li><code>payload</code> is the body of the response</li>
  3409.             <li><code>statusCode</code> is the status code of the response, or the error code if the request could not be completed</li>
  3410.             <li><code>headers</code> is an object containing the response headers</li>
  3411.             <li><code>responseUrl</code> is the url of the server that responds</li>
  3412.         </ul>
  3413.         <p><b>Note</b>: If you need to configure a proxy please add <b>http_proxy=...</b> to your environment variables and restart Node-RED.</p>
  3414.     </script><!--
  3415.       Copyright JS Foundation and other contributors, http://js.foundation
  3416.      
  3417.       Licensed under the Apache License, Version 2.0 (the "License");
  3418.       you may not use this file except in compliance with the License.
  3419.       You may obtain a copy of the License at
  3420.      
  3421.       http://www.apache.org/licenses/LICENSE-2.0
  3422.      
  3423.       Unless required by applicable law or agreed to in writing, software
  3424.       distributed under the License is distributed on an "AS IS" BASIS,
  3425.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3426.       See the License for the specific language governing permissions and
  3427.       limitations under the License.
  3428.     -->
  3429.     <!-- WebSocket Input Node -->
  3430.     <script type="text/x-red" data-template-name="websocket in">
  3431.         <div class="form-row">
  3432.             <label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
  3433.             <select id="node-input-mode">
  3434.                 <option value="server" data-i18n="websocket.listenon"></option>
  3435.                 <option value="client" data-i18n="websocket.connectto"></option>
  3436.             </select>
  3437.         </div>
  3438.         <div class="form-row" id="websocket-server-row">
  3439.             <label for="node-input-server"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
  3440.             <input type="text" id="node-input-server">
  3441.         </div>
  3442.         <div class="form-row" id="websocket-client-row">
  3443.             <label for="node-input-client"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
  3444.             <input type="text" id="node-input-client">
  3445.         </div>
  3446.         <div class="form-row">
  3447.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3448.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3449.         </div>
  3450.     </script>
  3451.      
  3452.      
  3453.      
  3454.     <script type="text/javascript">
  3455.      
  3456.     (function() {
  3457.      
  3458.         function ws_oneditprepare() {
  3459.             $("#websocket-client-row").hide();
  3460.             $("#node-input-mode").change(function() {
  3461.                 if ( $("#node-input-mode").val() === 'client') {
  3462.                     $("#websocket-server-row").hide();
  3463.                     $("#websocket-client-row").show();
  3464.                 }
  3465.                 else {
  3466.                     $("#websocket-server-row").show();
  3467.                     $("#websocket-client-row").hide();
  3468.                 }
  3469.             });
  3470.      
  3471.             if (this.client) {
  3472.                 $("#node-input-mode").val('client').change();
  3473.             }
  3474.             else {
  3475.                 $("#node-input-mode").val('server').change();
  3476.             }
  3477.         }
  3478.      
  3479.         function ws_oneditsave() {
  3480.             if ($("#node-input-mode").val() === 'client') {
  3481.                 $("#node-input-server").append('<option value="">Dummy</option>');
  3482.                 $("#node-input-server").val('');
  3483.             }
  3484.             else {
  3485.                 $("#node-input-client").append('<option value="">Dummy</option>');
  3486.                 $("#node-input-client").val('');
  3487.             }
  3488.         }
  3489.      
  3490.         function ws_label() {
  3491.             var nodeid = (this.client)?this.client:this.server;
  3492.             var wsNode = RED.nodes.node(nodeid);
  3493.             return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
  3494.         }
  3495.      
  3496.         function ws_validateserver() {
  3497.             if ($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
  3498.                 return true;
  3499.             }
  3500.             else {
  3501.                 return RED.nodes.node(this.server) != null;
  3502.             }
  3503.         }
  3504.      
  3505.         function ws_validateclient() {
  3506.             if ($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
  3507.                 return RED.nodes.node(this.client) != null;
  3508.             }
  3509.             else {
  3510.                 return true;
  3511.             }
  3512.         }
  3513.      
  3514.         RED.nodes.registerType('websocket in',{
  3515.             category: 'input',
  3516.             defaults: {
  3517.                 name: {value:""},
  3518.                 server: {type:"websocket-listener", validate: ws_validateserver},
  3519.                 client: {type:"websocket-client", validate: ws_validateclient}
  3520.             },
  3521.             color:"rgb(215, 215, 160)",
  3522.             inputs:0,
  3523.             outputs:1,
  3524.             icon: "white-globe.png",
  3525.             labelStyle: function() {
  3526.                 return this.name?"node_label_italic":"";
  3527.             },
  3528.             label: ws_label,
  3529.             oneditsave: ws_oneditsave,
  3530.             oneditprepare: ws_oneditprepare
  3531.         });
  3532.      
  3533.         RED.nodes.registerType('websocket out',{
  3534.             category: 'output',
  3535.             defaults: {
  3536.                 name: {value:""},
  3537.                 server: {type:"websocket-listener", validate: ws_validateserver},
  3538.                 client: {type:"websocket-client", validate: ws_validateclient}
  3539.             },
  3540.             color:"rgb(215, 215, 160)",
  3541.             inputs:1,
  3542.             outputs:0,
  3543.             icon: "white-globe.png",
  3544.             align: "right",
  3545.             labelStyle: function() {
  3546.                 return this.name?"node_label_italic":"";
  3547.             },
  3548.             label: ws_label,
  3549.             oneditsave: ws_oneditsave,
  3550.             oneditprepare: ws_oneditprepare
  3551.         });
  3552.      
  3553.         RED.nodes.registerType('websocket-listener',{
  3554.             category: 'config',
  3555.             defaults: {
  3556.                 path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
  3557.                 wholemsg: {value:"false"}
  3558.             },
  3559.             inputs:0,
  3560.             outputs:0,
  3561.             label: function() {
  3562.                 var root = RED.settings.httpNodeRoot;
  3563.                 if (root.slice(-1) != "/") {
  3564.                     root = root+"/";
  3565.                 }
  3566.                 if (this.path.charAt(0) == "/") {
  3567.                     root += this.path.slice(1);
  3568.                 } else {
  3569.                     root += this.path;
  3570.                 }
  3571.                 return root;
  3572.             },
  3573.             oneditprepare: function() {
  3574.                 var root = RED.settings.httpNodeRoot;
  3575.                 if (root.slice(-1) == "/") {
  3576.                     root = root.slice(0,-1);
  3577.                 }
  3578.                 if (root === "") {
  3579.                     $("#node-config-ws-tip").hide();
  3580.                 } else {
  3581.                     $("#node-config-ws-path").html(root);
  3582.                     $("#node-config-ws-tip").show();
  3583.                 }
  3584.             }
  3585.         });
  3586.      
  3587.         RED.nodes.registerType('websocket-client',{
  3588.             category: 'config',
  3589.             defaults: {
  3590.                 path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
  3591.                 wholemsg: {value:"false"}
  3592.             },
  3593.             inputs:0,
  3594.             outputs:0,
  3595.             label: function() {
  3596.                 return this.path;
  3597.             }
  3598.         });
  3599.      
  3600.     })();
  3601.     </script>
  3602.      
  3603.     <!-- WebSocket out Node -->
  3604.     <script type="text/x-red" data-template-name="websocket out">
  3605.         <div class="form-row">
  3606.             <label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
  3607.             <select id="node-input-mode">
  3608.                 <option value="server" data-i18n="websocket.listenon"></option>
  3609.                 <option value="client" data-i18n="websocket.connectto"></option>
  3610.             </select>
  3611.         </div>
  3612.         <div class="form-row" id="websocket-server-row">
  3613.             <label for="node-input-server"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
  3614.             <input type="text" id="node-input-server">
  3615.         </div>
  3616.         <div class="form-row" id="websocket-client-row">
  3617.             <label for="node-input-client"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
  3618.             <input type="text" id="node-input-client">
  3619.         </div>
  3620.         <div class="form-row">
  3621.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3622.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3623.         </div>
  3624.     </script>
  3625.      
  3626.      
  3627.      
  3628.     <!-- WebSocket Server configuration node -->
  3629.     <script type="text/x-red" data-template-name="websocket-listener">
  3630.         <div class="form-row">
  3631.             <label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
  3632.             <input id="node-config-input-path" type="text" placeholder="/ws/example">
  3633.         </div>
  3634.         <div class="form-row">
  3635.             <label for="node-config-input-wholemsg">&nbsp;</label>
  3636.             <select type="text" id="node-config-input-wholemsg" style="width: 70%;">
  3637.                 <option value="false" data-i18n="websocket.payload"></option>
  3638.                 <option value="true" data-i18n="websocket.message"></option>
  3639.             </select>
  3640.         </div>
  3641.         <div class="form-tips">
  3642.             <span data-i18n="[html]websocket.tip.path1"></span>
  3643.             <p id="node-config-ws-tip"><span data-i18n="[html]websocket.tip.path2"></span><code><span id="node-config-ws-path"></span></code>.</p>
  3644.         </div>
  3645.     </script>
  3646.      
  3647.      
  3648.      
  3649.     <!-- WebSocket Client configuration node -->
  3650.     <script type="text/x-red" data-template-name="websocket-client">
  3651.         <div class="form-row">
  3652.             <label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
  3653.             <input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">
  3654.         </div>
  3655.         <div class="form-row">
  3656.             <label for="node-config-input-wholemsg">&nbsp;</label>
  3657.             <select type="text" id="node-config-input-wholemsg" style="width: 70%;">
  3658.                 <option value="false" data-i18n="websocket.payload"></option>
  3659.                 <option value="true" data-i18n="websocket.message"></option>
  3660.             </select>
  3661.         </div>
  3662.         <div class="form-tips">
  3663.             <p><span data-i18n="[html]websocket.tip.url1"></span></p>
  3664.             <span data-i18n="[html]websocket.tip.url2"></span>
  3665.         </div>
  3666.     </script>
  3667.      
  3668.      
  3669.     <script type="text/x-red" data-help-name="websocket in">
  3670.         <p>WebSocket input node.</p>
  3671.         <p>By default, the data received from the WebSocket will be in <code>msg.payload</code>.
  3672.         The socket can be configured to expect a properly formed JSON string, in which
  3673.         case it will parse the JSON and send on the resulting object as the entire message.</p>
  3674.     </script><script type="text/x-red" data-help-name="websocket out">
  3675.         <p>WebSocket out node.</p>
  3676.         <p>By default, <code>msg.payload</code> will be sent over the WebSocket. The socket
  3677.         can be configured to encode the entire <code>msg</code> object as a JSON string and send that
  3678.         over the WebSocket.</p>
  3679.      
  3680.         <p>If the message arriving at this node started at a WebSocket In node, the message
  3681.         will be sent back to the client that triggered the flow. Otherwise, the message
  3682.         will be broadcast to all connected clients.</p>
  3683.         <p>If you want to broadcast a message that started at a WebSocket In node, you
  3684.         should delete the <code>msg._session</code> property within the flow.</p>
  3685.     </script><script type="text/x-red" data-help-name="websocket-listener">
  3686.        <p>This configuration node creates a WebSocket Server endpoint using the specified path.</p>
  3687.     </script><script type="text/x-red" data-help-name="websocket-client">
  3688.        <p>This configuration node connects a WebSocket client to the specified URL.</p>
  3689.     </script><!--
  3690.       Copyright JS Foundation and other contributors, http://js.foundation
  3691.      
  3692.       Licensed under the Apache License, Version 2.0 (the "License");
  3693.       you may not use this file except in compliance with the License.
  3694.       You may obtain a copy of the License at
  3695.      
  3696.       http://www.apache.org/licenses/LICENSE-2.0
  3697.      
  3698.       Unless required by applicable law or agreed to in writing, software
  3699.       distributed under the License is distributed on an "AS IS" BASIS,
  3700.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3701.       See the License for the specific language governing permissions and
  3702.       limitations under the License.
  3703.     -->
  3704.      
  3705.     <script type="text/x-red" data-template-name="watch">
  3706.         <div class="form-row node-input-filename">
  3707.              <label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label>
  3708.              <input id="node-input-files" type="text" tabindex="1" data-i18n="[placeholder]watch.placeholder.files">
  3709.         </div>
  3710.         <div class="form-row">
  3711.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3712.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3713.         </div>
  3714.          <div id="node-input-tip" class="form-tips"><span data-i18n="watch.tip"></span></div>
  3715.     </script>
  3716.      
  3717.      
  3718.      
  3719.     <script type="text/javascript">
  3720.         RED.nodes.registerType('watch',{
  3721.             category: 'advanced-input',
  3722.             defaults: {
  3723.                 name: {value:""},
  3724.                 files: {value:"",required:true}
  3725.             },
  3726.             color:"BurlyWood",
  3727.             inputs:0,
  3728.             outputs:1,
  3729.             icon: "watch.png",
  3730.             label: function() {
  3731.                 return this.name||this.files;
  3732.             },
  3733.             labelStyle: function() {
  3734.                 return this.name?"node_label_italic":"";
  3735.             }
  3736.         });
  3737.     </script>
  3738.     <script type="text/x-red" data-help-name="watch">
  3739.         <p>Watches a directory or file for changes.</p>
  3740.         <p>You can enter a list of comma separated directories and/or files. You will
  3741.         need to put quotes "..." around any that have spaces in.</p>
  3742.         <p>On Windows you must use double back-slashes \\ in any directory names.</p>
  3743.         <p>The full filename of the file that actually changed is put into <code>msg.payload</code>,
  3744.         while a stringified version of the watch list is returned in <code>msg.topic</code>.</p>
  3745.         <p><code>msg.file</code> contains just the short filename of the file that changed.
  3746.         <code>msg.type</code> has the type of thing changed, usually <i>file</i> or <i>directory</i>,
  3747.         while <code>msg.size</code> holds the file size in bytes.</p>
  3748.         <p>Of course in Linux, <i>everything</i> is a file and thus can be watched...</p>
  3749.         <p><b>Note: </b>The directory or file must exist in order to be watched. If the file
  3750.         or directory gets deleted it may no longer be monitored even if it gets re-created.</p>
  3751.     </script><!--
  3752.       Copyright JS Foundation and other contributors, http://js.foundation
  3753.      
  3754.       Licensed under the Apache License, Version 2.0 (the "License");
  3755.       you may not use this file except in compliance with the License.
  3756.       You may obtain a copy of the License at
  3757.      
  3758.       http://www.apache.org/licenses/LICENSE-2.0
  3759.      
  3760.       Unless required by applicable law or agreed to in writing, software
  3761.       distributed under the License is distributed on an "AS IS" BASIS,
  3762.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3763.       See the License for the specific language governing permissions and
  3764.       limitations under the License.
  3765.     -->
  3766.      
  3767.     <script type="text/x-red" data-template-name="tcp in">
  3768.         <div class="form-row">
  3769.             <label for="node-input-server"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
  3770.             <select id="node-input-server" style="width:120px; margin-right:5px;">
  3771.                 <option value="server" data-i18n="tcpin.type.listen"></option>
  3772.                 <option value="client" data-i18n="tcpin.type.connect"></option>
  3773.             </select>
  3774.             <span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width: 65px">
  3775.         </div>
  3776.         <div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
  3777.             <span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
  3778.         </div>
  3779.      
  3780.         <div class="form-row">
  3781.             <label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
  3782.             <select id="node-input-datamode" style="width:110px;">
  3783.                 <option value="stream" data-i18n="tcpin.output.stream"></option>
  3784.                 <option value="single" data-i18n="tcpin.output.single"></option>
  3785.             </select>
  3786.             <select id="node-input-datatype" style="width:140px;">
  3787.                 <option value="buffer" data-i18n="tcpin.output.buffer"></option>
  3788.                 <option value="utf8" data-i18n="tcpin.output.string"></option>
  3789.                 <option value="base64" data-i18n="tcpin.output.base64"></option>
  3790.             </select>
  3791.             <span data-i18n="tcpin.label.payload"></span>
  3792.         </div>
  3793.      
  3794.         <div id="node-row-newline" class="form-row hidden" style="padding-left: 110px;">
  3795.             <span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width: 110px;">
  3796.         </div>
  3797.      
  3798.         <div class="form-row">
  3799.             <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
  3800.             <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
  3801.         </div>
  3802.         <div class="form-row">
  3803.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3804.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3805.         </div>
  3806.     </script>
  3807.      
  3808.      
  3809.      
  3810.     <script type="text/javascript">
  3811.         RED.nodes.registerType('tcp in',{
  3812.             category: 'input',
  3813.             color:"Silver",
  3814.             defaults: {
  3815.                 name: {value:""},
  3816.                 server: {value:"server",required:true},
  3817.                 host: {value:"",validate:function(v) { return (this.server == "server")||v.length > 0;} },
  3818.                 port: {value:"",required:true,validate:RED.validators.number()},
  3819.                 datamode:{value:"stream"},
  3820.                 datatype:{value:"buffer"},
  3821.                 newline:{value:""},
  3822.                 topic: {value:""},
  3823.                 base64: {/*deprecated*/ value:false,required:true}
  3824.             },
  3825.             inputs:0,
  3826.             outputs:1,
  3827.             icon: "bridge-dash.png",
  3828.             label: function() {
  3829.                 return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
  3830.             },
  3831.             labelStyle: function() {
  3832.                 return this.name?"node_label_italic":"";
  3833.             },
  3834.             oneditprepare: function() {
  3835.                 var updateOptions = function() {
  3836.                     var sockettype = $("#node-input-server").val();
  3837.                     if (sockettype == "client") {
  3838.                         $("#node-input-host-row").show();
  3839.                     } else {
  3840.                         $("#node-input-host-row").hide();
  3841.                     }
  3842.                     var datamode = $("#node-input-datamode").val();
  3843.                     var datatype = $("#node-input-datatype").val();
  3844.                     if (datamode == "stream") {
  3845.                         if (datatype == "utf8") {
  3846.                             $("#node-row-newline").show();
  3847.                         } else {
  3848.                             $("#node-row-newline").hide();
  3849.                         }
  3850.                     } else {
  3851.                         $("#node-row-newline").hide();
  3852.                     }
  3853.                 };
  3854.                 updateOptions();
  3855.                 $("#node-input-server").change(updateOptions);
  3856.                 $("#node-input-datatype").change(updateOptions);
  3857.                 $("#node-input-datamode").change(updateOptions);
  3858.             }
  3859.         });
  3860.     </script>
  3861.      
  3862.      
  3863.     <script type="text/x-red" data-template-name="tcp out">
  3864.         <div class="form-row">
  3865.             <label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
  3866.             <select id="node-input-beserver" style="width:150px; margin-right:5px;">
  3867.                 <option value="server" data-i18n="tcpin.type.listen"></option>
  3868.                 <option value="client" data-i18n="tcpin.type.connect"></option>
  3869.                 <option value="reply" data-i18n="tcpin.type.reply"></option>
  3870.             </select>
  3871.             <span id="node-input-port-row"><span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width: 65px"></span>
  3872.         </div>
  3873.      
  3874.         <div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
  3875.             <span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" style="width: 60%;">
  3876.         </div>
  3877.      
  3878.         <div class="form-row hidden" id="node-input-end-row">
  3879.             <label>&nbsp;</label>
  3880.             <input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;">
  3881.             <label for="node-input-end" style="width: 70%;"><span data-i18n="tcpin.label.close-connection"></span></label>
  3882.         </div>
  3883.      
  3884.         <div class="form-row">
  3885.             <label>&nbsp;</label>
  3886.             <input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
  3887.             <label for="node-input-base64" style="width: 70%;"><span data-i18n="tcpin.label.decode-base64"></span></label>
  3888.         </div>
  3889.      
  3890.         <div class="form-row">
  3891.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3892.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3893.         </div>
  3894.     </script>
  3895.      
  3896.      
  3897.      
  3898.     <script type="text/javascript">
  3899.         RED.nodes.registerType('tcp out',{
  3900.             category: 'output',
  3901.             color:"Silver",
  3902.             defaults: {
  3903.                 host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} },
  3904.                 port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v) } },
  3905.                 beserver: {value:"client",required:true},
  3906.                 base64: {value:false,required:true},
  3907.                 end: {value:false,required:true},
  3908.                 name: {value:""}
  3909.             },
  3910.             inputs:1,
  3911.             outputs:0,
  3912.             icon: "bridge-dash.png",
  3913.             align: "right",
  3914.             label: function() {
  3915.                 return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
  3916.             },
  3917.             labelStyle: function() {
  3918.                 return (this.name)?"node_label_italic":"";
  3919.             },
  3920.             oneditprepare: function() {
  3921.                 var updateOptions = function() {
  3922.                     var sockettype = $("#node-input-beserver").val();
  3923.                     if (sockettype == "reply") {
  3924.                         $("#node-input-port-row").hide();
  3925.                         $("#node-input-host-row").hide();
  3926.                         $("#node-input-end-row").hide();
  3927.                     } else {
  3928.                         $("#node-input-port-row").show();
  3929.                         $("#node-input-end-row").show();
  3930.                     }
  3931.      
  3932.                     if (sockettype == "client") {
  3933.                         $("#node-input-host-row").show();
  3934.                     } else {
  3935.                         $("#node-input-host-row").hide();
  3936.                     }
  3937.                 };
  3938.                 updateOptions();
  3939.                 $("#node-input-beserver").change(updateOptions);
  3940.             }
  3941.         });
  3942.     </script>
  3943.      
  3944.      
  3945.     <script type="text/x-red" data-template-name="tcp request">
  3946.         <div class="form-row">
  3947.             <label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label>
  3948.             <input type="text" id="node-input-server" placeholder="ip.address" style="width:45%">
  3949.             &nbsp;port <input type="text" id="node-input-port" style="width:60px">
  3950.         </div>
  3951.         <div class="form-row">
  3952.             <label for="node-input-out"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label>
  3953.             <select type="text" id="node-input-out" style="width:56%;">
  3954.                 <option value="time" data-i18n="tcpin.return.timeout"></option>
  3955.                 <option value="char" data-i18n="tcpin.return.character"></option>
  3956.                 <option value="count" data-i18n="tcpin.return.number"></option>
  3957.                 <option value="sit" data-i18n="tcpin.return.never"></option>
  3958.             </select>
  3959.             <input type="text" id="node-input-splitc"  style="width:50px;">
  3960.             <span id="node-units"></span>
  3961.         </div>
  3962.         <div class="form-row">
  3963.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  3964.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  3965.         </div>
  3966.     </script>
  3967.      
  3968.      
  3969.      
  3970.     <script type="text/javascript">
  3971.         RED.nodes.registerType('tcp request',{
  3972.             category: 'function',
  3973.             color:"Silver",
  3974.             defaults: {
  3975.                 server: {value:""},
  3976.                 port: {value:"",validate:RED.validators.regex(/^(\d*|)$/)},
  3977.                 out: {value:"time",required:true},
  3978.                 splitc: {value:"0",required:true},
  3979.                 name: {value:""}
  3980.             },
  3981.             inputs:1,
  3982.             outputs:1,
  3983.             icon: "bridge-dash.png",
  3984.             label: function() {
  3985.                 return this.name || "tcp:"+(this.server?this.server+":":"")+this.port;
  3986.             },
  3987.             labelStyle: function() {
  3988.                 return this.name?"node_label_italic":"";
  3989.             },
  3990.             oneditprepare: function() {
  3991.                 var previous = null;
  3992.                 $("#node-input-out").on('focus', function () { previous = this.value; }).change(function() {
  3993.                     if (previous == null) { previous = $("#node-input-out").val(); }
  3994.                     if ($("#node-input-out").val() == "char") {
  3995.                         if (previous != "char") $("#node-input-splitc").val("\\n");
  3996.                         $("#node-units").text("");
  3997.                     }
  3998.                     else if ($("#node-input-out").val() == "time") {
  3999.                         if (previous != "time") $("#node-input-splitc").val("0");
  4000.                         $("#node-units").text("ms");
  4001.                     }
  4002.                     else if ($("#node-input-out").val() == "count") {
  4003.                         if (previous != "count") $("#node-input-splitc").val("12");
  4004.                         $("#node-units").text("chars");
  4005.                     }
  4006.                     else {
  4007.                         if (previous != "sit") $("#node-input-splitc").val("0");
  4008.                         $("#node-units").text("");
  4009.                     }
  4010.                 });
  4011.             }
  4012.         });
  4013.     </script>
  4014.     <script type="text/x-red" data-help-name="tcp in">
  4015.         <p>Provides a choice of TCP inputs. Can either connect to a remote TCP port,
  4016.            or accept incoming connections.</p>
  4017.     </script><script type="text/x-red" data-help-name="tcp out">
  4018.         <p>Provides a choice of TCP outputs. Can either connect to a remote TCP port,
  4019.         accept incoming connections, or reply to messages received from a TCP In node.</p>
  4020.         <p>Only <code>msg.payload</code> is sent.</p>
  4021.         <p>If <code>msg.payload</code> is a string containing a Base64 encoding of binary
  4022.         data, the Base64 decoding option will cause it to be converted back to binary
  4023.         before being sent.</p>
  4024.         <p>If <code>msg._session</code> is not present the payload is
  4025.         sent to <b>all</b> connected clients.</p>
  4026.     </script><script type="text/x-red" data-help-name="tcp request">
  4027.         <p>A simple TCP request node - sends the <code>msg.payload</code> to a server tcp port and expects a response.</p>
  4028.         <p>Connects, sends the "request", and reads the "response". It can either count a number of
  4029.         returned characters into a fixed buffer, match a specified character before returning,
  4030.         wait a fixed timeout from first reply and then return, or just sit and wait for data.</p>
  4031.         <p>The response will be output in <code>msg.payload</code> as a buffer, so you may want to .toString() it.</p>
  4032.         <p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties.</p>
  4033.     </script><!--
  4034.       Copyright JS Foundation and other contributors, http://js.foundation
  4035.      
  4036.       Licensed under the Apache License, Version 2.0 (the "License");
  4037.       you may not use this file except in compliance with the License.
  4038.       You may obtain a copy of the License at
  4039.      
  4040.       http://www.apache.org/licenses/LICENSE-2.0
  4041.      
  4042.       Unless required by applicable law or agreed to in writing, software
  4043.       distributed under the License is distributed on an "AS IS" BASIS,
  4044.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4045.       See the License for the specific language governing permissions and
  4046.       limitations under the License.
  4047.     -->
  4048.      
  4049.     <!--  The Input Node  -->
  4050.     <script type="text/x-red" data-template-name="udp in">
  4051.         <div class="form-row">
  4052.             <label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label>
  4053.             <select id="node-input-multicast" style='width:70%'>
  4054.               <option value="false" data-i18n="udp.udpmsgs"></option>
  4055.               <option value="true" data-i18n="udp.mcmsgs"></option>
  4056.             </select>
  4057.         </div>
  4058.         <div class="form-row node-input-group">
  4059.             <label for="node-input-group"><i class="fa fa-list"></i> <span data-i18n="udp.label.group"></span></label>
  4060.             <input type="text" id="node-input-group" placeholder="225.0.18.83">
  4061.         </div>
  4062.         <div class="form-row node-input-iface">
  4063.             <label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
  4064.             <input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceprompt">
  4065.         </div>
  4066.         <div class="form-row">
  4067.             <label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>
  4068.             <input type="text" id="node-input-port" style="width:80px">
  4069.             &nbsp;&nbsp;<span data-i18n="udp.label.using"></span> <select id="node-input-ipv" style="width:80px">
  4070.               <option value="udp4">ipv4</option>
  4071.               <option value="udp6">ipv6</option>
  4072.             </select>
  4073.         </div>
  4074.         <div class="form-row">
  4075.             <label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="udp.label.output"></span></label>
  4076.             <select id="node-input-datatype" style="width:70%;">
  4077.                 <option value="buffer" data-i18n="udp.output.buffer"></option>
  4078.                 <option value="utf8" data-i18n="udp.output.string"></option>
  4079.                 <option value="base64" data-i18n="udp.output.base64"></option>
  4080.             </select>
  4081.         </div>
  4082.         <div class="form-row">
  4083.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  4084.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  4085.         </div>
  4086.         <div class="form-tips"><span data-i18n="udp.tip.in"></span></div>
  4087.         <div class="form-tips" id="udpporttip"><span data-i18n="[html]udp.tip.port"></span></div>
  4088.     </script>
  4089.      
  4090.      
  4091.      
  4092.     <script type="text/javascript">
  4093.         RED.nodes.registerType('udp in',{
  4094.             category: 'input',
  4095.             color:"Silver",
  4096.             defaults: {
  4097.                 name: {value:""},
  4098.                 iface: {value:""},
  4099.                 port: {value:"",required:true,validate:RED.validators.number()},
  4100.                 ipv: {value:"udp4"},
  4101.                 multicast: {value:"false"},
  4102.                 group: {value:"",validate:function(v) { return (this.multicast !== "true")||v.length > 0;} },
  4103.                 datatype: {value:"buffer",required:true}
  4104.             },
  4105.             inputs:0,
  4106.             outputs:1,
  4107.             icon: "bridge-dash.png",
  4108.             label: function() {
  4109.                 if (this.multicast=="false") {
  4110.                     return this.name||"udp "+this.port;
  4111.                 }
  4112.                 else {
  4113.                     return this.name||"udp "+(this.group+":"+this.port);
  4114.                 }
  4115.             },
  4116.             labelStyle: function() {
  4117.                 return this.name?"node_label_italic":"";
  4118.             },
  4119.             oneditprepare: function() {
  4120.                 $("#node-input-multicast").change(function() {
  4121.                     var id = $("#node-input-multicast").val();
  4122.                     if (id == "false") {
  4123.                         $(".node-input-group").hide();
  4124.                         $(".node-input-iface").hide();
  4125.                     }
  4126.                     else {
  4127.                         $(".node-input-group").show();
  4128.                         $(".node-input-iface").show();
  4129.                     }
  4130.                 });
  4131.                 $("#node-input-multicast").change();
  4132.      
  4133.                 var porttip = this._("udp.tip.port");
  4134.                 var alreadyused = this._("udp.errors.alreadyused");
  4135.                 var portsInUse = {};
  4136.                 $.getJSON('udp-ports/'+this.id,function(data) {
  4137.                     portsInUse = data || {};
  4138.                     $('#udpporttip').html(porttip + data);
  4139.                 });
  4140.                 $("#node-input-port").change(function() {
  4141.                     var portnew = $("#node-input-port").val();
  4142.                     if (portsInUse.hasOwnProperty($("#node-input-port").val())) {
  4143.                         RED.notify(alreadyused+" "+$("#node-input-port").val(),"warn");
  4144.                     }
  4145.                 });
  4146.             }
  4147.         });
  4148.     </script>
  4149.      
  4150.      
  4151.     <!--  The Output Node  -->
  4152.     <script type="text/x-red" data-template-name="udp out">
  4153.         <div class="form-row">
  4154.             <label for="node-input-port"><i class="fa fa-envelope"></i> <span data-i18n="udp.label.send"></span></label>
  4155.             <select id="node-input-multicast" style="width:40%">
  4156.               <option value="false" data-i18n="udp.udpmsg"></option>
  4157.               <option value="broad" data-i18n="udp.bcmsg"></option>
  4158.               <option value="multi" data-i18n="udp.mcmsg"></option>
  4159.             </select>
  4160.             <span data-i18n="udp.label.toport"></span> <input type="text" id="node-input-port" style="width:70px">
  4161.         </div>
  4162.         <div class="form-row node-input-addr">
  4163.             <label for="node-input-addr" id="node-input-addr-label"><i class="fa fa-list"></i> <span data-i18n="udp.label.address"></span></label>
  4164.             <input type="text" id="node-input-addr" data-i18n="[placeholder]udp.placeholder.address" style="width:50%;">
  4165.             <select id="node-input-ipv" style="width:70px">
  4166.               <option value="udp4">ipv4</option>
  4167.               <option value="udp6">ipv6</option>
  4168.             </select>
  4169.         </div>
  4170.         <div class="form-row node-input-iface">
  4171.             <label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
  4172.             <input type="text" id="node-input-iface" data-i18n="[placeholder]udp.placeholder.interface">
  4173.         </div>
  4174.         <div class="form-row">
  4175.             <label for="node-input-outport-type">&nbsp;</label>
  4176.             <select id="node-input-outport-type">
  4177.               <option id="node-input-outport-type-random" value="random" data-i18n="udp.bind.random"></option>
  4178.               <option value="fixed" data-i18n="udp.bind.local"></option>
  4179.             </select>
  4180.             <input type="text" id="node-input-outport" style="width:70px;">
  4181.         </div>
  4182.         <div class="form-row">
  4183.             <label>&nbsp;</label>
  4184.             <input type="checkbox" id="node-input-base64" style="display:inline-block; width:auto; vertical-align:top;">
  4185.             <label for="node-input-base64" style="width:70%;"><span data-i18n="udp.label.decode-base64"></span></label>
  4186.         </div>
  4187.         <div class="form-row">
  4188.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  4189.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  4190.         </div>
  4191.         <div class="form-tips"><span data-i18n="[html]udp.tip.out"></span></div>
  4192.     </script>
  4193.      
  4194.      
  4195.      
  4196.     <script type="text/javascript">
  4197.         RED.nodes.registerType('udp out',{
  4198.             category: 'output',
  4199.             color:"Silver",
  4200.             defaults: {
  4201.                 name: {value:""},
  4202.                 addr: {value:""},
  4203.                 iface: {value:""},
  4204.                 port: {value:""},
  4205.                 ipv: {value:"udp4"},
  4206.                 outport: {value:""},
  4207.                 base64: {value:false,required:true},
  4208.                 multicast: {value:"false"}
  4209.             },
  4210.             inputs:1,
  4211.             outputs:0,
  4212.             icon: "bridge-dash.png",
  4213.             align: "right",
  4214.             label: function() {
  4215.                 return this.name||"udp "+(this.addr+":"+this.port);
  4216.             },
  4217.             labelStyle: function() {
  4218.                 return this.name?"node_label_italic":"";
  4219.             },
  4220.             oneditprepare: function() {
  4221.                 var addresslabel = this._("udp.label.address");
  4222.                 var addressph = this._("udp.placeholder.address");
  4223.                 var grouplabel = this._("udp.label.group");
  4224.                 var bindrandom = this._("udp.bind.random");
  4225.                 var bindtarget = this._("udp.bind.target");
  4226.      
  4227.                 var type = this.outport===""?"random":"fixed";
  4228.                 $("#node-input-outport-type").val(type);
  4229.      
  4230.                 $("#node-input-outport-type").change(function() {
  4231.                     var type = $(this).val();
  4232.                     if (type == "random") {
  4233.                         $("#node-input-outport").val("").hide();
  4234.                     } else {
  4235.                         $("#node-input-outport").show();
  4236.                     }
  4237.                 });
  4238.                 $("#node-input-outport-type").change();
  4239.      
  4240.                 $("#node-input-multicast").change(function() {
  4241.                     var id = $("#node-input-multicast").val();
  4242.                     if (id === "multi") {
  4243.                         $(".node-input-iface").show();
  4244.                         $("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + grouplabel);
  4245.                         $("#node-input-addr")[0].placeholder = '225.0.18.83';
  4246.                     }
  4247.                     else if (id === "broad") {
  4248.                         $(".node-input-iface").hide();
  4249.                         $("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + addresslabel);
  4250.                         $("#node-input-addr")[0].placeholder = '255.255.255.255';
  4251.                     }
  4252.                     else {
  4253.                         $(".node-input-iface").hide();
  4254.                         $("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + addresslabel);
  4255.                         $("#node-input-addr")[0].placeholder = addressph;
  4256.                     }
  4257.                     var type = $(this).val();
  4258.                     if (type == "false") {
  4259.                         $("#node-input-outport-type-random").html(bindrandom);
  4260.                     } else {
  4261.                         $("#node-input-outport-type-random").html(bindtarget);
  4262.                     }
  4263.                 });
  4264.                 $("#node-input-multicast").change();
  4265.             }
  4266.         });
  4267.     </script>
  4268.     <script type="text/x-red" data-help-name="udp in">
  4269.         <p>A UDP input node, that produces a <code>msg.payload</code> containing a
  4270.         Buffer, string, or base64 encoded string. Supports multicast.</p>
  4271.         <p>It also provides <code>msg.ip</code> and <code>msg.port</code> set to the
  4272.         ip address and port from which the message was received.</p>
  4273.         <p><b>Note</b>: On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
  4274.     </script><script type="text/x-red" data-help-name="udp out">
  4275.         <p>This node sends <code>msg.payload</code> to the designated UDP host and port. Supports multicast.</p>
  4276.         <p>You may also use <code>msg.ip</code> and <code>msg.port</code> to set the destination values, but the statically configured values have precedence.</p>
  4277.         <p>If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.</p>
  4278.         <p><b>Note</b>: On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
  4279.     </script><!--
  4280.       Copyright JS Foundation and other contributors, http://js.foundation
  4281.      
  4282.       Licensed under the Apache License, Version 2.0 (the "License");
  4283.       you may not use this file except in compliance with the License.
  4284.       You may obtain a copy of the License at
  4285.      
  4286.       http://www.apache.org/licenses/LICENSE-2.0
  4287.      
  4288.       Unless required by applicable law or agreed to in writing, software
  4289.       distributed under the License is distributed on an "AS IS" BASIS,
  4290.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4291.       See the License for the specific language governing permissions and
  4292.       limitations under the License.
  4293.     -->
  4294.      
  4295.     <script type="text/x-red" data-template-name="switch">
  4296.         <div class="form-row">
  4297.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  4298.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  4299.         </div>
  4300.         <div class="form-row">
  4301.             <label data-i18n="switch.label.property"></label>
  4302.             <input type="text" id="node-input-property" style="width: 70%"/>
  4303.         </div>
  4304.         <div class="form-row node-input-rule-container-row">
  4305.             <ol id="node-input-rule-container"></ol>
  4306.         </div>
  4307.         <div class="form-row">
  4308.             <select id="node-input-checkall" style="width:100%; margin-right:5px;">
  4309.                 <option value="true" data-i18n="switch.checkall"></option>
  4310.                 <option value="false" data-i18n="switch.stopfirst"></option>
  4311.             </select>
  4312.         </div>
  4313.     </script>
  4314.      
  4315.      
  4316.      
  4317.     <script type="text/javascript">
  4318.         RED.nodes.registerType('switch', {
  4319.             color: "#E2D96E",
  4320.             category: 'function',
  4321.             defaults: {
  4322.                 name: {value:""},
  4323.                 property: {value:"payload", required:true, validate: RED.validators.typedInput("propertyType")},
  4324.                 propertyType: { value:"msg" },
  4325.                 rules: {value:[{t:"eq", v:""}]},
  4326.                 checkall: {value:"true", required:true},
  4327.                 outputs: {value:1}
  4328.             },
  4329.             inputs: 1,
  4330.             outputs: 1,
  4331.             icon: "switch.png",
  4332.             label: function() {
  4333.                 return this.name||"switch";
  4334.             },
  4335.             oneditprepare: function() {
  4336.                 var node = this;
  4337.                 var previousValueType = {value:"prev",label:this._("inject.previous"),hasValue:false};
  4338.      
  4339.                 $("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global','jsonata']});
  4340.                 var operators = [
  4341.                     {v:"eq",t:"=="},
  4342.                     {v:"neq",t:"!="},
  4343.                     {v:"lt",t:"<"},
  4344.                     {v:"lte",t:"<="},
  4345.                     {v:"gt",t:">"},
  4346.                     {v:"gte",t:">="},
  4347.                     {v:"btwn",t:this._("switch.rules.btwn")},
  4348.                     {v:"cont",t:this._("switch.rules.cont")},
  4349.                     {v:"regex",t:this._("switch.rules.regex")},
  4350.                     {v:"true",t:this._("switch.rules.true")},
  4351.                     {v:"false",t:this._("switch.rules.false")},
  4352.                     {v:"null",t:this._("switch.rules.null")},
  4353.                     {v:"nnull",t:this._("switch.rules.nnull")},
  4354.                     {v:"else",t:this._("switch.rules.else")}
  4355.                 ];
  4356.      
  4357.                 var andLabel = this._("switch.and");
  4358.                 var caseLabel = this._("switch.ignorecase");
  4359.      
  4360.                 function resizeRule(rule) {
  4361.                     var newWidth = rule.width();
  4362.                     var selectField = rule.find("select");
  4363.                     var type = selectField.val()||"";
  4364.                     var valueField = rule.find(".node-input-rule-value");
  4365.                     var btwnField1 = rule.find(".node-input-rule-btwn-value");
  4366.                     var btwnField2 = rule.find(".node-input-rule-btwn-value2");
  4367.                     var selectWidth;
  4368.                     if (type.length < 4) {
  4369.                         selectWidth = 60;
  4370.                     } else if (type === "regex") {
  4371.                         selectWidth = 147;
  4372.                     } else {
  4373.                         selectWidth = 120;
  4374.                     }
  4375.                     selectField.width(selectWidth);
  4376.                     if (type === "btwn") {
  4377.                         btwnField1.typedInput("width",(newWidth-selectWidth-70));
  4378.                         btwnField2.typedInput("width",(newWidth-selectWidth-70));
  4379.                     } else {
  4380.                         if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
  4381.                             // valueField.hide();
  4382.                         } else {
  4383.                             valueField.typedInput("width",(newWidth-selectWidth-70));
  4384.                         }
  4385.                     }
  4386.                 }
  4387.      
  4388.                 $("#node-input-rule-container").css('min-height','250px').css('min-width','450px').editableList({
  4389.                     addItem: function(container,i,opt) {
  4390.                         if (!opt.hasOwnProperty('r')) {
  4391.                             opt.r = {};
  4392.                         }
  4393.                         var rule = opt.r;
  4394.                         if (!rule.hasOwnProperty('t')) {
  4395.                             rule.t = 'eq';
  4396.                         }
  4397.                         var row = $('<div/>').appendTo(container);
  4398.                         var row2 = $('<div/>',{style:"padding-top: 5px; padding-left: 175px;"}).appendTo(container);
  4399.                         var row3 = $('<div/>',{style:"padding-top: 5px; padding-left: 102px;"}).appendTo(container);
  4400.                         var selectField = $('<select/>',{style:"width:120px; margin-left: 5px; text-align: center;"}).appendTo(row);
  4401.                         for (var d in operators) {
  4402.                             selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t));
  4403.                         }
  4404.                         var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata',previousValueType]});
  4405.                         var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
  4406.                         var btwnAndLabel = $('<span/>',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3);
  4407.                         var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
  4408.                         var finalspan = $('<span/>',{style:"float: right;margin-top: 6px;"}).appendTo(row);
  4409.                         finalspan.append(' &#8594; <span class="node-input-rule-index">'+(i+1)+'</span> ');
  4410.                         var caseSensitive = $('<input/>',{id:"node-input-rule-case-"+i,class:"node-input-rule-case",type:"checkbox",style:"width:auto;vertical-align:top"}).appendTo(row2);
  4411.                         $('<label/>',{for:"node-input-rule-case-"+i,style:"margin-left: 3px;"}).text(caseLabel).appendTo(row2);
  4412.                         selectField.change(function() {
  4413.                             resizeRule(container);
  4414.                             var type = selectField.val();
  4415.                             if (type === "btwn") {
  4416.                                 valueField.typedInput('hide');
  4417.                                 btwnValueField.typedInput('show');
  4418.                             } else {
  4419.                                 btwnValueField.typedInput('hide');
  4420.                                 if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
  4421.                                     valueField.typedInput('hide');
  4422.                                 } else {
  4423.                                     valueField.typedInput('show');
  4424.                                 }
  4425.                             }
  4426.                             if (type === "regex") {
  4427.                                 row2.show();
  4428.                                 row3.hide();
  4429.                             } else if (type === "btwn"){
  4430.                                 row2.hide();
  4431.                                 row3.show();
  4432.                             } else {
  4433.                                 row2.hide();
  4434.                                 row3.hide();
  4435.                             }
  4436.                         });
  4437.                         selectField.val(rule.t);
  4438.                         if (rule.t == "btwn") {
  4439.                             btwnValueField.typedInput('value',rule.v);
  4440.                             btwnValueField.typedInput('type',rule.vt||'num');
  4441.                             btwnValue2Field.typedInput('value',rule.v2);
  4442.                             btwnValue2Field.typedInput('type',rule.v2t||'num');
  4443.                         } else if (typeof rule.v != "undefined") {
  4444.                             valueField.typedInput('value',rule.v);
  4445.                             valueField.typedInput('type',rule.vt||'str');
  4446.                         }
  4447.                         if (rule.case) {
  4448.                             caseSensitive.prop('checked',true);
  4449.                         } else {
  4450.                             caseSensitive.prop('checked',false);
  4451.                         }
  4452.                         selectField.change();
  4453.                     },
  4454.                     removeItem: function(opt) {
  4455.                         if (opt.hasOwnProperty('i')) {
  4456.                             var removedList = $("#node-input-rule-container").data('removedList')||[];
  4457.                             removedList.push(opt.i);
  4458.                             $("#node-input-rule-container").data('removedList',removedList);
  4459.                         }
  4460.      
  4461.                         var rules = $("#node-input-rule-container").editableList('items');
  4462.                         rules.each(function(i) { $(this).find(".node-input-rule-index").html(i+1); });
  4463.                     },
  4464.                     resizeItem: resizeRule,
  4465.                     sortItems: function(rules) {
  4466.                         var rules = $("#node-input-rule-container").editableList('items');
  4467.                         rules.each(function(i) { $(this).find(".node-input-rule-index").html(i+1); });
  4468.                     },
  4469.                     sortable: true,
  4470.                     removable: true
  4471.                 });
  4472.      
  4473.                 for (var i=0;i<this.rules.length;i++) {
  4474.                     var rule = this.rules[i];
  4475.                     $("#node-input-rule-container").editableList('addItem',{r:rule,i:i});
  4476.                 }
  4477.             },
  4478.             oneditsave: function() {
  4479.                 var rules = $("#node-input-rule-container").editableList('items');
  4480.                 var ruleset;
  4481.                 var node = this;
  4482.                 node.rules = [];
  4483.                 var changedOutputs = {};
  4484.                 var removedList = $("#node-input-rule-container").data('removedList')||[];
  4485.                 removedList.forEach(function(i) {
  4486.                     changedOutputs[i] = -1;
  4487.                 });
  4488.                 rules.each(function(i) {
  4489.                     var ruleData = $(this).data('data');
  4490.                     var rule = $(this);
  4491.                     var type = rule.find("select").val();
  4492.                     var r = {t:type};
  4493.                     if (!(type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else")) {
  4494.                         if (type === "btwn") {
  4495.                             r.v = rule.find(".node-input-rule-btwn-value").typedInput('value');
  4496.                             r.vt = rule.find(".node-input-rule-btwn-value").typedInput('type');
  4497.                             r.v2 = rule.find(".node-input-rule-btwn-value2").typedInput('value');
  4498.                             r.v2t = rule.find(".node-input-rule-btwn-value2").typedInput('type');
  4499.                         } else {
  4500.                             r.v = rule.find(".node-input-rule-value").typedInput('value');
  4501.                             r.vt = rule.find(".node-input-rule-value").typedInput('type');
  4502.                         }
  4503.                         if (type === "regex") {
  4504.                             r.case = rule.find(".node-input-rule-case").prop("checked");
  4505.                         }
  4506.                     }
  4507.                     if (ruleData.hasOwnProperty('i')) {
  4508.                         if (ruleData.i !== i) {
  4509.                             changedOutputs[ruleData.i] = i;
  4510.                         }
  4511.                     }
  4512.                     node.rules.push(r);
  4513.                 });
  4514.                 this._outputs = changedOutputs;
  4515.                 this.outputs = node.rules.length;
  4516.                 this.propertyType = $("#node-input-property").typedInput('type');
  4517.             },
  4518.             oneditresize: function(size) {
  4519.                 var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
  4520.                 var height = size.height;
  4521.                 for (var i=0;i<rows.size();i++) {
  4522.                     height -= $(rows[i]).outerHeight(true);
  4523.                 }
  4524.                 var editorRow = $("#dialog-form>div.node-input-rule-container-row");
  4525.                 height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  4526.                 $("#node-input-rule-container").editableList('height',height);
  4527.             }
  4528.         });
  4529.     </script>
  4530.     <script type="text/x-red" data-help-name="switch">
  4531.         <p>A node to route messages based on property values.</p>
  4532.         <p>When a message arrives, the selected property is evaluated against each
  4533.         of the defined rules. The message is then sent to the output of <i>all</i>
  4534.         rules that pass.</p>
  4535.         <p><b>Note</b>: the <i>otherwise</i> rule applies as a "not any of" the rules preceding it.</p>
  4536.     </script><!--
  4537.       Copyright JS Foundation and other contributors, http://js.foundation
  4538.      
  4539.       Licensed under the Apache License, Version 2.0 (the "License");
  4540.       you may not use this file except in compliance with the License.
  4541.       You may obtain a copy of the License at
  4542.      
  4543.       http://www.apache.org/licenses/LICENSE-2.0
  4544.      
  4545.       Unless required by applicable law or agreed to in writing, software
  4546.       distributed under the License is distributed on an "AS IS" BASIS,
  4547.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4548.       See the License for the specific language governing permissions and
  4549.       limitations under the License.
  4550.     -->
  4551.      
  4552.     <script type="text/x-red" data-template-name="change">
  4553.         <div class="form-row">
  4554.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  4555.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  4556.         </div>
  4557.         <div class="form-row" style="margin-bottom:0;">
  4558.             <label><i class="fa fa-list"></i> <span data-i18n="change.label.rules"></span></label>
  4559.         </div>
  4560.         <div class="form-row node-input-rule-container-row">
  4561.             <ol id="node-input-rule-container"></ol>
  4562.         </div>
  4563.     </script>
  4564.      
  4565.      
  4566.      
  4567.     <script type="text/javascript">
  4568.         RED.nodes.registerType('change', {
  4569.             color: "#E2D96E",
  4570.             category: 'function',
  4571.             defaults: {
  4572.                 name: {value:""},
  4573.                 rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}]},
  4574.                 // legacy
  4575.                 action: {value:""},
  4576.                 property: {value:""},
  4577.                 from: {value:""},
  4578.                 to: {value:""},
  4579.                 reg: {value:false}
  4580.             },
  4581.             inputs: 1,
  4582.             outputs: 1,
  4583.             icon: "swap.png",
  4584.             label: function() {
  4585.                 if (this.name) {
  4586.                     return this.name;
  4587.                 }
  4588.                 if (!this.rules) {
  4589.                     if (this.action === "replace") {
  4590.                         return this._("change.label.set",{property:"msg."+this.property});
  4591.                     } else if (this.action === "change") {
  4592.                         return this._("change.label.change",{property:"msg."+this.property});
  4593.                     } else if (this.action === "move") {
  4594.                         return this._("change.label.move",{property:"msg."+this.property});
  4595.                     } else {
  4596.                         return this._("change.label.delete",{property:"msg."+this.property});
  4597.                     }
  4598.                 } else {
  4599.                     if (this.rules.length == 1) {
  4600.                         if (this.rules[0].t === "set") {
  4601.                             return this._("change.label.set",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
  4602.                         } else if (this.rules[0].t === "change") {
  4603.                             return this._("change.label.change",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
  4604.                         } else if (this.rules[0].t === "move") {
  4605.                             return this._("change.label.move",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
  4606.                         } else {
  4607.                             return this._("change.label.delete",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
  4608.                         }
  4609.                     } else {
  4610.                         return this._("change.label.changeCount",{count:this.rules.length});
  4611.                     }
  4612.                 }
  4613.             },
  4614.             labelStyle: function() {
  4615.                 return this.name ? "node_label_italic" : "";
  4616.             },
  4617.             oneditprepare: function() {
  4618.                 var set = this._("change.action.set");
  4619.                 var change = this._("change.action.change");
  4620.                 var del = this._("change.action.delete");
  4621.                 var move = this._("change.action.move");
  4622.                 var to = this._("change.action.to");
  4623.                 var search = this._("change.action.search");
  4624.                 var replace = this._("change.action.replace");
  4625.                 var regex = this._("change.label.regex");
  4626.      
  4627.                 function resizeRule(rule) {
  4628.                     var newWidth = rule.width();
  4629.                     rule.find('.red-ui-typedInput').typedInput("width",newWidth-150);
  4630.      
  4631.                 }
  4632.                 $('#node-input-rule-container').css('min-height','300px').css('min-width','450px').editableList({
  4633.                     addItem: function(container,i,opt) {
  4634.                         var rule = opt;
  4635.                         if (!rule.hasOwnProperty('t')) {
  4636.                             rule = {t:"set",p:"payload",to:"",tot:"str"};
  4637.                         }
  4638.                         if (rule.t === "change" && rule.re) {
  4639.                             rule.fromt = 're';
  4640.                             delete rule.re;
  4641.                         }
  4642.                         if (rule.t === "set" && !rule.tot) {
  4643.                             if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
  4644.                                 rule.to = rule.to.substring(4);
  4645.                                 rule.tot = "msg";
  4646.                             } else {
  4647.                                 rule.tot = "str";
  4648.                             }
  4649.                         }
  4650.                         if (rule.t === "move" && !rule.tot) {
  4651.                             rule.tot = "msg";
  4652.                         }
  4653.      
  4654.                         var row1 = $('<div/>').appendTo(container);
  4655.                         var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
  4656.                         var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
  4657.                         var row4 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
  4658.      
  4659.                         var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
  4660.                         var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del},{v:"move",l:move}];
  4661.                         for (var i=0; i<4; i++) {
  4662.                             selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l));
  4663.                         }
  4664.      
  4665.                         var propertyName = $('<input/>',{class:"node-input-rule-property-name",type:"text"})
  4666.                             .appendTo(row1)
  4667.                             .typedInput({types:['msg','flow','global']});
  4668.      
  4669.                         $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
  4670.                             .text(to)
  4671.                             .appendTo(row2);
  4672.                         var propertyValue = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
  4673.                             .appendTo(row2)
  4674.                             .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','date','jsonata']});
  4675.      
  4676.                         var row3_1 = $('<div/>').appendTo(row3);
  4677.                         $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
  4678.                             .text(search)
  4679.                             .appendTo(row3_1);
  4680.                         var fromValue = $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
  4681.                             .appendTo(row3_1)
  4682.                             .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool']});
  4683.      
  4684.                         var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
  4685.                         $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
  4686.                             .text(replace)
  4687.                             .appendTo(row3_2);
  4688.                         var toValue = $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
  4689.                             .appendTo(row3_2)
  4690.                             .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json']});
  4691.      
  4692.                         $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
  4693.                             .text(to)
  4694.                             .appendTo(row4);
  4695.                         var moveValue = $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
  4696.                             .appendTo(row4)
  4697.                             .typedInput({default:'msg',types:['msg','flow','global']});
  4698.      
  4699.                         selectField.change(function() {
  4700.                             var width = $("#node-input-rule-container").width();
  4701.                             var type = $(this).val();
  4702.                             if (type == "set") {
  4703.                                 row2.show();
  4704.                                 row3.hide();
  4705.                                 row4.hide();
  4706.                             } else if (type == "change") {
  4707.                                 row2.hide();
  4708.                                 row3.show();
  4709.                                 row4.hide();
  4710.                             } else if (type == "delete") {
  4711.                                 row2.hide();
  4712.                                 row3.hide();
  4713.                                 row4.hide();
  4714.                             } else if (type == "move") {
  4715.                                 row2.hide();
  4716.                                 row3.hide();
  4717.                                 row4.show();
  4718.                             }
  4719.                             resizeRule(container);
  4720.                         });
  4721.      
  4722.                         selectField.val(rule.t);
  4723.                         propertyName.typedInput('value',rule.p);
  4724.                         propertyName.typedInput('type',rule.pt);
  4725.                         propertyValue.typedInput('value',rule.to);
  4726.                         propertyValue.typedInput('type',rule.tot);
  4727.                         moveValue.typedInput('value',rule.to);
  4728.                         moveValue.typedInput('type',rule.tot);
  4729.                         fromValue.typedInput('value',rule.from);
  4730.                         fromValue.typedInput('type',rule.fromt);
  4731.                         toValue.typedInput('value',rule.to);
  4732.                         toValue.typedInput('type',rule.tot);
  4733.                         selectField.change();
  4734.      
  4735.                         var newWidth = $("#node-input-rule-container").width();
  4736.                         resizeRule(container);
  4737.                     },
  4738.                     resizeItem: resizeRule,
  4739.                     removable: true,
  4740.                     sortable: true
  4741.                 });
  4742.      
  4743.                 if (!this.rules) {
  4744.                     var rule = {
  4745.                         t:(this.action=="replace"?"set":this.action),
  4746.                         p:this.property,
  4747.                         pt:"msg"
  4748.                     }
  4749.      
  4750.                     if ((rule.t === "set")||(rule.t === "move")) {
  4751.                         rule.to = this.to;
  4752.                     } else if (rule.t === "change") {
  4753.                         rule.from = this.from;
  4754.                         rule.to = this.to;
  4755.                         rule.re = this.reg;
  4756.                     }
  4757.      
  4758.                     delete this.to;
  4759.                     delete this.from;
  4760.                     delete this.reg;
  4761.                     delete this.action;
  4762.                     delete this.property;
  4763.      
  4764.                     this.rules = [rule];
  4765.                 }
  4766.      
  4767.                 for (var i=0; i<this.rules.length; i++) {
  4768.                     var rule = this.rules[i];
  4769.                     $("#node-input-rule-container").editableList('addItem',rule);
  4770.                 }
  4771.             },
  4772.             oneditsave: function() {
  4773.                 var rules = $("#node-input-rule-container").editableList('items');
  4774.                 var ruleset;
  4775.                 var node = this;
  4776.                 node.rules= [];
  4777.                 rules.each(function(i) {
  4778.                     var rule = $(this);
  4779.                     var type = rule.find(".node-input-rule-type").val();
  4780.                     var r = {
  4781.                         t:type,
  4782.                         p:rule.find(".node-input-rule-property-name").typedInput('value'),
  4783.                         pt:rule.find(".node-input-rule-property-name").typedInput('type')
  4784.                     };
  4785.                     if (type === "set") {
  4786.                         r.to = rule.find(".node-input-rule-property-value").typedInput('value');
  4787.                         r.tot = rule.find(".node-input-rule-property-value").typedInput('type');
  4788.                     } else if (type === "move") {
  4789.                         r.to = rule.find(".node-input-rule-property-move-value").typedInput('value');
  4790.                         r.tot = rule.find(".node-input-rule-property-move-value").typedInput('type');
  4791.                     } else if (type === "change") {
  4792.                         r.from = rule.find(".node-input-rule-property-search-value").typedInput('value');
  4793.                         r.fromt = rule.find(".node-input-rule-property-search-value").typedInput('type');
  4794.                         r.to = rule.find(".node-input-rule-property-replace-value").typedInput('value');
  4795.                         r.tot = rule.find(".node-input-rule-property-replace-value").typedInput('type');
  4796.                     }
  4797.                     node.rules.push(r);
  4798.                 });
  4799.             },
  4800.             oneditresize: function(size) {
  4801.                 var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
  4802.                 var height = size.height;
  4803.                 for (var i=0; i<rows.size(); i++) {
  4804.                     height -= $(rows[i]).outerHeight(true);
  4805.                 }
  4806.                 var editorRow = $("#dialog-form>div.node-input-rule-container-row");
  4807.                 height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
  4808.      
  4809.                 $("#node-input-rule-container").editableList('height',height);
  4810.             }
  4811.         });
  4812.     </script>
  4813.     <script type="text/x-red" data-help-name="change">
  4814.         <p>Set, change, delete or move properties of a message, flow context or global context.</p>
  4815.         <p>The node can specify multiple rules that will be applied in turn.</p>
  4816.         <p>The available operations are:</p>
  4817.         <ul>
  4818.             <li><b>Set</b> - set a property. The value can be a variety of different types, or
  4819.                 can be taken from an existing message or context property.</li>
  4820.             <li><b>Change</b> - search &amp; replace parts of the property. If regular expressions
  4821.                 are enabled, the <b>replace with</b> property can include capture groups, for
  4822.                 example <code>$1</code>. Replace will only change the <b>type</b> if there
  4823.                 is a complete match.</li>
  4824.             <li><b>Delete</b> - delete a property.</li>
  4825.             <li><b>Move</b> - move or rename a property.</li>
  4826.         </ul>
  4827.     </script><!--
  4828.       Copyright JS Foundation and other contributors, http://js.foundation
  4829.      
  4830.       Licensed under the Apache License, Version 2.0 (the "License");
  4831.       you may not use this file except in compliance with the License.
  4832.       You may obtain a copy of the License at
  4833.      
  4834.       http://www.apache.org/licenses/LICENSE-2.0
  4835.      
  4836.       Unless required by applicable law or agreed to in writing, software
  4837.       distributed under the License is distributed on an "AS IS" BASIS,
  4838.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4839.       See the License for the specific language governing permissions and
  4840.       limitations under the License.
  4841.     -->
  4842.      
  4843.     <script type="text/x-red" data-template-name="range">
  4844.         <div class="form-row">
  4845.             <label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="range.label.action"></span></label>
  4846.             <select id="node-input-action" style="width:70%;">
  4847.                 <option value="scale" data-i18n="range.scale.payload"></option>
  4848.                 <option value="clamp" data-i18n="range.scale.limit"></option>
  4849.                 <option value="roll" data-i18n="range.scale.wrap"></option>
  4850.             </select>
  4851.         </div>
  4852.         <br/>
  4853.         <div class="form-row"><i class="fa fa-sign-in"></i> <span data-i18n="range.label.inputrange"></span>:</div>
  4854.         <div class="form-row"><label></label>
  4855.             <span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minin" data-i18n="[placeholder]range.placeholder.min" style="width:100px;"/>
  4856.             &nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxin" data-i18n="[placeholder]range.placeholder.maxin" style="width:100px;"/>
  4857.         </div>
  4858.         <div class="form-row"><i class="fa fa-sign-out"></i> <span data-i18n="range.label.resultrange"></span>:</div>
  4859.         <div class="form-row"><label></label>
  4860.             <span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minout" data-i18n="[placeholder]range.placeholder.min" style="width:100px;"/>
  4861.             &nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxout" data-i18n="[placeholder]range.placeholder.maxout" style="width:100px;"/>
  4862.         </div>
  4863.         <br/>
  4864.         <div class="form-row"><label></label>
  4865.             <input type="checkbox" id="node-input-round" style="display: inline-block; width: auto; vertical-align: top;">
  4866.             <label style="width: auto;" for="node-input-round"><span data-i18n="range.label.roundresult"></span></label></input>
  4867.         </div>
  4868.         <br/>
  4869.         <div class="form-row">
  4870.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  4871.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  4872.         </div>
  4873.         <div class="form-tips" id="node-tip"><span data-i18n="range.tip"></span></div>
  4874.     </script>
  4875.      
  4876.      
  4877.      
  4878.     <script type="text/javascript">
  4879.         RED.nodes.registerType('range', {
  4880.             color: "#E2D96E",
  4881.             category: 'function',
  4882.             defaults: {
  4883.                 minin: {value:"",required:true,validate:RED.validators.number()},
  4884.                 maxin: {value:"",required:true,validate:RED.validators.number()},
  4885.                 minout: {value:"",required:true,validate:RED.validators.number()},
  4886.                 maxout: {value:"",required:true,validate:RED.validators.number()},
  4887.                 action: {value:"scale"},
  4888.                 round: {value:false},
  4889.                 name: {value:""}
  4890.             },
  4891.             inputs: 1,
  4892.             outputs: 1,
  4893.             icon: "range.png",
  4894.             label: function() {
  4895.                 return this.name || "range";
  4896.             },
  4897.             labelStyle: function() {
  4898.                 return this.name ? "node_label_italic" : "";
  4899.             }
  4900.         });
  4901.     </script>
  4902.     <script type="text/x-red" data-help-name="range">
  4903.         <p>A simple function node to remap numeric input values to another scale.</p>
  4904.         <p>Currently only does a linear scaling.</p>
  4905.         <p><b>Note:</b> This only operates on <b>numbers</b>. Anything else will try to be made into a number and rejected if that fails.</p>
  4906.         <p><i>Scale and limit to target range</i> means that the result will never be outside the range specified within the result range.</p>
  4907.         <p><i>Scale and wrap within the target range</i> means that the result will essentially be a "modulo-style" wrap-around within the result range.</p>
  4908.     </script><!DOCTYPE html>
  4909.     <!--
  4910.       Copyright JS Foundation and other contributors, http://js.foundation
  4911.      
  4912.       Licensed under the Apache License, Version 2.0 (the "License");
  4913.       you may not use this file except in compliance with the License.
  4914.       You may obtain a copy of the License at
  4915.      
  4916.       http://www.apache.org/licenses/LICENSE-2.0
  4917.      
  4918.       Unless required by applicable law or agreed to in writing, software
  4919.       distributed under the License is distributed on an "AS IS" BASIS,
  4920.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4921.       See the License for the specific language governing permissions and
  4922.       limitations under the License.
  4923.     -->
  4924.      
  4925.     <script type="text/x-red" data-template-name="split">
  4926.         <div class="form-row">
  4927.             <label for="node-input-splt"><i class="fa fa-scissors"></i> Split</label>
  4928.             <input type="text" id="node-input-splt" placeholder="character to split strings on : e.g. \n">
  4929.         </div>
  4930.         <div class="form-row">
  4931.             <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
  4932.             <input type="text" id="node-input-name" placeholder="Name">
  4933.         </div>
  4934.     </script>
  4935.      
  4936.      
  4937.      
  4938.     <script type="text/javascript">
  4939.         RED.nodes.registerType('split',{
  4940.             category: 'function',
  4941.             color:"#E2D96E",
  4942.             defaults: {
  4943.                 name: {value:""},
  4944.                 splt: {value:"\\n"}
  4945.             },
  4946.             inputs:1,
  4947.             outputs:1,
  4948.             icon: "split.png",
  4949.             label: function() {
  4950.                 return this.name||"split";
  4951.             },
  4952.             labelStyle: function() {
  4953.                 return this.name?"node_label_italic":"";
  4954.             }
  4955.         });
  4956.     </script>
  4957.      
  4958.     <script type="text/x-red" data-template-name="join">
  4959.         <div class="form-row">
  4960.             <label>Mode</label>
  4961.             <select id="node-input-mode" style="width:200px;">
  4962.                 <option value="auto">automatic</option>
  4963.                 <option value="custom">manual</option>
  4964.             </select>
  4965.         </div>
  4966.         <div class="node-row-custom">
  4967.             <div class="form-row node-row-property">
  4968.                 <label>Combine each </label>
  4969.                 <input type="text" id="node-input-property" style="width:70%;">
  4970.                 <input type="hidden" id="node-input-propertyType">
  4971.             </div>
  4972.             <div class="form-row">
  4973.                 <label>to create </label>
  4974.                 <select id="node-input-build" style="width:200px;">
  4975.                     <option id="node-input-build-string" value="string">a String</option>
  4976.                     <option value="array">an Array</option>
  4977.                     <option value="object">a key/value Object</option>
  4978.                     <option value="merged">a merged Object</option>
  4979.                 </select>
  4980.             </div>
  4981.             <div class="form-row node-row-key">
  4982.                 <label style="vertical-align: top; margin-top: 7px;">using</label>
  4983.                 <div style="display:inline-block">
  4984.                     <input type="text" id="node-input-key"  style="width:300px;">
  4985.                     <div style="margin-top: 7px;">as the property key</div>
  4986.                 </div>
  4987.             </div>
  4988.             <div class="form-row node-row-joiner">
  4989.                 <label for="node-input-joiner">joined using</label>
  4990.                 <input type="text" id="node-input-joiner" style="width: 40px">
  4991.             </div>
  4992.      
  4993.             <div class="form-row node-row-trigger">
  4994.                 <label style="width: auto;">Send the message:</label>
  4995.                 <ul>
  4996.                     <li style="height: 40px;">
  4997.                         <label style="width: 280px;" for="node-input-count">After a fixed number of messages:</label> <input id="node-input-count" placeholder="count" type="text" style="width: 75px;">
  4998.                     </li>
  4999.                     <li style="height: 40px;">
  5000.                         <label style="width: 280px;" for="node-input-timeout">After a timeout following the first message:</label> <input id="node-input-timeout" placeholder="seconds" type="text" style="width: 75px;">
  5001.                     </li>
  5002.                     <li style="height: 40px;">
  5003.                         <label style="width: auto; padding-top: 6px;">After a message with the <code>msg.complete</code> property set</label>
  5004.                     </li>
  5005.                 </ul>
  5006.             </div>
  5007.         </div>
  5008.         <div class="form-tips form-tips-auto hide">This mode assumes this node is either
  5009.         paired with a <i>split</i> node or the received messages will have a properly
  5010.         configured <code>msg.parts</code> property.</div>
  5011.      
  5012.     </script>
  5013.      
  5014.      
  5015.      
  5016.     <script type="text/javascript">
  5017.         RED.nodes.registerType('join',{
  5018.             category: 'function',
  5019.             color:"#E2D96E",
  5020.             defaults: {
  5021.                 name: {value:""},
  5022.                 mode: {value:"auto"},
  5023.                 build: { value:"string"},
  5024.                 property: { value: "payload", validate: RED.validators.typedInput("propertyType")},
  5025.                 propertyType: { value:"msg"},
  5026.                 key: {value:"topic"},
  5027.                 joiner: { value:"\\n"},
  5028.                 timeout: {value:""},
  5029.                 count: {value:""}
  5030.             },
  5031.             inputs:1,
  5032.             outputs:1,
  5033.             icon: "join.png",
  5034.             label: function() {
  5035.                 return this.name||"join";
  5036.             },
  5037.             labelStyle: function() {
  5038.                 return this.name?"node_label_italic":"";
  5039.             },
  5040.             oneditprepare: function() {
  5041.                 $("#node-input-mode").change(function(e) {
  5042.                     var val = $(this).val();
  5043.                     $(".node-row-custom").toggle(val==='custom');
  5044.                     $(".form-tips-auto").toggle(val==='auto');
  5045.                 });
  5046.      
  5047.                 $("#node-input-build").change(function(e) {
  5048.                     var val = $(this).val();
  5049.                     $(".node-row-key").toggle(val==='object');
  5050.                     $(".node-row-joiner").toggle(val==='string');
  5051.                     $(".node-row-trigger").toggle(val!=='auto');
  5052.                     if (val === 'string') {
  5053.                         $("#node-input-property").typedInput('types',['msg']);
  5054.                     } else {
  5055.                         $("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]);
  5056.                     }
  5057.                 })
  5058.      
  5059.                 $("#node-input-property").typedInput({
  5060.                     typeField: $("#node-input-propertyType"),
  5061.                     types:['msg', {value:"full",label:"complete message",hasValue:false}]
  5062.                 })
  5063.                 $("#node-input-key").typedInput({
  5064.                     types:['msg', {value:"merge",label:"",hasValue: false}]
  5065.                 })
  5066.      
  5067.                 $("#node-input-build").change();
  5068.                 $("#node-input-mode").change();
  5069.             }
  5070.         });
  5071.     </script>
  5072.     <script type="text/x-red" data-help-name="split">
  5073.         <p>A function that splits <code>msg.payload</code> into multiple messages.</p>
  5074.         <p>The behaviour is determined by the type of <code>msg.payload</code>:</p>
  5075.         <ul>
  5076.             <li><b>string</b> - a message is sent for each part of the string after it is split using the specified character, by default a newline (<code>\n</code>).
  5077.             <li><b>array</b> - a message is sent for each element of the array</li>
  5078.             <li><b>object</b> - a message is sent for each key/value pair of the object. <code>msg.parts.key</code> is set to the key of the property.</li>
  5079.         </ul>
  5080.         <p>Each message is sent with the <code>msg.parts</code> property set. This is
  5081.         an object that provides any subsequent <i>join</i> node the necessary information
  5082.         for it to reassemble the messages back into a single one. The object has the following
  5083.         properties:</p>
  5084.         <ul>
  5085.             <li><b>id</b> - an identifier for the group of messages</li>
  5086.             <li><b>index</b> - the position within the group</li>
  5087.             <li><b>count</b> - the total number of messages in the group</li>
  5088.             <li><b>type</b> - the type of message - string/array/object</li>
  5089.             <li><b>ch</b> - for a string, the character used to split</li>
  5090.             <li><b>key</b> - for an object, the key of the property this message was created from</li>
  5091.         </ul>
  5092.     </script><script type="text/x-red" data-help-name="join">
  5093.         <p>A function that joins a sequence of messages into a single message.</p>
  5094.         <p>When paired with the <b>split</b> node, it will automatically join the messages
  5095.         to reverse the split that was performed.</p>
  5096.         <p>The node can join either a specific property of each received message or,
  5097.         if the output type is not string, the entire message.</p>
  5098.         <p>The type of the resulting message property can be:</p>
  5099.         <ul>
  5100.             <li>a <b>string</b> - created by joining the property of each message with the specified join character.</li>
  5101.             <li>an <b>array</b>.</li>
  5102.             <li>a <b>key/value object</b> - created by using a property of each message to determine the key under which
  5103.             the required value is stored.</li>
  5104.             <li>a <b>merged object</b> - created by merging the property of each message under a single object.</li>
  5105.         </ul>
  5106.         The other properties of the output message are taken from the last message received before the result is sent.</p>
  5107.         <p>A <i>count</i> can be set for how many messages should be received before generating the output message</p>
  5108.         <p>A <i>timeout</i> can be set to trigger sending the new message using whatever has been received so far.</p>
  5109.         <p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
  5110.         <p>The automatic behaviour relies on the received messages to have <b>msg.parts</b> set. The split node generates
  5111.         this property, but can be manually created. It has the following properties:</p>
  5112.        <ul>
  5113.            <li><b>id</b> - an identifier for the group of messages</li>
  5114.            <li><b>index</b> - the position within the group</li>
  5115.            <li><b>count</b> - the total number of messages in the group</li>
  5116.            <li><b>type</b> - the type of message - string/array/object</li>
  5117.            <li><b>ch</b> - for a string, the character used to split</li>
  5118.            <li><b>key</b> - for an object, the key of the property this message was created from</li>
  5119.        </ul>
  5120.     </script>
  5121.     <!--
  5122.       Copyright JS Foundation and other contributors, http://js.foundation
  5123.      
  5124.       Licensed under the Apache License, Version 2.0 (the "License");
  5125.       you may not use this file except in compliance with the License.
  5126.       You may obtain a copy of the License at
  5127.      
  5128.       http://www.apache.org/licenses/LICENSE-2.0
  5129.      
  5130.       Unless required by applicable law or agreed to in writing, software
  5131.       distributed under the License is distributed on an "AS IS" BASIS,
  5132.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5133.       See the License for the specific language governing permissions and
  5134.       limitations under the License.
  5135.     -->
  5136.      
  5137.     <script type="text/x-red" data-template-name="csv">
  5138.         <div class="form-row">
  5139.             <label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label>
  5140.             <input type="text" id="node-input-temp" data-i18n="[placeholder]csv.placeholder.columns">
  5141.         </div>
  5142.         <div class="form-row">
  5143.             <label for="node-input-select-sep"><i class="fa fa-text-width"></i> <span data-i18n="csv.label.separator"></span></label>
  5144.                 <select style="width: 150px" id="node-input-select-sep">
  5145.                     <option value="," data-i18n="csv.separator.comma"></option>
  5146.                     <option value="\t" data-i18n="csv.separator.tab"></option>
  5147.                     <option value=" " data-i18n="csv.separator.space"></option>
  5148.                     <option value=";" data-i18n="csv.separator.semicolon"></option>
  5149.                     <option value=":" data-i18n="csv.separator.colon"></option>
  5150.                     <option value="#" data-i18n="csv.separator.hashtag"></option>
  5151.                     <option value="" data-i18n="csv.separator.other"></option>
  5152.                </select>
  5153.                <input style="width: 40px;" type="text" id="node-input-sep" pattern=".">
  5154.         </div>
  5155.      
  5156.         <div class="form-row">
  5157.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5158.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5159.         </div>
  5160.         <hr align="middle"/>
  5161.         <div class="form-row">
  5162.             <label style="width: 100%;"><i class="fa fa-gears"></i> <span data-i18n="csv.label.c2o"></span></label>
  5163.             <label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label>
  5164.             <input style="width: 30px" type="checkbox" id="node-input-hdrin"><label style="width: auto;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></span>
  5165.         </div>
  5166.         <div class="form-row">
  5167.             <label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
  5168.             <select type="text" id="node-input-multi" style="width: 250px;">
  5169.                 <option value="one" data-i18n="csv.output.row"></option>
  5170.                 <option value="mult" data-i18n="csv.output.array"></option>
  5171.             </select>
  5172.         </div>
  5173.         <hr align="middle"/>
  5174.         <div class="form-row">
  5175.             <label style="width: 100%;"><i class="fa fa-gears"></i> <span data-i18n="csv.label.o2c"></span></label>
  5176.             <label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label>
  5177.             <input style="width: 30px" type="checkbox" id="node-input-hdrout"><label style="width: auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span>
  5178.         </div>
  5179.         <div class="form-row">
  5180.             <label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-align-left"></i> <span data-i18n="csv.label.newline"></span></label>
  5181.             <select style="width: 150px" id="node-input-ret">
  5182.                 <option value='\n' data-i18n="csv.newline.linux"></option>
  5183.                 <option value='\r' data-i18n="csv.newline.mac"></option>
  5184.                 <option value='\r\n' data-i18n="csv.newline.windows"></option>
  5185.            </select>
  5186.         </div>
  5187.     </script>
  5188.      
  5189.      
  5190.      
  5191.     <script type="text/javascript">
  5192.         RED.nodes.registerType('csv',{
  5193.             category: 'function',
  5194.             color:"#DEBD5C",
  5195.             defaults: {
  5196.                 name: {value:""},
  5197.                 sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)},
  5198.                 //quo: {value:'"',required:true},
  5199.                 hdrin: {value:""},
  5200.                 hdrout: {value:""},
  5201.                 multi: {value:"one",required:true},
  5202.                 ret: {value:'\\n'},
  5203.                 temp: {value:""}
  5204.             },
  5205.             inputs:1,
  5206.             outputs:1,
  5207.             icon: "parser-csv.png",
  5208.             label: function() {
  5209.                 return this.name||"csv";
  5210.             },
  5211.             labelStyle: function() {
  5212.                 return this.name?"node_label_italic":"";
  5213.             },
  5214.             oneditprepare: function() {
  5215.                 if (this.sep == "," || this.sep == "\\t" || this.sep == ";" || this.sep == ":" || this.sep == " " || this.sep == "#") {
  5216.                     $("#node-input-select-sep").val(this.sep);
  5217.                     $("#node-input-sep").hide();
  5218.                 } else {
  5219.                     $("#node-input-select-sep").val("");
  5220.                     $("#node-input-sep").val(this.sep);
  5221.                     $("#node-input-sep").show();
  5222.                 }
  5223.                 $("#node-input-select-sep").change(function() {
  5224.                     var v = $("#node-input-select-sep").val();
  5225.                     $("#node-input-sep").val(v);
  5226.                     if (v == "") {
  5227.                         $("#node-input-sep").val("");
  5228.                         $("#node-input-sep").show().focus();
  5229.                     } else {
  5230.                         $("#node-input-sep").hide();
  5231.                     }
  5232.                 });
  5233.             }
  5234.         });
  5235.     </script>
  5236.     <script type="text/x-red" data-help-name="csv">
  5237.         <p>A function that parses the <code>msg.payload</code> to convert CSV to/from a javascript object.
  5238.         Places the result in the payload.</p>
  5239.         <p>If the input is a string it tries to parse it as CSV and creates a javascript object.</p>
  5240.         <p>If the input is a javascript object it tries to build a CSV string.</p>
  5241.         <p>If the input is a simple array the output is just a CSV generated from that array.</p>
  5242.         <p>If the input is an array of arrays, or an array of objects, a multiple-line CSV is created.</p>
  5243.         <p>The columns template should contain an ordered list of column headers. For CSV input these become the property names.
  5244.         For CSV output these specify the properties to extract from the object and the order for the CSV.</p>
  5245.         <p>If the input is an array then the columns template does not matter, but can be used to generate a row of column titles.</p>
  5246.         <p><b>Note:</b> the columns should always be specified comma separated - even if another separator is chosen for the data.</p>
  5247.         </script><!--
  5248.       Copyright JS Foundation and other contributors, http://js.foundation
  5249.      
  5250.       Licensed under the Apache License, Version 2.0 (the "License");
  5251.       you may not use this file except in compliance with the License.
  5252.       You may obtain a copy of the License at
  5253.      
  5254.       http://www.apache.org/licenses/LICENSE-2.0
  5255.      
  5256.       Unless required by applicable law or agreed to in writing, software
  5257.       distributed under the License is distributed on an "AS IS" BASIS,
  5258.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5259.       See the License for the specific language governing permissions and
  5260.       limitations under the License.
  5261.     -->
  5262.      
  5263.     <script type="text/x-red" data-template-name="html">
  5264.         <div class="form-row">
  5265.             <label for="node-input-tag"><i class="fa fa-filter"></i> <span data-i18n="html.label.select"></span></label>
  5266.             <input type="text" id="node-input-tag" placeholder="h1">
  5267.         </div>
  5268.         <div class="form-row">
  5269.             <label for="node-input-ret"><i class="fa fa-sign-out"></i> <span data-i18n="html.label.output"></span></label>
  5270.             <select id="node-input-ret" style="width:70%">
  5271.                 <option value="html" data-i18n="html.output.html"></option>
  5272.                 <option value="text" data-i18n="html.output.text"></option>
  5273.                 <option value="attr" data-i18n="html.output.attr"></option>
  5274.                 <!-- <option value="val">return the value from a form element</option> -->
  5275.             </select>
  5276.         </div>
  5277.         <div class="form-row">
  5278.             <label for="node-input-as">&nbsp;</label>
  5279.             <select id="node-input-as" style="width:70%">
  5280.                 <option value="single" data-i18n="html.format.single"></option>
  5281.                 <option value="multi" data-i18n="html.format.multi"></option>
  5282.             </select>
  5283.         </div>
  5284.         <br/>
  5285.         <div class="form-row">
  5286.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5287.             <input type="text" id="node-input-name" style="width:70%" data-i18n="[placeholder]common.label.name">
  5288.         </div>
  5289.         <div class="form-tips"><span data-i18n="[html]html.tip"></span></div>
  5290.     </script>
  5291.      
  5292.      
  5293.      
  5294.     <script type="text/javascript">
  5295.         RED.nodes.registerType('html',{
  5296.             category: 'function',
  5297.             color:"#DEBD5C",
  5298.             defaults: {
  5299.                 name: {value:""},
  5300.                 tag: {value:""},
  5301.                 ret: {value:"html"},
  5302.                 as: {value:"single"}
  5303.             },
  5304.             inputs:1,
  5305.             outputs:1,
  5306.             icon: "parser-html.png",
  5307.             label: function() {
  5308.                 return this.name||this.tag||"html";
  5309.             },
  5310.             labelStyle: function() {
  5311.                 return this.name?"node_label_italic":"";
  5312.             }
  5313.         });
  5314.     </script>
  5315.     <script type="text/x-red" data-help-name="html">
  5316.         <p>Extracts elements from an html document held in <code>msg.payload</code> using a selector.</p>
  5317.         <p>If left blank the selector may be set dynamically by passing in <code>msg.select</code> along with the <code>msg.payload</code>.
  5318.         <p>The selector uses <a href="https://github.com/cheeriojs/cheerio/blob/master/Readme.md" target="_blank">Cheerio</a>
  5319.          which uses the <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_blank">CSS selector</a> syntax.</p>
  5320.         <p>The result can be either a single message with a payload containing an array of the matched elements, or multiple
  5321.            messages that each contain a matched element.</p>
  5322.     </script><!--
  5323.       Copyright JS Foundation and other contributors, http://js.foundation
  5324.      
  5325.       Licensed under the Apache License, Version 2.0 (the "License");
  5326.       you may not use this file except in compliance with the License.
  5327.       You may obtain a copy of the License at
  5328.      
  5329.       http://www.apache.org/licenses/LICENSE-2.0
  5330.      
  5331.       Unless required by applicable law or agreed to in writing, software
  5332.       distributed under the License is distributed on an "AS IS" BASIS,
  5333.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5334.       See the License for the specific language governing permissions and
  5335.       limitations under the License.
  5336.     -->
  5337.      
  5338.     <script type="text/x-red" data-template-name="json">
  5339.         <div class="form-row">
  5340.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5341.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5342.         </div>
  5343.     </script>
  5344.      
  5345.      
  5346.      
  5347.     <script type="text/javascript">
  5348.         RED.nodes.registerType('json',{
  5349.             category: 'function',
  5350.             color:"#DEBD5C",
  5351.             defaults: {
  5352.                 name: {value:""}
  5353.             },
  5354.             inputs:1,
  5355.             outputs:1,
  5356.             icon: "parser-json.png",
  5357.             label: function() {
  5358.                 return this.name||"json";
  5359.             },
  5360.             labelStyle: function() {
  5361.                 return this.name?"node_label_italic":"";
  5362.             }
  5363.         });
  5364.     </script>
  5365.     <script type="text/x-red" data-help-name="json">
  5366.         <p>A function that parses the <code>msg.payload</code> to convert a JSON string to/from a javascript object. Places the result back into the payload.</p>
  5367.         <p>If the input is a JSON string it tries to parse it to a javascript object.</p>
  5368.         <p>If the input is a javascript object it creates a JSON string.</p>
  5369.     </script><!--
  5370.       Copyright JS Foundation and other contributors, http://js.foundation
  5371.      
  5372.       Licensed under the Apache License, Version 2.0 (the "License");
  5373.       you may not use this file except in compliance with the License.
  5374.       You may obtain a copy of the License at
  5375.      
  5376.       http://www.apache.org/licenses/LICENSE-2.0
  5377.      
  5378.       Unless required by applicable law or agreed to in writing, software
  5379.       distributed under the License is distributed on an "AS IS" BASIS,
  5380.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5381.       See the License for the specific language governing permissions and
  5382.       limitations under the License.
  5383.     -->
  5384.      
  5385.     <script type="text/x-red" data-template-name="xml">
  5386.         <div class="form-row">
  5387.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5388.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5389.         </div>
  5390.         <div class="form-row" id="advanced">
  5391.         </div>
  5392.         <div id="advanced-options">
  5393.             <div class="form-row">
  5394.                 <i class="fa fa-key"></i> <span data-i18n="xml.label.represent"></span> <input type="text" id="node-input-attr" style="text-align:center; width:40px" placeholder="$">
  5395.             </div>
  5396.             <div class="form-row">
  5397.                 <i class="fa fa-key"></i> <span data-i18n="xml.label.prefix"></span> <input type="text" id="node-input-chr" style="text-align:center; width:40px" placeholder="_">
  5398.             </div>
  5399.             <div class="form-tips"><span data-i18n="xml.tip"></span></div>
  5400.         </div>
  5401.     </script>
  5402.      
  5403.      
  5404.      
  5405.     <script type="text/javascript">
  5406.         RED.nodes.registerType('xml',{
  5407.             category: 'function',
  5408.             color:"#DEBD5C",
  5409.             defaults: {
  5410.                 name: {value:""},
  5411.                 attr: {value:""},
  5412.                 chr: {value:""}
  5413.             },
  5414.             inputs:1,
  5415.             outputs:1,
  5416.             icon: "parser-xml.png",
  5417.             label: function() {
  5418.                 return this.name||"xml";
  5419.             },
  5420.             labelStyle: function() {
  5421.                 return this.name?"node_label_italic":"";
  5422.             },
  5423.             oneditprepare: function() {
  5424.                 var showadvanced = showadvanced || true;
  5425.                 var advanced = this._("xml.label.advanced");
  5426.                 var showall = function() {
  5427.                     showadvanced = !showadvanced;
  5428.                     if (showadvanced) {
  5429.                         $("#advanced-options").show();
  5430.                         $("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-minus-square"></i> '+advanced+'</label>');
  5431.                     }
  5432.                     else {
  5433.                         $("#advanced-options").hide();
  5434.                         $("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-plus-square"></i> '+advanced+' ...</label>');
  5435.                     }
  5436.                 };
  5437.                 showall();
  5438.                 $("#advanced").click( function() { showall(); });
  5439.             }
  5440.         });
  5441.     </script>
  5442.     <script type="text/x-red" data-help-name="xml">
  5443.         <p>A function that parses the <code>msg.payload</code> to convert xml to/from a javascript object. Places the result in the payload.</p>
  5444.         <p>If the input is a string it tries to parse it as XML and creates a javascript object.</p>
  5445.         <p>If the input is a javascript object it tries to build an XML string.</p>
  5446.         <p>You can also pass in a <code>msg.options</code> object to override all the multitude of parameters. See
  5447.         <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">the xml2js docs</a>
  5448.          for more information.</p>
  5449.          <p>If set, options in the edit dialogue override those passed in on the msg.options object.</p>
  5450.     </script>
  5451.     <script type="text/x-red" data-template-name="yaml">
  5452.         <div class="form-row">
  5453.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5454.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5455.         </div>
  5456.     </script>
  5457.      
  5458.      
  5459.      
  5460.     <script type="text/javascript">
  5461.         RED.nodes.registerType('yaml',{
  5462.             category: 'function',
  5463.             color:"#DEBD5C",
  5464.             defaults: {
  5465.                 name: {value:""}
  5466.             },
  5467.             inputs:1,
  5468.             outputs:1,
  5469.             icon: "parser-yaml.png",
  5470.             label: function() {
  5471.                 return this.name||"yaml";
  5472.             },
  5473.             labelStyle: function() {
  5474.                 return this.name?"node_label_italic":"";
  5475.             }
  5476.         });
  5477.     </script>
  5478.     <script type="text/x-red" data-help-name="yaml">
  5479.         <p>A function that parses the <code>msg.payload</code> to convert a YAML string to/from a javascript object. Places the result back into the payload.</p>
  5480.         <p>If the input is a YAML string it tries to parse it to a javascript object.</p>
  5481.         <p>If the input is a javascript object it creates a YAML string.</p>
  5482.     </script><!--
  5483.       Copyright JS Foundation and other contributors, http://js.foundation
  5484.      
  5485.       Licensed under the Apache License, Version 2.0 (the "License");
  5486.       you may not use this file except in compliance with the License.
  5487.       You may obtain a copy of the License at
  5488.      
  5489.       http://www.apache.org/licenses/LICENSE-2.0
  5490.      
  5491.       Unless required by applicable law or agreed to in writing, software
  5492.       distributed under the License is distributed on an "AS IS" BASIS,
  5493.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5494.       See the License for the specific language governing permissions and
  5495.       limitations under the License.
  5496.     -->
  5497.      
  5498.     <script type="text/x-red" data-template-name="tail">
  5499.         <div class="form-row">
  5500.              <label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="tail.label.filename"></span></label>
  5501.              <input id="node-input-filename" type="text">
  5502.         </div>
  5503.         <div class="form-row">
  5504.             <label for="node-input-filetype"><i class="fa fa-file-text-o"></i> <span data-i18n="tail.label.type"></span></label>
  5505.             <select type="text" id="node-input-filetype">
  5506.                 <option value="text" data-i18n="tail.action.text"></option>
  5507.                 <option value="binary" data-i18n="tail.action.binary"></option>
  5508.             </select>
  5509.         </div>
  5510.         <div class="form-row" id="node-tail-split">
  5511.             <label>&nbsp;</label>
  5512.             <input type="checkbox" id="node-input-split" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
  5513.             <label for="node-input-split" style="width: 70%;"><span data-i18n="tail.label.splitlines"></span></label>
  5514.         </div>
  5515.         <div class="form-row">
  5516.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5517.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5518.         </div>
  5519.     </script>
  5520.      
  5521.      
  5522.      
  5523.     <script type="text/javascript">
  5524.         RED.nodes.registerType('tail',{
  5525.             category: 'storage-input',
  5526.             defaults: {
  5527.                 name: {value:""},
  5528.                 filetype: {value:"text"},
  5529.                 split: {value:false},
  5530.                 filename: {value:"",required:true}
  5531.             },
  5532.             color:"BurlyWood",
  5533.             inputs:0,
  5534.             outputs:1,
  5535.             icon: "file.png",
  5536.             label: function() {
  5537.                 return this.name||this.filename;
  5538.             },
  5539.             labelStyle: function() {
  5540.                 return this.name?"node_label_italic":"";
  5541.             },
  5542.             oneditprepare: function() {
  5543.               $("#node-input-filetype").on("change",function() {
  5544.                 if (this.value === "text") { $("#node-tail-split").show(); }
  5545.                 else { $("#node-tail-split").hide(); }
  5546.               });
  5547.             }
  5548.         });
  5549.     </script>
  5550.     <script type="text/x-red" data-help-name="tail">
  5551.         <p>Tails (watches for things to be added) to the configured file. (Linux/Mac ONLY)</p>
  5552.         <p>This will not work on Windows filesystems, as it relies on the <b>tail -F</b> command.</p>
  5553.         <p>Text (UTF-8) files will be returned as strings. Binary files will be returned as a Buffer object.</p>
  5554.     </script><!--
  5555.       Copyright JS Foundation and other contributors, http://js.foundation
  5556.      
  5557.       Licensed under the Apache License, Version 2.0 (the "License");
  5558.       you may not use this file except in compliance with the License.
  5559.       You may obtain a copy of the License at
  5560.      
  5561.       http://www.apache.org/licenses/LICENSE-2.0
  5562.      
  5563.       Unless required by applicable law or agreed to in writing, software
  5564.       distributed under the License is distributed on an "AS IS" BASIS,
  5565.       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  5566.       See the License for the specific language governing permissions and
  5567.       limitations under the License.
  5568.     -->
  5569.      
  5570.     <script type="text/x-red" data-template-name="file">
  5571.         <div class="form-row node-input-filename">
  5572.              <label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
  5573.              <input id="node-input-filename" type="text">
  5574.         </div>
  5575.         <div class="form-row">
  5576.             <label for="node-input-overwriteFile"><i class="fa fa-random"></i> <span data-i18n="file.label.action"></span></label>
  5577.             <select type="text" id="node-input-overwriteFile" style="display: inline-block; width: 250px; vertical-align: top;">
  5578.                 <option value="false" data-i18n="file.action.append"></option>
  5579.                 <option value="true" data-i18n="file.action.overwrite"></option>
  5580.                 <option value="delete" data-i18n="file.action.delete"></option>
  5581.             </select>
  5582.         </div>
  5583.         <div class="form-row" id="node-appline">
  5584.             <label>&nbsp;</label>
  5585.             <input type="checkbox" id="node-input-appendNewline" style="display: inline-block; width: auto; vertical-align: top;">
  5586.             <label for="node-input-appendNewline" style="width: 70%;"><span data-i18n="file.label.addnewline"></span></label>
  5587.         </div>
  5588.         <div class="form-row">
  5589.             <label>&nbsp;</label>
  5590.             <input type="checkbox" id="node-input-createDir" style="display: inline-block; width: auto; vertical-align: top;">
  5591.             <label for="node-input-createDir" style="width: 70%;"><span data-i18n="file.label.createdir"></span></label>
  5592.         </div>
  5593.         <div class="form-row">
  5594.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5595.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5596.         </div>
  5597.         <div class="form-tips"><span data-i18n="file.tip"></span></div>
  5598.     </script>
  5599.      
  5600.      
  5601.      
  5602.     <script type="text/x-red" data-template-name="file in">
  5603.         <div class="form-row">
  5604.              <label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
  5605.              <input id="node-input-filename" type="text" data-i18n="[placeholder]file.label.filename">
  5606.         </div>
  5607.         <div class="form-row">
  5608.             <label for="node-input-format"><i class="fa fa-sign-out"></i> <span data-i18n="file.label.outputas"></span></label>
  5609.             <select id="node-input-format">
  5610.                 <option value="utf8" data-i18n="file.output.utf8"></option>
  5611.                 <option value="" data-i18n="file.output.buffer"></option>
  5612.             </select>
  5613.         </div>
  5614.         <div class="form-row">
  5615.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
  5616.             <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
  5617.         </div>
  5618.     </script>
  5619.      
  5620.      
  5621.      
  5622.     <script type="text/javascript">
  5623.         RED.nodes.registerType('file',{
  5624.             category: 'storage-output',
  5625.             defaults: {
  5626.                 name: {value:""},
  5627.                 filename: {value:""},
  5628.                 appendNewline: {value:true},
  5629.                 createDir: {value:false},
  5630.                 overwriteFile: {value:"false"}
  5631.             },
  5632.             color:"BurlyWood",
  5633.             inputs:1,
  5634.             outputs:0,
  5635.             icon: "file.png",
  5636.             align: "right",
  5637.             label: function() {
  5638.                 if (this.overwriteFile === "delete") {
  5639.                     return this.name||this._("file.label.deletelabel",{file:this.filename})
  5640.                 } else {
  5641.                     return this.name||this.filename||this._("file.label.filelabel");
  5642.                 }
  5643.             },
  5644.             labelStyle: function() {
  5645.                 return this.name?"node_label_italic":"";
  5646.             },
  5647.             oneditprepare: function() {
  5648.               $("#node-input-overwriteFile").on("change",function() {
  5649.                 if (this.value === "delete") { $("#node-appline").hide(); }
  5650.                 else { $("#node-appline").show(); }
  5651.               });
  5652.             }
  5653.         });
  5654.      
  5655.         RED.nodes.registerType('file in',{
  5656.             category: 'storage-input',
  5657.             defaults: {
  5658.                 name: {value:""},
  5659.                 filename: {value:""},
  5660.                 format: {value:"utf8"},
  5661.             },
  5662.             color:"BurlyWood",
  5663.             inputs:1,
  5664.             outputs:1,
  5665.             icon: "file.png",
  5666.             label: function() {
  5667.                 return this.name||this.filename||this._("file.label.filelabel");
  5668.             },
  5669.             labelStyle: function() {
  5670.                 return this.name?"node_label_italic":"";
  5671.             }
  5672.         });
  5673.      
  5674.     </script>
  5675.     <script type="text/x-red" data-help-name="file">
  5676.         <p>Writes <code>msg.payload</code> to the file specified, for example to create a log.</p>
  5677.         <p>The filename can be configured in the node. If left blank it should be
  5678.         set by <code>msg.filename</code> on the incoming message.</p>
  5679.         <p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
  5680.         <p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
  5681.         <p>This node can also be configured to delete a file if required.</p>
  5682.     </script><script type="text/x-red" data-help-name="file in">
  5683.         <p>Reads the specified file and sends the content as <code>msg.payload</code>,
  5684.         and the filename as <code>msg.filename</code>.</p>
  5685.         <p>The filename can be configured in the node. If left blank it should be
  5686.         set by <code>msg.filename</code> on the incoming message.</p>
  5687.     </script>
  5688.     <script type="text/x-red" data-template-name="e-mail">
  5689.         <div class="form-row">
  5690.             <label for="node-input-name"><i class="fa fa-envelope"></i> <span data-i18n="email.label.to"></span></label>
  5691.             <input type="text" id="node-input-name" placeholder="email@address.com">
  5692.         </div>
  5693.         <!-- <div class="form-row">
  5694.             <label for="node-input-pin"><i class="fa fa-asterisk"></i> Service</label>
  5695.             <select type="text" id="node-input-pin" style="width: 150px;">
  5696.                 <option value="-" disabled> </option>
  5697.                 <option value="DynectEmail">DynectEmail</option>
  5698.                 <option value="Gmail">Gmail</option>
  5699.                 <option value="hot.ee">hot.ee</option>
  5700.                 <option value="Hotmail">Hotmail</option>
  5701.                 <option value="iCloud">iCloud</option>
  5702.                 <option value="mail.ee">mail.ee</option>
  5703.                 <option value="Mail.Ru">Mail.Ru</option>
  5704.                 <option value="Mailgun">Mailgun</option>
  5705.                 <option value="Mailjet">Mailjet</option>
  5706.                 <option value="Mandrill">Mandrill</option>
  5707.                 <option value="Postmark">Postmark</option>
  5708.                 <option value="QQ">QQ</option>
  5709.                 <option value="QQex">QQex</option>
  5710.                 <option value="SendGrid">SendGrid</option>
  5711.                 <option value="SendCloud">SendCloud</option>
  5712.                 <option value="SES">SES</option>
  5713.                 <option value="Yahoo">Yahoo</option>
  5714.                 <option value="yandex">yandex</option>
  5715.                 <option value="Zoho">Zoho</option>
  5716.              </select>
  5717.         </div> -->
  5718.         <div class="form-row">
  5719.             <label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="email.label.server"></span></label>
  5720.             <input type="text" id="node-input-server" placeholder="smtp.gmail.com">
  5721.         </div>
  5722.         <div class="form-row">
  5723.             <label for="node-input-port"><i class="fa fa-random"></i> <span data-i18n="email.label.port"></span></label>
  5724.             <input type="text" id="node-input-port" placeholder="465">
  5725.         </div>
  5726.         <div class="form-row">
  5727.             <label for="node-input-userid"><i class="fa fa-user"></i> <span data-i18n="email.label.userid"></span></label>
  5728.             <input type="text" id="node-input-userid">
  5729.         </div>
  5730.         <div class="form-row">
  5731.             <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="email.label.password"></span></label>
  5732.             <input type="password" id="node-input-password">
  5733.         </div>
  5734.         <br/>
  5735.         <div class="form-row">
  5736.             <label for="node-input-dname"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
  5737.             <input type="text" id="node-input-dname" data-i18n="[placeholder]node-red:common.label.name">
  5738.         </div>
  5739.         <div class="form-tips" id="node-tip"><span data-i18n="[html]email.tip.cred"></span></div>
  5740.     </script>
  5741.      
  5742.      
  5743.      
  5744.     <script type="text/javascript">
  5745.     (function() {
  5746.         RED.nodes.registerType('e-mail',{
  5747.             category: 'social-output',
  5748.             color:"#c7e9c0",
  5749.             defaults: {
  5750.                 server: {value:"smtp.gmail.com",required:true},
  5751.                 port: {value:"465",required:true},
  5752.                 name: {value:""},
  5753.                 dname: {value:""}
  5754.             },
  5755.             credentials: {
  5756.                 userid: {type:"text"},
  5757.                 password: {type: "password"},
  5758.                 global: { type:"boolean"}
  5759.             },
  5760.             inputs:1,
  5761.             outputs:0,
  5762.             icon: "envelope.png",
  5763.             align: "right",
  5764.             label: function() {
  5765.                 return this.dname||this.name||"email";
  5766.             },
  5767.             labelStyle: function() {
  5768.                 return (this.dname||!this.topic)?"node_label_italic":"";
  5769.             },
  5770.             oneditprepare: function() {
  5771.                 if (this.credentials.global) {
  5772.                     $('#node-tip').show();
  5773.                 } else {
  5774.                     $('#node-tip').hide();
  5775.                 }
  5776.             }
  5777.         });
  5778.     })();
  5779.     </script>
  5780.      
  5781.      
  5782.     <script type="text/x-red" data-template-name="e-mail in">
  5783.         <div class="form-row node-input-repeat">
  5784.             <label for="node-input-repeat"><i class="fa fa-repeat"></i> <span data-i18n="email.label.repeat"></span></label>
  5785.             <input type="text" id="node-input-repeat" style="width: 80px"> <span data-i18n="email.label.seconds">seconds</span>
  5786.         </div>
  5787.         <div class="form-row">
  5788.             <label for="node-input-protocol"><i class="fa fa-envelope"></i> <span data-i18n="email.label.protocol"></span></label>
  5789.             <select type="text" id="node-input-protocol">
  5790.                 <option value="IMAP">IMAP</option>
  5791.                 <option value="POP3">POP3</option>
  5792.             </select>
  5793.         </div>
  5794.         <div class="form-row">
  5795.             <label for="node-input-useSSL"><i class="fa fa-lock"></i> <span data-i18n="email.label.useSSL"></span></label>
  5796.             <input type="checkbox" id="node-input-useSSL" style="width: auto;">
  5797.         </div>
  5798.         <div class="form-row">
  5799.             <label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="email.label.server"></span></label>
  5800.             <input type="text" id="node-input-server" placeholder="imap.gmail.com">
  5801.         </div>
  5802.         <div class="form-row">
  5803.             <label for="node-input-port"><i class="fa fa-random"></i> <span data-i18n="email.label.port"></span></label>
  5804.             <input type="text" id="node-input-port" placeholder="993">
  5805.         </div>
  5806.         <div class="form-row">
  5807.             <label for="node-input-userid"><i class="fa fa-user"></i> <span data-i18n="email.label.userid"></span></label>
  5808.             <input type="text" id="node-input-userid">
  5809.         </div>
  5810.         <div class="form-row">
  5811.             <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="email.label.password"></span></label>
  5812.             <input type="password" id="node-input-password">
  5813.         </div>
  5814.         <div class="form-row node-input-box">
  5815.             <label for="node-input-box"><i class="fa fa-inbox"></i> <span data-i18n="email.label.folder"></span></label>
  5816.             <input type="text" id="node-input-box">
  5817.         </div>
  5818.         <div class="form-row node-input-disposition">
  5819.                 <label for="node-input-disposition"><i class="fa fa-trash"></i> <span data-i18n="email.label.disposition"></span></label>
  5820.             <select type="text" id="node-input-disposition">
  5821.                 <option value="None" selected="selected">None</option>
  5822.                 <option value="Read">Mark Read</option>
  5823.                 <option value="Delete">Delete</option>
  5824.             </select>
  5825.         </div>
  5826.         <br/>
  5827.         <div class="form-row">
  5828.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
  5829.             <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
  5830.         </div>
  5831.         <script>
  5832.             var checkPorts = function() {
  5833.                 var currentPort = $("#node-input-port").val();
  5834.                 if (currentPort === "143" || currentPort === "993" || currentPort === "110" || currentPort == "995") {
  5835.                     if ($("#node-input-useSSL").prop("checked") === true) {
  5836.                         $("#node-input-port").val($("#node-input-protocol").val() === "IMAP"?"993":"995");
  5837.                     } else {
  5838.                         $("#node-input-port").val($("#node-input-protocol").val() === "IMAP"?"143":"110");
  5839.                     }
  5840.                 }
  5841.             };
  5842.      
  5843.             $("#node-input-useSSL").change(function(x, y) {
  5844.               console.log("useSSL: x="+ JSON.stringify(x) + ", y=" + y);
  5845.               console.log("Value: " + $("#node-input-useSSL").prop("checked"));
  5846.               checkPorts();
  5847.             });
  5848.      
  5849.             $("#node-input-protocol").change(function() {
  5850.                 var protocol = $("#node-input-protocol").val();
  5851.                 if (protocol === "IMAP") {
  5852.                     $(".node-input-box").show();
  5853.                     $(".node-input-disposition").show();
  5854.                 } else {
  5855.                     $(".node-input-box").hide();
  5856.                     $(".node-input-disposition").hide();
  5857.                 }
  5858.                 checkPorts();
  5859.             });
  5860.         </script>
  5861.     </script>
  5862.      
  5863.      
  5864.      
  5865.     <script type="text/javascript">
  5866.     (function() {
  5867.         RED.nodes.registerType('e-mail in',{
  5868.             category: 'social-input',
  5869.             color:"#c7e9c0",
  5870.             defaults: {
  5871.                 name: {value:""},
  5872.                 protocol: {value: "IMAP", required:true}, // Which protocol to use to connect to the mail server ("IMAP" or "POP3")
  5873.                 server: {value:"imap.gmail.com",required:true},
  5874.                 useSSL: {value: true},
  5875.                 port: {value:"993",required:true},
  5876.                 box: {value:"INBOX"}, // For IMAP, The mailbox to process
  5877.                 disposition: { value: "Read" }, // For IMAP, the disposition of the read email
  5878.                 repeat: {value:"300",required:true}
  5879.             },
  5880.             credentials: {
  5881.                 userid: {type:"text"},
  5882.                 password: {type: "password"},
  5883.                 global: { type:"boolean"}
  5884.             },
  5885.             inputs:0,
  5886.             outputs:1,
  5887.             icon: "envelope.png",
  5888.             label: function() {
  5889.                 return this.name||"email";
  5890.             },
  5891.             labelStyle: function() {
  5892.                 return (this.name||!this.topic)?"node_label_italic":"";
  5893.             },
  5894.             oneditprepare: function() {
  5895.                 if (this.credentials.global) {
  5896.                     $('#node-tip').show();
  5897.                 } else {
  5898.                     $('#node-tip').hide();
  5899.                 }
  5900.                 if (typeof this.box === 'undefined') {
  5901.                     $("#node-input-box").val("INBOX");
  5902.                     this.box = "INBOX";
  5903.                 }
  5904.             }
  5905.         });
  5906.     })();
  5907.     </script>
  5908.     <script type="text/x-red" data-help-name="e-mail">
  5909.         <p>Sends the <code>msg.payload</code> as an email, with a subject of <code>msg.topic</code>.</p>
  5910.         <p>The default message recipient can be configured in the node, if it is left
  5911.         blank it should be set using the <code>msg.to</code> property of the incoming message. If left blank
  5912.         you can also specify <code>msg.cc</code> and/or <code>msg.bcc</code> properties.</p>
  5913.         <p>You may optionally set <code>msg.from</code> in the payload which will override the <code>userid</code>
  5914.         default value.</p>
  5915.         <p>The payload can be html format.</p>
  5916.         <p>If the payload is a binary buffer then it will be converted to an attachment.
  5917.         The filename should be set using <code>msg.filename</code>. Optionally <code>msg.description</code> can be added for the body text.</p>
  5918.         <p>Alternatively you may provide <code>msg.attachments</code> which should contain an array of one or
  5919.         more attachments in <a href="https://www.npmjs.com/package/nodemailer#attachments" target="_new">nodemailer</a> format.</p>
  5920.         <p>If required by your recipient you may also pass in a <code>msg.envelope</code> object, typically containing extra from and to properties.</p>
  5921.         <p>Note: uses SMTP with SSL to port 465.</p>
  5922.     </script><script type="text/x-red" data-help-name="e-mail in">
  5923.         <p>Repeatedly gets a <b>single email</b> from an IMAP server and forwards on as a msg if not already seen.</p>
  5924.         <p>The subject is loaded into <code>msg.topic</code> and <code>msg.payload</code> is the plain text body.
  5925.            If there is text/html then that is returned in <code>msg.html</code>. <code>msg.from</code> and <code>msg.date</code> are also set if you need them.</p>
  5926.         <p>Additionally <code>msg.header</code> contains the complete header object including
  5927.         <i>to</i>, <i>cc</i> and other potentially useful properties.</p>
  5928.         <p>Uses the imap module.</p>
  5929.         <p><b>Note:</b> this node <i>only</i> gets the most recent single email from the inbox, so set the repeat (polling) time appropriately.</p>
  5930.         <p>Note: uses IMAP with SSL to port 993.</p>
  5931.         <p>Any attachments supplied in the incoming email can be found in the <code>msg.attachments</code> property.  This will be an array of objects where
  5932.         each object represents a specific attachments.  The format of the object is:</p>
  5933.      
  5934.     <pre>
  5935.     {
  5936.       contentType:        // The MIME content description
  5937.       fileName:           // A suggested file name associated with this attachment
  5938.       transferEncoding:   // How was the original email attachment encodded?
  5939.       contentDisposition: // Unknown
  5940.       generatedFileName:  // A suggested file name associated with this attachment
  5941.       contentId:          // A unique generated ID for this attachment
  5942.       checksum:           // A checksum against the data
  5943.       length:             // Size of data in bytes
  5944.       content:            // The actual content of the data contained in a Node.js Buffer object
  5945.                           // We can turn this into a base64 data string with content.toString('base64')
  5946.     }
  5947.     </pre>
  5948.     <p>For POP3, the default port numbers are 110 for plain TCP and 995 for SSL.  For IMAP the port numbers are 143 for plain TCP and 993 for SSL.</p>
  5949.      
  5950.     </script>
  5951.     <script type="text/x-red" data-template-name="feedparse">
  5952.         <div class="form-row">
  5953.             <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="feedparse.label.feedurl"></span></label>
  5954.             <input type="text" id="node-input-url">
  5955.         </div>
  5956.         <div class="form-row">
  5957.             <label for="node-input-interval"><i class="fa fa-repeat"></i> <span data-i18n="feedparse.label.refresh"></span></label>
  5958.             <input type="text" id="node-input-interval" style="width: 50px"> <span data-i18n="feedparse.label.minutes"></span>
  5959.         </div>
  5960.         <div class="form-row">
  5961.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
  5962.             <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
  5963.         </div>
  5964.     </script>
  5965.      
  5966.      
  5967.      
  5968.     <script type="text/javascript">
  5969.         RED.nodes.registerType('feedparse',{
  5970.             category: 'advanced-input',
  5971.             color:"#C0DEED",
  5972.             defaults: {
  5973.                 name: {value:""},
  5974.                 url: {value:"", required:true},
  5975.                 interval: { value:15, required: true,validate:RED.validators.number()}
  5976.             },
  5977.             inputs:0,
  5978.             outputs:1,
  5979.             icon: "feed.png",
  5980.             label: function() {
  5981.                 return this.name||this.url;
  5982.             },
  5983.             labelStyle: function() {
  5984.                 return this.name?"node_label_italic":"";
  5985.             }
  5986.         });
  5987.     </script>
  5988.     <script type="text/x-red" data-help-name="feedparse">
  5989.         <p>Monitors an RSS/atom feed for new entries.</p>
  5990.         <p>The <code>msg.topic</code> contains the original article link. The <code>msg.payload</code>
  5991.         contains the description, and <code>msg.article</code> contains the complete article object,
  5992.         which has properties such as <code>.title</code>, <code>.summary</code>, <code>.date</code> and so on.</p>
  5993.     </script>
  5994.     <script type="text/x-red" data-template-name="rbe">
  5995.         <div class="form-row">
  5996.             <label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="rbe.label.func"></span></label>
  5997.             <select type="text" id="node-input-func" style="width:74%;">
  5998.                 <option value="rbe" data-i18n="rbe.opts.rbe"></option>
  5999.                 <option value="deadband" data-i18n="rbe.opts.deadband"></option>
  6000.                 <option value="narrowband" data-i18n="rbe.opts.narrowband"></option>
  6001.             </select>
  6002.         </div>
  6003.         <div class="form-row" id="node-bandgap">
  6004.             <label for="node-input-gap">&nbsp;</label>
  6005.             <input type="text" id="node-input-gap" data-i18n="[placeholder]rbe.placeholder.bandgap" style="width:70px;">
  6006.             <select type="text" id="node-input-inout" style="width:55%;">
  6007.                 <option value="out" data-i18n="rbe.opts.out"></option>
  6008.                 <option value="in" data-i18n="rbe.opts.in"></option>
  6009.             </select>
  6010.         </div>
  6011.         <div class="form-row" id="node-startvalue">
  6012.             <label for="node-input-start"><i class="fa fa-thumb-tack"/> <span data-i18n="rbe.label.start"></span></label>
  6013.             <input type="text" id="node-input-start" data-i18n="[placeholder]rbe.placeholder.start" style="width:71%;">
  6014.         </div>
  6015.         <div class="form-row">
  6016.             <label for="node-input-name"><i class="fa fa-tag"/> <span data-i18n="rbe.label.name"></span></label>
  6017.             <input type="text" id="node-input-name" data-i18n="[placeholder]rbe.label.name" style="width:71%;">
  6018.         </div>
  6019.     </script>
  6020.      
  6021.      
  6022.      
  6023.     <script type="text/javascript">
  6024.         RED.nodes.registerType("rbe", {
  6025.             color:"#E2D96E",
  6026.             category: 'function',
  6027.             defaults: {
  6028.                 name: {value:""},
  6029.                 func: {value:"rbe"},
  6030.                 gap: {value:"",validate:RED.validators.regex(/^(\d*[.]*\d*|)(%|)$/)},
  6031.                 start: {value:""},
  6032.                 inout: {value:"out"}
  6033.             },
  6034.             inputs:1,
  6035.             outputs:1,
  6036.             icon: "rbe.png",
  6037.             label: function() {
  6038.                 return this.name||this.func||"rbe";
  6039.             },
  6040.             labelStyle: function() {
  6041.                 return this.name?"node_label_italic":"";
  6042.             },
  6043.             oneditprepare: function() {
  6044.                 //$( "#node-input-gap" ).spinner({min:0});
  6045.                 if ($("#node-input-inout").val() === null) {
  6046.                     $("#node-input-inout").val("out");
  6047.                 }
  6048.                 $("#node-input-func").on("change",function() {
  6049.                     if ($("#node-input-func").val() === "rbe") {
  6050.                         $("#node-bandgap").hide();
  6051.                     } else {
  6052.                         $("#node-bandgap").show();
  6053.                     }
  6054.                     if ($("#node-input-func").val() === "narrowband") {
  6055.                         $("#node-startvalue").show();
  6056.                     } else {
  6057.                         $("#node-startvalue").hide();
  6058.                     }
  6059.                 });
  6060.             }
  6061.         });
  6062.     </script>
  6063.     <script type="text/x-red" data-help-name="rbe">
  6064.         <p>Report by Exception node - only passes on data if it has changed.</p>
  6065.         <p>The node can either block until the <code>msg.payload</code> is
  6066.         different to the previous one - <b>rbe</b> mode. This works on numbers, strings, and simple objects.</p>
  6067.         <p>Or it can block until the value changes by a specified amount - <b>deadband</b> mode.</p>
  6068.         <p>In deadband mode the incoming payload must contain a parseable <i>number</i> and is
  6069.         output only if greater than + or - the <i>band gap</i> away from a previous value.</p>
  6070.         <p>Deadband also supports % - only sends if the input differs by more than x% of the original value.</p>
  6071.         <p>It can also ignore outlier values - <b>narrowband</b> mode.</p>
  6072.         <p>In narrowband mode the incoming payload is blocked if it is more than + or - the band gap
  6073.         away from the previous value. Useful for ignoring outliers from a faulty sensor for example.</p>
  6074.         <p>Both Deadband and Narrowband allow comparison against either the <i>previous valid output value</i>, thus
  6075.         ignoring any values out of range; or the <i>previous input value</i>, which resets the set point, thus allowing
  6076.         gradual drift (deadband), or a step change (narrowband).</p>
  6077.         <p><b>Note:</b> This works on a per <code>msg.topic</code> basis. This means that a single rbe node can
  6078.         handle multiple topics at the same time.</p>
  6079.     </script>
  6080.     <script type="text/x-red" data-template-name="twitter-credentials">
  6081.         <div class="form-row" id="node-config-twitter-row"></div>
  6082.         <input type="hidden" id="node-config-input-screen_name">
  6083.     </script>
  6084.      
  6085.     <script type="text/javascript">
  6086.     (function() {
  6087.         var twitterConfigNodeId = null;
  6088.         var twitterConfigNodeIntervalId = null;
  6089.      
  6090.         RED.nodes.registerType('twitter-credentials',{
  6091.             category: 'config',
  6092.             defaults: {
  6093.                 screen_name: {value:""}
  6094.             },
  6095.             credentials: {
  6096.                 screen_name: {type:"text"},
  6097.                 access_token: {type: "password"},
  6098.                 access_token_secret: {type:"password"}
  6099.             },
  6100.             label: function() {
  6101.                 return this.screen_name;
  6102.             },
  6103.             exportable: false,
  6104.             oneditprepare: function() {
  6105.                 var twitterConfigNodeId = this.id;
  6106.                 var clickhere = this._("twitter.label.clickhere");
  6107.                 var twitterID = this._("twitter.label.twitter-id");
  6108.                 function showTwitterAuthStart() {
  6109.                     var pathname = document.location.pathname;
  6110.                     if (pathname.slice(-1) != "/") {
  6111.                         pathname += "/";
  6112.                     }
  6113.                     var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter-credentials/"+twitterConfigNodeId+"/auth/callback");
  6114.                     $("#node-config-dialog-ok").button("disable");
  6115.                     $("#node-config-twitter-row").html('<div style="text-align: center; margin-top: 20px; "><a class="btn" id="node-config-twitter-start" href="twitter-credentials/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank">'+clickhere+'</a></div>');
  6116.                     $("#node-config-twitter-start").click(function() {
  6117.                         twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
  6118.                     });
  6119.                 }
  6120.                 function updateTwitterScreenName(sn) {
  6121.                     $("#node-config-input-screen_name").val(sn);
  6122.                     $("#node-config-twitter-row").html('<label><i class="fa fa-user"></i> '+twitterID+'</label><span class="input-xlarge uneditable-input">'+sn+'</span>');
  6123.                 }
  6124.                 function pollTwitterCredentials(e) {
  6125.                     $.getJSON('credentials/twitter-credentials/'+twitterConfigNodeId,function(data) {
  6126.                         if (data.screen_name) {
  6127.                             updateTwitterScreenName(data.screen_name);
  6128.                             twitterConfigNodeIntervalId = null;
  6129.                             $("#node-config-dialog-ok").button("enable");
  6130.                         } else {
  6131.                             twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
  6132.                         }
  6133.                     })
  6134.                 }
  6135.                 if (!this.screen_name || this.screen_name === "") {
  6136.                     showTwitterAuthStart();
  6137.                 } else {
  6138.                     if (this.credentials.screen_name) {
  6139.                         updateTwitterScreenName(this.credentials.screen_name);
  6140.                     } else {
  6141.                         showTwitterAuthStart();
  6142.                     }
  6143.                 }
  6144.             },
  6145.             oneditsave: function() {
  6146.                 if (twitterConfigNodeIntervalId) {
  6147.                     window.clearTimeout(twitterConfigNodeIntervalId);
  6148.                 }
  6149.             },
  6150.             oneditcancel: function(adding) {
  6151.                 if (twitterConfigNodeIntervalId) {
  6152.                     window.clearTimeout(twitterConfigNodeIntervalId);
  6153.                 }
  6154.             }
  6155.         });
  6156.     })();
  6157.     </script>
  6158.      
  6159.     <script type="text/x-red" data-template-name="twitter in">
  6160.         <div class="form-row">
  6161.             <label for="node-input-twitter"><i class="fa fa-user"></i> <span data-i18n="twitter.label.twitter-id"></span></label>
  6162.             <input type="text" id="node-input-twitter">
  6163.         </div>
  6164.         <div class="form-row">
  6165.             <label for="node-input-user"><i class="fa fa-search"></i> <span data-i18n="twitter.label.search"></span></label>
  6166.             <select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;">
  6167.                 <option value="false" data-i18n="twitter.search.public"></option>
  6168.                 <option value="true" data-i18n="twitter.search.follow"></option>
  6169.                 <option value="user" data-i18n="twitter.search.user"></option>
  6170.                 <option value="dm" data-i18n="twitter.search.direct"></option>
  6171.                 <option value="event" data-i18n="twitter.search.events"></option>
  6172.             </select>
  6173.         </div>
  6174.         <div class="form-row" id="node-input-tags-row">
  6175.             <label for="node-input-tags"><i class="fa fa-tags"></i> <span id="node-input-tags-label" data-i18n="twitter.label.for"></span></label>
  6176.             <input type="text" id="node-input-tags" data-i18n="[placeholder]twitter.placeholder.for">
  6177.         </div>
  6178.         <div class="form-row">
  6179.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
  6180.             <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
  6181.         </div>
  6182.         <div class="form-tips"><span data-i18n="[html]twitter.tip"></span></div>
  6183.     </script>
  6184.      
  6185.      
  6186.      
  6187.     <script type="text/javascript">
  6188.         RED.nodes.registerType('twitter in',{
  6189.             category: 'social-input',
  6190.             color:"#C0DEED",
  6191.             defaults: {
  6192.                 twitter: {type:"twitter-credentials",required:true},
  6193.                 tags: {value:""},
  6194.                 user: {value:"false",required:true},
  6195.                 name: {value:""},
  6196.                 topic: {value:"tweets"},
  6197.                 inputs: {value:0}
  6198.             },
  6199.             inputs: 0,
  6200.             outputs: 1,
  6201.             icon: "twitter.png",
  6202.             label: function() {
  6203.                 if (this.name) {
  6204.                     return this.name;
  6205.                 }
  6206.                 var uname = RED.nodes.node(this.twitter);
  6207.                 if (this.user == "dm") {
  6208.                     return (uname?uname.label()+" ":"")+this._("twitter.label.dmslabel");
  6209.                 } else if (this.user == "event") {
  6210.                     var user = RED.nodes.node(this.twitter);
  6211.                     return (user?user.label()+" ":"")+this._("twitter.label.eventslabel");
  6212.                 } else if (this.user == "user") {
  6213.                     return this.tags+" "+this._("twitter.label.tweetslabel");
  6214.                 }
  6215.                 else if (this.user == "true") {
  6216.                     return this._("twitter.label.followers") + (uname?(" "+uname.label()):"");
  6217.                 }
  6218.                 return "twitter";
  6219.             },
  6220.             labelStyle: function() {
  6221.                 return this.name?"node_label_italic":"";
  6222.             },
  6223.             oneditprepare: function() {
  6224.                 var userlabel = this._("twitter.label.user");
  6225.                 var userph = this._("twitter.placeholder.user");
  6226.                 var forlabel = this._("twitter.label.for");
  6227.                 var forph = this._("twitter.placeholder.for");
  6228.                 $("#node-input-user").change(function() {
  6229.                     var type = $("#node-input-user option:selected").val();
  6230.                     if (type == "user") {
  6231.                         $("#node-input-tags-row").show();
  6232.                         $("#node-input-tags-label").html(userlabel);
  6233.                         $("#node-input-tags").attr("placeholder",userph);
  6234.                     } else if ((type == "dm")||(type == "true")||(type == "event")) {
  6235.                         $("#node-input-tags-row").hide();
  6236.                     } else {
  6237.                         $("#node-input-tags-row").show();
  6238.                         $("#node-input-tags-label").html(forlabel);
  6239.                         $("#node-input-tags").attr("placeholder",forph);
  6240.                     }
  6241.                 });
  6242.                 $("#node-input-user").change();
  6243.             },
  6244.             oneditsave: function() {
  6245.                 if ($('#node-input-tags').val() === '' && $("#node-input-user option:selected").val() === 'false') {
  6246.                     this.inputs = 1;
  6247.                 }
  6248.                 else {
  6249.                     //set back the default state of 0 inputs
  6250.                     //this.inputs = 0;
  6251.                 }
  6252.             }
  6253.         });
  6254.     </script>
  6255.      
  6256.      
  6257.     <script type="text/x-red" data-template-name="twitter out">
  6258.         <div class="form-row">
  6259.             <label for="node-input-twitter"><i class="fa fa-user"></i> <span data-i18n="twitter.label.twitter-id"></span></label>
  6260.             <input type="text" id="node-input-twitter">
  6261.         </div>
  6262.         <div class="form-row">
  6263.             <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
  6264.             <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
  6265.         </div>
  6266.     </script>
  6267.      
  6268.      
  6269.      
  6270.     <script type="text/javascript">
  6271.         RED.nodes.registerType('twitter out',{
  6272.             category: 'social-output',
  6273.             color:"#C0DEED",
  6274.             defaults: {
  6275.                 twitter: {type:"twitter-credentials",required:true},
  6276.                 name: {value:"Tweet"}
  6277.             },
  6278.             inputs:1,
  6279.             outputs:0,
  6280.             icon: "twitter.png",
  6281.             align: "right",
  6282.             label: function() {
  6283.                 return this.name;
  6284.             }
  6285.         });
  6286.     </script>
  6287.     <script type="text/x-red" data-help-name="twitter in">
  6288.         <p>Twitter input node. Can be used to search either:
  6289.         <ul><li>the public stream for tweets containing the configured search term</li>
  6290.             <li>all the tweets from accounts that the authenticated user follows</li>
  6291.             <li>all tweets by specified users</li>
  6292.             <li>direct messages received by the authenticated user</li>
  6293.             <li>twitter events for the authenticated user</li>
  6294.         </ul></p>
  6295.         <p>Use space for <i>and</i> and comma , for <i>or</i> when searching for multiple terms.
  6296.         If you want to pass in the search term(s) via the <code>msg.payload</code>, leave the <b>for</b> field blank.</p>
  6297.         <p>Sets the <code>msg.topic</code> to <i>tweets/</i> and then appends the senders screen name.</p>
  6298.         <p>Sets <code>msg.location</code> to the tweeters location if known.</p>
  6299.         <p>When returning events it sets the <code>msg.payload</code> to the twitter event, a full list is documented by
  6300.         <a href="https://dev.twitter.com/streaming/overview/messages-types#Events_event" target="_new">Twitter</a>.</p>
  6301.         <p>Sets <code>msg.tweet</code> to the full tweet object as documented by <a href="https://dev.twitter.com/overview/api/tweets" target="_new">Twitter</a>.
  6302.      
  6303.         <p><b>Note</b>: This node is not connected to the FireHose, so will not return 100% of all tweets to a busy @id or #hashtag.</p>
  6304.         <p><b>Note:</b> when set to follow specific users, or your direct messages, the node is subject to
  6305.           the rate limiting of the Twitter API. If you deploy the flows multiple times within a 15 minute window, you may
  6306.           exceed the limit and will see errors from the node. These errors will clear automatically when the current 15
  6307.           minute window passes.</p>
  6308.     </script><script type="text/x-red" data-help-name="twitter out">
  6309.         <p>Twitter out node. Tweets the <code>msg.payload</code>.</p>
  6310.         <p>To send a Direct Message (DM) - use a payload like "D {username} {message}"</p>
  6311.         <p>If <code>msg.media</code> exists and is a Buffer object, this node will treat it
  6312.            as an image and attach it to the tweet.</p>
  6313.         <p>If <code>msg.params</code> exists and is an object of name:value pairs,
  6314.         this node will treat it as parameters for the update request.</p>
  6315.     </script>
Add Comment
Please, Sign In to add comment