mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'dev' of https://github.com/node-red/node-red into dev
This commit is contained in:
		
							
								
								
									
										56
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,59 @@ | ||||
| #### 1.0.6: Maintenance Release | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Update to JSONata 1.8.3 | ||||
|  - #2536 Handle clone of null in utils | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Prevent button label wrapping in typedInput | ||||
|  - Handle error objects when reporting in palette manager | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - Inject: Revert to cron 1.7.2 | ||||
|  - UDP: when reusing input socket honour the broadcast mode. | ||||
|  | ||||
| #### 1.0.5: Maintenance Release | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - #2500 Support for context stores using JSONata and evaluateNodeProperty() | ||||
|  - Add better handling of host-key-verify error with projects | ||||
|  - #2517 Handle false values in $env() properly | ||||
|  - #2514 Ensure complete node scope is remapped in subflows | ||||
|  - #2513 Flows/subflows must preinitialise their context objects | ||||
|  - Clear node.close timeout to avoid unnecessary work on restart | ||||
|  - #2532 Set flow.disabled when disabled property is false | ||||
|  - #2522 Ensure file context does not write 'undefined' to store | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - #2489 Fix XPath in UI tests | ||||
|  - #2504 Fix paletteCategories order | ||||
|  - #2501 Add page objects for UI testing | ||||
|  - #2494 Check node props when deciding if pasted node can splice links | ||||
|  - #2521 Don't double-sanitize node name in debug sidebar | ||||
|  - #2519 German i18n updates | ||||
|  - #2523 Update nodeTabMap when replacing unknown nodes | ||||
|  - Update TypedInput to use flexbox and remove resizing code | ||||
|  - Handle nodes with no wires array | ||||
|  - Do not collapse whitespace in Debug string messages | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - File: Remove old legacy wording from file node info to stop confusing users. | ||||
|  - Join: Ensure join node handles missing buffer joiner when not in string mode | ||||
|  - Exec: make exec node logging consistent with itself. (only be verbose when in verbose mode) | ||||
|  - Trigger: reset default timeout value when switching away from wait for reset | ||||
|  - Join: Fix join to not crash on appending invalid types to buffer. | ||||
|  - MQTT out: Add warning if topic contains + or # | ||||
|  - #2502 WebSocket i18n update | ||||
|  - #2508 Add Japanese translation for join node | ||||
|  - TCP out: tidy up select of which rows to display | ||||
|  | ||||
|  | ||||
| #### 1.0.4: Maintenance Release | ||||
|  | ||||
| Runtime | ||||
|   | ||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								package.json
									
									
									
									
									
								
							| @@ -32,9 +32,9 @@ | ||||
|         "clone": "2.1.2", | ||||
|         "content-type": "1.0.4", | ||||
|         "cookie": "0.4.0", | ||||
|         "cookie-parser": "1.4.4", | ||||
|         "cookie-parser": "1.4.5", | ||||
|         "cors": "2.8.5", | ||||
|         "cron": "1.8.2", | ||||
|         "cron": "1.7.2", | ||||
|         "denque": "1.4.1", | ||||
|         "express": "4.17.1", | ||||
|         "express-session": "1.17.0", | ||||
| @@ -47,18 +47,18 @@ | ||||
|         "is-utf8": "0.2.1", | ||||
|         "js-yaml": "3.13.1", | ||||
|         "json-stringify-safe": "5.0.1", | ||||
|         "jsonata": "1.8.1", | ||||
|         "jsonata": "1.8.3", | ||||
|         "lodash.clonedeep": "^4.5.0", | ||||
|         "media-typer": "1.1.0", | ||||
|         "memorystore": "1.6.2", | ||||
|         "mime": "2.4.4", | ||||
|         "mqtt": "2.18.8", | ||||
|         "multer": "1.4.2", | ||||
|         "mustache": "4.0.0", | ||||
|         "mustache": "4.0.1", | ||||
|         "node-red-node-rbe": "^0.2.6", | ||||
|         "node-red-node-sentiment": "^0.1.6", | ||||
|         "node-red-node-tail": "^0.1.0", | ||||
|         "nopt": "4.0.1", | ||||
|         "nopt": "4.0.3", | ||||
|         "oauth2orize": "1.11.0", | ||||
|         "on-headers": "1.0.2", | ||||
|         "passport": "0.4.1", | ||||
| @@ -67,16 +67,16 @@ | ||||
|         "raw-body": "2.4.1", | ||||
|         "request": "2.88.0", | ||||
|         "semver": "6.3.0", | ||||
|         "uglify-js": "3.8.0", | ||||
|         "uglify-js": "3.8.1", | ||||
|         "when": "3.7.8", | ||||
|         "ws": "6.2.1", | ||||
|         "xml2js": "0.4.23" | ||||
|     }, | ||||
|     "optionalDependencies": { | ||||
|         "bcrypt": "3.0.6" | ||||
|         "bcrypt": "3.0.8" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "marked": "0.8.0", | ||||
|         "marked": "0.8.2", | ||||
|         "dompurify": "2.0.8", | ||||
|         "grunt": "~1.0.4", | ||||
|         "grunt-chmod": "~1.1.1", | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
|         "express": "4.17.1", | ||||
|         "memorystore": "1.6.2", | ||||
|         "mime": "2.4.4", | ||||
|         "mustache": "4.0.0", | ||||
|         "mustache": "4.0.1", | ||||
|         "oauth2orize": "1.11.0", | ||||
|         "passport-http-bearer": "1.0.1", | ||||
|         "passport-oauth2-client-password": "0.1.2", | ||||
|   | ||||
| @@ -1224,7 +1224,7 @@ RED.nodes = (function() { | ||||
|                                     defaults: {}, | ||||
|                                     label: "unknown: "+n.type, | ||||
|                                     labelStyle: "red-ui-flow-node-label-italic", | ||||
|                                     outputs: n.outputs||n.wires.length, | ||||
|                                     outputs: n.outputs|| (n.wires && n.wires.length) || 0, | ||||
|                                     set: registry.getNodeSet("node-red/unknown") | ||||
|                                 } | ||||
|                             } else { | ||||
|   | ||||
| @@ -737,11 +737,13 @@ | ||||
|                             this.optionExpandButton.shown = false; | ||||
|                         } | ||||
|                         if (this.optionSelectTrigger) { | ||||
|                             this.optionSelectTrigger.show(); | ||||
|                             this.optionSelectTrigger.css({"display":"inline-flex"}); | ||||
|                             if (!opt.hasValue) { | ||||
|                                 this.optionSelectTrigger.css({"flex-grow":1}) | ||||
|                                 this.elementDiv.hide(); | ||||
|                                 this.valueLabelContainer.hide(); | ||||
|                             } else { | ||||
|                                 this.optionSelectTrigger.css({"flex-grow":0}) | ||||
|                                 this.elementDiv.show(); | ||||
|                                 this.valueLabelContainer.hide(); | ||||
|                             } | ||||
|   | ||||
| @@ -223,7 +223,11 @@ RED.palette.editor = (function() { | ||||
|                         var setElements = nodeEntry.sets[setName]; | ||||
|                         if (set.err) { | ||||
|                             errorCount++; | ||||
|                             $("<li>").text(set.err).appendTo(nodeEntry.errorList); | ||||
|                             var errMessage = set.err; | ||||
|                             if (set.err.message) { | ||||
|                                 errMessage = set.err.message; | ||||
|                             } | ||||
|                             $("<li>").text(errMessage).appendTo(nodeEntry.errorList); | ||||
|                         } | ||||
|                         if (set.enabled) { | ||||
|                             activeTypeCount += set.types.length; | ||||
|   | ||||
| @@ -217,6 +217,10 @@ | ||||
| .red-ui-debug-msg-type-number { color: $debug-message-text-color-msg-type-number; }; | ||||
| .red-ui-debug-msg-type-number-toggle { cursor: pointer;} | ||||
|  | ||||
| .red-ui-debug-msg-type-string { | ||||
|     white-space: pre-wrap; | ||||
| } | ||||
|  | ||||
| .red-ui-debug-msg-row { | ||||
|     display: block; | ||||
|     padding: 4px 2px 2px; | ||||
|   | ||||
| @@ -760,7 +760,7 @@ button.red-ui-toggleButton.toggle { | ||||
|             .red-ui-typedInput-value-label,.red-ui-typedInput-option-label { | ||||
|                 select,.placeholder-input  { | ||||
|                     margin: 3px; | ||||
|                     height: 26px; | ||||
|                     height: 24px; | ||||
|                     width: calc(100% - 10px); | ||||
|                     padding-left: 3px; | ||||
|                 } | ||||
|   | ||||
| @@ -110,9 +110,9 @@ button.red-ui-typedInput-option-trigger | ||||
|     background: $form-button-background; | ||||
|     height: 32px; | ||||
|     line-height: 30px; | ||||
|     min-width: 23px; | ||||
|     vertical-align: middle; | ||||
|     color: $form-text-color; | ||||
|     white-space: nowrap; | ||||
|     i.red-ui-typedInput-icon { | ||||
|         margin-left: 1px; | ||||
|         margin-right: 2px; | ||||
| @@ -174,25 +174,21 @@ button.red-ui-typedInput-option-trigger { | ||||
|     padding: 0 0 0 0; | ||||
|     position:relative; | ||||
|     flex-grow: 1; | ||||
|     line-height: 32px; | ||||
|     display: inline-flex; | ||||
|     .red-ui-typedInput-option-label { | ||||
|         background:$form-button-background; | ||||
|         color: $form-text-color; | ||||
|         position:absolute; | ||||
|         left:0; | ||||
|         right:23px; | ||||
|         top: 0; | ||||
|         padding: 0 5px 0 8px; | ||||
|         i.red-ui-typedInput-icon { | ||||
|             margin-right: 4px; | ||||
|         } | ||||
|         flex-grow: 1; | ||||
|         padding: 0 0 0 8px; | ||||
|         display:inline-block; | ||||
|     } | ||||
|     .red-ui-typedInput-option-caret { | ||||
|         top: 0; | ||||
|         position: absolute; | ||||
|         right: 0; | ||||
|         bottom: 0; | ||||
|         width: 17px; | ||||
|         padding-left: 5px; | ||||
|         flex-grow: 0; | ||||
|         display:inline-block; | ||||
|         width: 23px; | ||||
|         text-align: center; | ||||
|         height: 100%; | ||||
|         &:before { | ||||
|             content:''; | ||||
|             display: inline-block; | ||||
|   | ||||
| @@ -16,14 +16,12 @@ | ||||
|  | ||||
| <script type="text/html" data-template-name="inject"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label> | ||||
|         <input type="text" id="node-input-payload" style="width:70%"> | ||||
|         <input type="hidden" id="node-input-payloadType"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label> | ||||
|         <input type="text" id="node-input-topic"> | ||||
|      | ||||
|     <div class="form-row node-input-property-container-row"> | ||||
|         <ol id="node-input-property-container"></ol> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row" id="node-once"> | ||||
| @@ -114,12 +112,7 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-tips" data-i18n="[html]inject.tip"></div> | ||||
| </script> | ||||
| <style> | ||||
|     .inject-time-row { | ||||
| @@ -160,36 +153,85 @@ | ||||
|         color:"#a6bbcf", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|             props:{value:[{p:"payload",v:"",vt:"date"},{p:"topic",v:"",vt:"str"}]}, | ||||
|             repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 2147483))) }}, | ||||
|             crontab: {value:""}, | ||||
|             once: {value:false}, | ||||
|             onceDelay: {value:0.1} | ||||
|             onceDelay: {value:0.1}, | ||||
|             /* Legacy */ | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|             /* */ | ||||
|         }, | ||||
|         icon: "inject.svg", | ||||
|         inputs:0, | ||||
|         outputs:1, | ||||
|         outputLabels: function(index) { | ||||
|             var lab = this.payloadType; | ||||
|             if (lab === "json") { | ||||
|                 try { | ||||
|                     lab = typeof JSON.parse(this.payload); | ||||
|                     if (lab === "object") { | ||||
|                         if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; } | ||||
|             var lab = ''; | ||||
|              | ||||
|             // if only payload and topic - display payload type | ||||
|             // if only one property - show it's type | ||||
|             // if more than one property (other than payload and topic) - show "x properties" where x is the number of properties. | ||||
|  | ||||
|             // this.props will not be an array for legacy inject nodes until they are re-deployed | ||||
|             if (Array.isArray(this.props)) { | ||||
|                 var propertyCount = this.props.length; | ||||
|                 var payloadProperty = this.props.find(p => p.p === 'payload'); | ||||
|                 var topicProperty = this.props.find(p => p.p === 'topic' && p.vt === 'str'); | ||||
|                  | ||||
|                 if (payloadProperty && topicProperty) { | ||||
|                     lab = payloadProperty.vt; | ||||
|                 } else if (propertyCount > 1){ | ||||
|                     lab = propertyCount + " " + this._("inject.label.properties"); | ||||
|                 } else if(propertyCount === 1){ | ||||
|                     lab = this.props[0].vt; | ||||
|                 } | ||||
|                  | ||||
|             } else { | ||||
|  | ||||
|                 lab = this.payloadType; | ||||
|                 if (lab === "json") { | ||||
|                     try { | ||||
|                         lab = typeof JSON.parse(this.payload); | ||||
|                         if (lab === "object") { | ||||
|                             if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; } | ||||
|                         } | ||||
|                     } catch(e) { | ||||
|                         return this._("inject.label.invalid"); | ||||
|                     } | ||||
|                 } catch(e) { | ||||
|                     return this._("inject.label.invalid"); } | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             var name = "inject.label."+lab; | ||||
|             var label = this._(name); | ||||
|             if (name !== label) { | ||||
|                 return label; | ||||
|             } | ||||
|             if (name !== label) { lab = label; } | ||||
|  | ||||
|             return lab; | ||||
|         }, | ||||
|         label: function() { | ||||
|         label: function() {         | ||||
|             if (Array.isArray(this.props)) { | ||||
|                 // find the payload & topic | ||||
|                 var payloadProperty = this.props.find(p => p.p === 'payload'); | ||||
|                 var topicProperty = this.props.find(p => p.p === 'topic' && p.vt === 'str'); | ||||
|                  | ||||
|                 // If no payload/topic are found, use the first property instead | ||||
|                 if(this.props[0]){ | ||||
|                     payloadProperty = payloadProperty === undefined ? this.props[0] : payloadProperty; | ||||
|                     topicProperty = topicProperty === undefined ? {v: payloadProperty.p} : topicProperty; // if no topic, use the property name of the payload | ||||
|                 } | ||||
|  | ||||
|                 var payload = payloadProperty === undefined ? "" : payloadProperty.v; | ||||
|                 var payloadType = payloadProperty === undefined ? "str" : payloadProperty.vt; | ||||
|                 var topic = topicProperty === undefined ? "" : topicProperty.v;                 | ||||
|             } else { | ||||
|                 /* Legacy */ | ||||
|                 var payload = this.payload; | ||||
|                 var payloadType = this.payloadType; | ||||
|                 var topic = this.topic; | ||||
|             } | ||||
|              | ||||
|              | ||||
|             var suffix = ""; | ||||
|             // if fire once then add small indication | ||||
|             if (this.once) { | ||||
| @@ -201,27 +243,27 @@ | ||||
|             } | ||||
|             if (this.name) { | ||||
|                 return this.name+suffix; | ||||
|             } else if (this.payloadType === "string" || | ||||
|                     this.payloadType === "str" || | ||||
|                     this.payloadType === "num" || | ||||
|                     this.payloadType === "bool" || | ||||
|                     this.payloadType === "json") { | ||||
|                 if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) { | ||||
|                     return this.topic + ":" + this.payload+suffix; | ||||
|                 } else if (this.payload.length > 0 && this.payload.length < 24) { | ||||
|                     return this.payload+suffix; | ||||
|             } else if (payloadType === "string" || | ||||
|                     payloadType === "str" || | ||||
|                     payloadType === "num" || | ||||
|                     payloadType === "bool" || | ||||
|                     payloadType === "json") { | ||||
|                 if ((topic !== "") && ((topic.length + payload.length) <= 32)) { | ||||
|                     return topic + ":" + payload+suffix; | ||||
|                 } else if (payload.length > 0 && payload.length < 24) { | ||||
|                     return payload+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.inject")+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'date') { | ||||
|                 if ((this.topic !== "") && (this.topic.length <= 16)) { | ||||
|                     return this.topic + ":" + this._("inject.timestamp")+suffix; | ||||
|             } else if (payloadType === 'date' || payloadType === 'bin' || payloadType === 'env') { | ||||
|                 if ((topic !== "") && (topic.length <= 16)) { | ||||
|                     return topic + ":" + this._(`inject.label.${payloadType}`)+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.timestamp")+suffix; | ||||
|                     return this._(`inject.label.${payloadType}`)+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'flow' || this.payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(this.payload); | ||||
|                 return this.payloadType+"."+key.key+suffix; | ||||
|             } else if (payloadType === 'flow' || payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(payload); | ||||
|                 return payloadType+"."+key.key+suffix; | ||||
|             } else { | ||||
|                 return this._("inject.inject")+suffix; | ||||
|             } | ||||
| @@ -259,6 +301,10 @@ | ||||
|                     $("#node-once").hide(); | ||||
|                     $("#node-input-once").prop('checked', false); | ||||
|                 } | ||||
|                  | ||||
|                 // Scroll down | ||||
|                 var scrollDiv = $("#dialog-form").parent(); | ||||
|                 scrollDiv.scrollTop(scrollDiv.prop('scrollHeight')); | ||||
|             }); | ||||
|  | ||||
|             $("#node-input-once").on("change", function() { | ||||
| @@ -383,8 +429,117 @@ | ||||
|             $("#inject-time-type-select").trigger("change"); | ||||
|             $("#inject-time-interval-time-start").trigger("change"); | ||||
|  | ||||
|             /* */ | ||||
|  | ||||
|             function resizeItem(item) { | ||||
|                 var newWidth = item.width(); | ||||
|  | ||||
|                 item.find('.node-input-prop-property-name').typedInput("width", '155px'); | ||||
|                 item.find('.node-input-prop-property-value').typedInput("width", `${newWidth - 180}px`); | ||||
|             } | ||||
|  | ||||
|             $('#node-input-property-container').css('min-height','150px').css('min-width','450px').editableList({ | ||||
|                 addItem: function(container,i,opt) { | ||||
|                     var prop = opt; | ||||
|                     if (!prop.hasOwnProperty('p')) { | ||||
|                         prop = {p:"",v:"",vt:"str"}; | ||||
|                     } | ||||
|                     container.css({ | ||||
|                         overflow: 'hidden', | ||||
|                         whiteSpace: 'nowrap' | ||||
|                     }); | ||||
|                     var row = $('<div/>').appendTo(container); | ||||
|  | ||||
|                     var propertyName = $('<input/>',{class:"node-input-prop-property-name",type:"text"}) | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({types:['msg']}); | ||||
|  | ||||
|                     $('<div/>',{style: 'display:inline-block; padding:0px 4px 0px 4px;'}) | ||||
|                         .text('=') | ||||
|                         .appendTo(row); | ||||
|  | ||||
|                     var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"}) | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']}); | ||||
|  | ||||
|                     propertyName.typedInput('value',prop.p); | ||||
|  | ||||
|                     propertyValue.typedInput('value',prop.v); | ||||
|                     propertyValue.typedInput('type',prop.vt); | ||||
|  | ||||
|                     resizeItem(container); | ||||
|                 }, | ||||
|                 resizeItem: resizeItem, | ||||
|                 removable: true, | ||||
|                 sortable: true | ||||
|             }); | ||||
|  | ||||
|             if (!this.props) { | ||||
|                 var payload = { | ||||
|                     p:'payload', | ||||
|                     v: this.payload ? this.payload : '', | ||||
|                     vt:this.payloadType ? this.payloadType : 'date' | ||||
|                 }; | ||||
|                 var topic = { | ||||
|                     p:'topic', | ||||
|                     v: this.topic ? this.topic : '', | ||||
|                     vt:'string' | ||||
|                 } | ||||
|  | ||||
|                 this.props = [payload,topic]; | ||||
|             } | ||||
|  | ||||
|             for (var i=0; i<this.props.length; i++) { | ||||
|                 var prop = this.props[i]; | ||||
|                 $("#node-input-property-container").editableList('addItem',prop); | ||||
|             } | ||||
|              | ||||
|             /* Experimental paste object | ||||
|              * This allows you to copy an object to your clipboard from the debug and then paste it back into an inject node | ||||
|              */ | ||||
|             // $("#dialog-form").on('paste', function(e) { | ||||
|             //   var pasteData = e.originalEvent.clipboardData.getData('text'); | ||||
|             //   try{ | ||||
|             //       var pasteObject = JSON.parse(pasteData); | ||||
|             //   } catch(e){ } | ||||
|             // | ||||
|             //   if(pasteObject){ | ||||
|             //       for(var p in pasteObject){ | ||||
|             //           if(p === '_msgid') continue; | ||||
|             //           var v = pasteObject[p]; | ||||
|             //           var vt = 'json'; | ||||
|             // | ||||
|             //           // Remove existing property before adding to avoid duplicates | ||||
|             //           $(`#node-input-property-container .node-input-prop-property-name[value=${p}]`).closest('.red-ui-editableList-item-content').parent().remove(); | ||||
|             // | ||||
|             //           if(typeof v === 'string'){ | ||||
|             //               vt = 'str'; | ||||
|             //           } else if (typeof v === "boolean") { | ||||
|             //               vt = 'bool'; | ||||
|             //           } else if (!isNaN(v)) { | ||||
|             //               vt = 'num'; | ||||
|             //           } else if(Array.isArray(v) && v.every(e => Number.isInteger(e) && e >= 0 && e <=255)) { // Fuzzy buffer detection | ||||
|             //               vt = 'bin'; | ||||
|             //               v = JSON.stringify(pasteObject[p]); | ||||
|             //           } else { | ||||
|             //               vt = 'json'; | ||||
|             //               v = JSON.stringify(pasteObject[p]); | ||||
|             //           } | ||||
|             // | ||||
|             //           var prop = {p, v, vt}; | ||||
|             //           $("#node-input-property-container").editableList('addItem',prop); | ||||
|             //       } | ||||
|             //   } | ||||
|             // }); | ||||
|  | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             /* Cleanup Legacy */ | ||||
|             delete this.payload; | ||||
|             delete this.payloadType | ||||
|             delete this.topic | ||||
|             /* */ | ||||
|              | ||||
|             var repeat = ""; | ||||
|             var crontab = ""; | ||||
|             var type = $("#inject-time-type-select").val(); | ||||
| @@ -474,6 +629,22 @@ | ||||
|  | ||||
|             $("#node-input-repeat").val(repeat); | ||||
|             $("#node-input-crontab").val(crontab); | ||||
|  | ||||
|             /* Gather the injected properties of the msg object */ | ||||
|             var props = $("#node-input-property-container").editableList('items'); | ||||
|             var node = this; | ||||
|             node.props= []; | ||||
|             props.each(function(i) { | ||||
|                 var prop = $(this); | ||||
|                 var p = { | ||||
|                     p:prop.find(".node-input-prop-property-name").typedInput('value') | ||||
|                 }; | ||||
|  | ||||
|                 p.v = prop.find(".node-input-prop-property-value").typedInput('value'); | ||||
|                 p.vt = prop.find(".node-input-prop-property-value").typedInput('type'); | ||||
|  | ||||
|                 node.props.push(p); | ||||
|             }); | ||||
|         }, | ||||
|         button: { | ||||
|             enabled: function() { | ||||
| @@ -483,12 +654,7 @@ | ||||
|                 if (this.changed) { | ||||
|                     return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning"); | ||||
|                 } | ||||
|                 var payload = this.payload; | ||||
|                 if ((this.payloadType === 'flow') || | ||||
|                     (this.payloadType === 'global')) { | ||||
|                     var key = RED.utils.parseContextKey(payload); | ||||
|                     payload = this.payloadType+"."+key.key; | ||||
|                 } | ||||
|  | ||||
|                 var label = this._def.label.call(this); | ||||
|                 if (label.length > 30) { | ||||
|                     label = label.substring(0,50)+"..."; | ||||
| @@ -514,6 +680,17 @@ | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|             var rows = $("#dialog-form>div:not(.node-input-property-container-row):visible"); | ||||
|             var height = size.height; | ||||
|             for (var i=0; i<rows.length; i++) { | ||||
|                 height -= $(rows[i]).outerHeight(true); | ||||
|             } | ||||
|             var editorRow = $("#dialog-form>div.node-input-property-container-row"); | ||||
|             height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); | ||||
|             height += 16; | ||||
|             $("#node-input-property-container").editableList('height',height); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -20,9 +20,23 @@ module.exports = function(RED) { | ||||
|  | ||||
|     function InjectNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.topic = n.topic; | ||||
|         this.payload = n.payload; | ||||
|         this.payloadType = n.payloadType; | ||||
|          | ||||
|         /* Handle legacy */ | ||||
|         if(!Array.isArray(n.props)){ | ||||
|             n.props = []; | ||||
|             n.props.push({ | ||||
|                 p:'payload', | ||||
|                 v:n.payload, | ||||
|                 vt:n.payloadType | ||||
|             }); | ||||
|             n.props.push({ | ||||
|                 p:'topic', | ||||
|                 v:n.topic, | ||||
|                 vt:'str' | ||||
|             }); | ||||
|         } | ||||
|          | ||||
|         this.props = n.props; | ||||
|         this.repeat = n.repeat; | ||||
|         this.crontab = n.crontab; | ||||
|         this.once = n.once; | ||||
| @@ -62,34 +76,27 @@ module.exports = function(RED) { | ||||
|           node.repeaterSetup(); | ||||
|         } | ||||
|  | ||||
|         this.on("input",function(msg) { | ||||
|             msg.topic = this.topic; | ||||
|             if (this.payloadType !== 'flow' && this.payloadType !== 'global') { | ||||
|                 try { | ||||
|                     if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") { | ||||
|                         msg.payload = Date.now(); | ||||
|                     } else if (this.payloadType == null) { | ||||
|                         msg.payload = this.payload; | ||||
|                     } else if (this.payloadType === 'none') { | ||||
|                         msg.payload = ""; | ||||
|                     } else { | ||||
|                         msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg); | ||||
|                     } | ||||
|                     this.send(msg); | ||||
|                     msg = null; | ||||
|                 } catch(err) { | ||||
|                     this.error(err,msg); | ||||
|                 } | ||||
|             } else { | ||||
|                 RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg, function(err,res) { | ||||
|                     if (err) { | ||||
|                         node.error(err,msg); | ||||
|                     } else { | ||||
|                         msg.payload = res; | ||||
|                         node.send(msg); | ||||
|                     } | ||||
|         this.on("input", function(msg) { | ||||
|             var errors = []; | ||||
|  | ||||
|                 }); | ||||
|             this.props.forEach(p => { | ||||
|                 var property = p.p; | ||||
|                 var value = p.v ? p.v : ''; | ||||
|                 var valueType = p.vt ? p.vt : 'str'; | ||||
|  | ||||
|                 if (!property) return; | ||||
|  | ||||
|                 try { | ||||
|                     RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true); | ||||
|                 } catch (err) { | ||||
|                     errors.push(err); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (errors.length) { | ||||
|                 node.error(errors.join('; '), msg); | ||||
|             } else { | ||||
|                 node.send(msg); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,10 @@ | ||||
|  | ||||
| <script type="text/html" data-template-name="change"> | ||||
|     <style> | ||||
|         ol#node-input-rule-container .red-ui-typedInput-container { | ||||
|             flex:1; | ||||
|         } | ||||
|     </style> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
| @@ -76,11 +81,6 @@ | ||||
|             var replace = this._("change.action.replace"); | ||||
|             var regex = this._("change.label.regex"); | ||||
|  | ||||
|             function resizeRule(rule) { | ||||
|                 var newWidth = rule.width(); | ||||
|                 rule.find('.red-ui-typedInput').typedInput("width",newWidth-130); | ||||
|  | ||||
|             } | ||||
|             $('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({ | ||||
|                 addItem: function(container,i,opt) { | ||||
|                     var rule = opt; | ||||
| @@ -106,10 +106,11 @@ | ||||
|                         overflow: 'hidden', | ||||
|                         whiteSpace: 'nowrap' | ||||
|                     }); | ||||
|                     var row1 = $('<div/>').appendTo(container); | ||||
|                     var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container); | ||||
|                     var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container); | ||||
|                     var row4 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container); | ||||
|                     let fragment = document.createDocumentFragment(); | ||||
|                     var row1 = $('<div/>',{style:"display:flex;"}).appendTo(fragment); | ||||
|                     var row2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment); | ||||
|                     var row4 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment); | ||||
|  | ||||
|                     var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1); | ||||
|                     var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del},{v:"move",l:move}]; | ||||
| @@ -124,41 +125,82 @@ | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(to) | ||||
|                         .appendTo(row2); | ||||
|                     var propertyValue = $('<input/>',{class:"node-input-rule-property-value",type:"text"}) | ||||
|  | ||||
|                     function createPropertyValue() { | ||||
|                         return $('<input/>',{class:"node-input-rule-property-value",type:"text"}) | ||||
|                         .appendTo(row2) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']}); | ||||
|                     } | ||||
|  | ||||
|                     var row3_1 = $('<div/>').appendTo(row3); | ||||
|                     var row3_1 = $('<div/>', {style:"display:flex;"}).appendTo(row3); | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(search) | ||||
|                         .appendTo(row3_1); | ||||
|                     var fromValue = $('<input/>',{class:"node-input-rule-property-search-value",type:"text"}) | ||||
|  | ||||
|                     function createFromValue() { | ||||
|                         return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"}) | ||||
|                         .appendTo(row3_1) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']}); | ||||
|                     } | ||||
|  | ||||
|                     var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3); | ||||
|                     var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(row3); | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(replace) | ||||
|                         .appendTo(row3_2); | ||||
|                     var toValue = $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"}) | ||||
|  | ||||
|                     function createToValue() { | ||||
|                         return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"}) | ||||
|                         .appendTo(row3_2) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']}); | ||||
|                     } | ||||
|  | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(to) | ||||
|                         .appendTo(row4); | ||||
|                     var moveValue = $('<input/>',{class:"node-input-rule-property-move-value",type:"text"}) | ||||
|  | ||||
|                     function createMoveValue() { | ||||
|                         return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"}) | ||||
|                         .appendTo(row4) | ||||
|                         .typedInput({default:'msg',types:['msg','flow','global']}); | ||||
|                     } | ||||
|  | ||||
|                     let propertyValue = null; | ||||
|                     let fromValue = null; | ||||
|                     let toValue = null; | ||||
|                     let moveValue = null; | ||||
|  | ||||
|                     selectField.on("change", function() { | ||||
|                         var width = $("#node-input-rule-container").width(); | ||||
|                         var type = $(this).val(); | ||||
|                         if (propertyValue) { | ||||
|                             propertyValue.typedInput('hide'); | ||||
|                         } | ||||
|                         if (fromValue) { | ||||
|                             fromValue.typedInput('hide'); | ||||
|                         } | ||||
|                         if (toValue) { | ||||
|                             toValue.typedInput('hide'); | ||||
|                         } | ||||
|                         if (moveValue) { | ||||
|                             moveValue.typedInput('hide'); | ||||
|                         } | ||||
|  | ||||
|                         if (type == "set") { | ||||
|                             if(!propertyValue) { | ||||
|                                 propertyValue = createPropertyValue(); | ||||
|                             } | ||||
|                             propertyValue.typedInput('show'); | ||||
|                             row2.show(); | ||||
|                             row3.hide(); | ||||
|                             row4.hide(); | ||||
|                         } else if (type == "change") { | ||||
|                             if(!fromValue) { | ||||
|                                 fromValue = createFromValue(); | ||||
|                             } | ||||
|                             fromValue.typedInput('show'); | ||||
|                             if(!toValue) { | ||||
|                                 toValue = createToValue(); | ||||
|                             } | ||||
|                             toValue.typedInput('show'); | ||||
|                             row2.hide(); | ||||
|                             row3.show(); | ||||
|                             row4.hide(); | ||||
| @@ -167,30 +209,48 @@ | ||||
|                             row3.hide(); | ||||
|                             row4.hide(); | ||||
|                         } else if (type == "move") { | ||||
|                             if(!moveValue) { | ||||
|                                 moveValue = createMoveValue(); | ||||
|                             } | ||||
|                             moveValue.typedInput('show'); | ||||
|                             row2.hide(); | ||||
|                             row3.hide(); | ||||
|                             row4.show(); | ||||
|                         } | ||||
|                         resizeRule(container); | ||||
|                     }); | ||||
|  | ||||
|                     selectField.val(rule.t); | ||||
|                     propertyName.typedInput('value',rule.p); | ||||
|                     propertyName.typedInput('type',rule.pt); | ||||
|                     propertyValue.typedInput('value',rule.to); | ||||
|                     propertyValue.typedInput('type',rule.tot); | ||||
|                     moveValue.typedInput('value',rule.to); | ||||
|                     moveValue.typedInput('type',rule.tot); | ||||
|                     fromValue.typedInput('value',rule.from); | ||||
|                     fromValue.typedInput('type',rule.fromt); | ||||
|                     toValue.typedInput('value',rule.to); | ||||
|                     toValue.typedInput('type',rule.tot); | ||||
|                     if (rule.t == "set") { | ||||
|                         if(!propertyValue) { | ||||
|                             propertyValue = createPropertyValue(); | ||||
|                         } | ||||
|                         propertyValue.typedInput('value',rule.to); | ||||
|                         propertyValue.typedInput('type',rule.tot); | ||||
|                     } | ||||
|                     if (rule.t == "move") { | ||||
|                         if(!moveValue) { | ||||
|                             moveValue = createMoveValue(); | ||||
|                         } | ||||
|                         moveValue.typedInput('value',rule.to); | ||||
|                         moveValue.typedInput('type',rule.tot); | ||||
|                     } | ||||
|                     if (rule.t == "change") { | ||||
|                         if(!fromValue) { | ||||
|                             fromValue = createFromValue(); | ||||
|                         } | ||||
|                         fromValue.typedInput('value',rule.from); | ||||
|                         fromValue.typedInput('type',rule.fromt); | ||||
|                         if (!toValue) { | ||||
|                             toValue = createToValue(); | ||||
|                         } | ||||
|                         toValue.typedInput('value',rule.to); | ||||
|                         toValue.typedInput('type',rule.tot); | ||||
|                     } | ||||
|                     selectField.change(); | ||||
|  | ||||
|                     var newWidth = $("#node-input-rule-container").width(); | ||||
|                     resizeRule(container); | ||||
|                     container[0].appendChild(fragment); | ||||
|                 }, | ||||
|                 resizeItem: resizeRule, | ||||
|                 removable: true, | ||||
|                 sortable: true | ||||
|             }); | ||||
|   | ||||
| @@ -47,6 +47,10 @@ | ||||
|         <input type="hidden" id="node-input-op2type"> | ||||
|         <input style="width:70%" type="text" id="node-input-op2" placeholder="0"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label></label> | ||||
|             <input type="checkbox" id="node-input-second" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-second" data-i18n="trigger.second"></label> | ||||
|         </div> | ||||
|     <div class="form-row"> | ||||
|         <label data-i18n="trigger.label.reset" style="width:auto"></label> | ||||
|         <div style="display:inline-block; width:70%;vertical-align:top"> | ||||
| @@ -58,10 +62,13 @@ | ||||
|     <br/> | ||||
|     <div class="form-row"> | ||||
|         <label data-i18n="trigger.for" for="node-input-bytopic"></label> | ||||
|         <select id="node-input-bytopic"> | ||||
|         <select id="node-input-bytopic" style="width:120px;"> | ||||
|             <option value="all" data-i18n="trigger.alltopics"></option> | ||||
|             <option value="topic" data-i18n="trigger.bytopics"></option> | ||||
|         </select> | ||||
|         <span class="form-row" id="node-stream-topic"> | ||||
|             <input type="text" id="node-input-topic" style="width:46%;"/> | ||||
|         </span> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
| @@ -74,6 +81,7 @@ | ||||
|         category: 'function', | ||||
|         color:"#E6E0F8", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             op1: {value:"1", validate: RED.validators.typedInput("op1type")}, | ||||
|             op2: {value:"0", validate: RED.validators.typedInput("op2type")}, | ||||
|             op1type: {value:"val"}, | ||||
| @@ -82,8 +90,9 @@ | ||||
|             extend: {value:"false"}, | ||||
|             units: {value:"ms"}, | ||||
|             reset: {value:""}, | ||||
|             bytopic: {value: "all"}, | ||||
|             name: {value:""} | ||||
|             bytopic: {value:"all"}, | ||||
|             topic: {value:"topic",required:true}, | ||||
|             outputs: {value:1} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:1, | ||||
| @@ -103,6 +112,28 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var that = this; | ||||
|             if (this.topic === undefined) { $("#node-input-topic").val("topic"); } | ||||
|             $("#node-input-topic").typedInput({default:'msg',types:['msg']}); | ||||
|             $("#node-input-bytopic").on("change", function() { | ||||
|                 if ($("#node-input-bytopic").val() === "all") { | ||||
|                     $("#node-stream-topic").hide(); | ||||
|                 } else { | ||||
|                     $("#node-stream-topic").show(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (this.outputs == 2) { $("#node-input-second").prop('checked', true) } | ||||
|             else { $("#node-input-second").prop('checked', false) } | ||||
|          | ||||
|             $("#node-input-second").change(function() { | ||||
|                 if ($("#node-input-second").is(":checked")) { | ||||
|                     that.outputs = 2; | ||||
|                 } | ||||
|                 else  { | ||||
|                     that.outputs = 1; | ||||
|                 } | ||||
|             }); | ||||
|             $("#node-then-type").on("change", function() { | ||||
|                 if ($(this).val() == "block") { | ||||
|                     $(".node-type-wait").hide(); | ||||
| @@ -177,7 +208,7 @@ | ||||
|             } | ||||
|             if ($("#node-then-type").val() == "loop") { | ||||
|                 $("#node-input-duration").val($("#node-input-duration").val() * -1); | ||||
|             }     | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -24,6 +24,8 @@ module.exports = function(RED) { | ||||
|         this.op2 = n.op2 || "0"; | ||||
|         this.op1type = n.op1type || "str"; | ||||
|         this.op2type = n.op2type || "str"; | ||||
|         this.second = (n.outputs == 2) ? true : false; | ||||
|         this.topic = n.topic || "topic"; | ||||
|  | ||||
|         if (this.op1type === 'val') { | ||||
|             if (this.op1 === 'true' || this.op1 === 'false') { | ||||
| @@ -112,7 +114,7 @@ module.exports = function(RED) { | ||||
|         }); | ||||
|  | ||||
|         var processMessage = function(msg) { | ||||
|             var topic = msg.topic || "_none"; | ||||
|             var topic = RED.util.getMessageProperty(msg,node.topic) || "_none"; | ||||
|             var promise; | ||||
|             if (node.bytopic === "all") { topic = "_none"; } | ||||
|             node.topics[topic] = node.topics[topic] || {}; | ||||
| @@ -197,6 +199,9 @@ module.exports = function(RED) { | ||||
|                                                     node.send(msg2); | ||||
|                                                 } | ||||
|                                                 delete node.topics[topic]; | ||||
|  | ||||
|                                                 if (node.second === true) { node.send([null,msg2]); } | ||||
|                                                 else { node.send(msg2); } | ||||
|                                                 node.status({}); | ||||
|                                             }).catch(err => { | ||||
|                                                 node.error(err); | ||||
| @@ -246,7 +251,8 @@ module.exports = function(RED) { | ||||
|                             } | ||||
|                             delete node.topics[topic]; | ||||
|                             node.status({}); | ||||
|                             node.send(msg2); | ||||
|                             if (node.second === true) { node.send([null,msg2]); } | ||||
|                             else { node.send(msg2); } | ||||
|                         }).catch(err => { | ||||
|                             node.error(err); | ||||
|                         }); | ||||
|   | ||||
| @@ -170,15 +170,14 @@ | ||||
|                     $("#node-input-port-row").hide(); | ||||
|                     $("#node-input-host-row").hide(); | ||||
|                     $("#node-input-end-row").hide(); | ||||
|                 } else if (sockettype == "client"){ | ||||
|                     $("#node-input-port-row").show(); | ||||
|                     $("#node-input-host-row").show(); | ||||
|                     $("#node-input-end-row").show(); | ||||
|                 } else { | ||||
|                     $("#node-input-port-row").show(); | ||||
|                     $("#node-input-end-row").show(); | ||||
|                 } | ||||
|  | ||||
|                 if (sockettype == "client") { | ||||
|                     $("#node-input-host-row").show(); | ||||
|                 } else { | ||||
|                     $("#node-input-host-row").hide(); | ||||
|                     $("#node-input-end-row").show(); | ||||
|                 } | ||||
|             }; | ||||
|             updateOptions(); | ||||
|   | ||||
| @@ -180,6 +180,10 @@ module.exports = function(RED) { | ||||
|         node.tout = setTimeout(function() { | ||||
|             if (udpInputPortsInUse[p]) { | ||||
|                 sock = udpInputPortsInUse[p]; | ||||
|                 if (node.multicast != "false") { | ||||
|                     sock.setBroadcast(true); | ||||
|                     sock.setMulticastLoopback(false); | ||||
|                 } | ||||
|                 node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port})); | ||||
|                 if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } | ||||
|             } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label> | ||||
|         <span data-i18n="csv.label.skip-s"></span> <input type="text" id="node-input-skip" style="width:30px; height:25px;"/> <span data-i18n="csv.label.skip-e"></span><br/> | ||||
|         <span data-i18n="csv.label.skip-s"></span> <input type="text" id="node-input-skip" style="width:40px; height:25px;"/> <span data-i18n="csv.label.skip-e"></span><br/> | ||||
|         <label> </label> | ||||
|         <input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br/> | ||||
|         <label> </label> | ||||
| @@ -49,8 +49,13 @@ | ||||
|         <label style="width:100%;"><span data-i18n="csv.label.o2c"></span></label> | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label> | ||||
|         <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> | ||||
|         <label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label> | ||||
|         <!-- <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> --> | ||||
|         <select style="width:60%" id="node-input-hdrout"> | ||||
|             <option value="none" data-i18n="csv.hdrout.none"></option> | ||||
|             <option value="all" data-i18n="csv.hdrout.all"></option> | ||||
|             <option value="once" data-i18n="csv.hdrout.once"></option> | ||||
|         </select> | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label></label> | ||||
| @@ -73,7 +78,7 @@ | ||||
|             sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)}, | ||||
|             //quo: {value:'"',required:true}, | ||||
|             hdrin: {value:""}, | ||||
|             hdrout: {value:""}, | ||||
|             hdrout: {value:"none"}, | ||||
|             multi: {value:"one",required:true}, | ||||
|             ret: {value:'\\n'}, | ||||
|             temp: {value:""}, | ||||
| @@ -92,6 +97,8 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             if (this.hdrout === false) { this.hdrout = "none"; $("#node-input-hdrout").val("none"); } | ||||
|             if (this.hdrout === true) { this.hdrout = "all"; $("#node-input-hdrout").val("all");} | ||||
|             if (this.strings === undefined) { this.strings = true; $("#node-input-strings").prop('checked', true); } | ||||
|             if (this.skip === undefined) { this.skip = 0; $("#node-input-skip").val("0");} | ||||
|             $("#node-input-skip").spinner({ min:0 }); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ module.exports = function(RED) { | ||||
|         this.lineend = "\n"; | ||||
|         this.multi = n.multi || "one"; | ||||
|         this.hdrin = n.hdrin || false; | ||||
|         this.hdrout = n.hdrout || false; | ||||
|         this.hdrout = n.hdrout || "none"; | ||||
|         this.goodtmpl = true; | ||||
|         this.skip = parseInt(n.skip || 0); | ||||
|         this.store = []; | ||||
| @@ -34,6 +34,8 @@ module.exports = function(RED) { | ||||
|         this.include_empty_strings = n.include_empty_strings || false; | ||||
|         this.include_null_values = n.include_null_values || false; | ||||
|         if (this.parsestrings === undefined) { this.parsestrings = true; } | ||||
|         if (this.hdrout === false) { this.hdrout = "none"; } | ||||
|         if (this.hdrout === true) { this.hdrout = "all"; } | ||||
|         var tmpwarn = true; | ||||
|         var node = this; | ||||
|  | ||||
| @@ -51,14 +53,22 @@ module.exports = function(RED) { | ||||
|             return col; | ||||
|         } | ||||
|         node.template = clean(node.template); | ||||
|         node.hdrSent = false; | ||||
|  | ||||
|         this.on("input", function(msg) { | ||||
|             if (msg.hasOwnProperty("reset")) { | ||||
|                 node.hdrSent = false; | ||||
|             } | ||||
|             if (msg.hasOwnProperty("payload")) { | ||||
|                 if (typeof msg.payload == "object") { // convert object to CSV string | ||||
|                     try { | ||||
|                         var ou = ""; | ||||
|                         if (node.hdrout) { | ||||
|                         if (node.hdrout !== "none" && node.hdrSent === false) { | ||||
|                             if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                 node.template = clean((msg.columns || "").split(",")); | ||||
|                             } | ||||
|                             ou += node.template.join(node.sep) + node.ret; | ||||
|                             if (node.hdrout === "once") { node.hdrSent = true; } | ||||
|                         } | ||||
|                         if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } | ||||
|                         for (var s = 0; s < msg.payload.length; s++) { | ||||
| @@ -77,13 +87,15 @@ module.exports = function(RED) { | ||||
|                                 ou += msg.payload[s].join(node.sep) + node.ret; | ||||
|                             } | ||||
|                             else { | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                     node.template = clean((msg.columns || "").split(",")); | ||||
|                                 } | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '')) { | ||||
|                                     /* istanbul ignore else */ | ||||
|                                     if (tmpwarn === true) { // just warn about missing template once | ||||
|                                         node.warn(RED._("csv.errors.obj_csv")); | ||||
|                                         tmpwarn = false; | ||||
|                                     } | ||||
|                                     ou = ""; | ||||
|                                     for (var p in msg.payload[0]) { | ||||
|                                         /* istanbul ignore else */ | ||||
|                                         if (msg.payload[0].hasOwnProperty(p)) { | ||||
| @@ -127,6 +139,7 @@ module.exports = function(RED) { | ||||
|                             } | ||||
|                         } | ||||
|                         msg.payload = ou; | ||||
|                         msg.columns = node.template.join(','); | ||||
|                         if (msg.payload !== '') { node.send(msg); } | ||||
|                     } | ||||
|                     catch(e) { node.error(e,msg); } | ||||
| @@ -227,6 +240,7 @@ module.exports = function(RED) { | ||||
|                             a.push(o); // add to the array | ||||
|                         } | ||||
|                         var has_parts = msg.hasOwnProperty("parts"); | ||||
|  | ||||
|                         if (node.multi !== "one") { | ||||
|                             msg.payload = a; | ||||
|                             if (has_parts) { | ||||
| @@ -235,12 +249,14 @@ module.exports = function(RED) { | ||||
|                                 } | ||||
|                                 if (msg.parts.index + 1 === msg.parts.count) { | ||||
|                                     msg.payload = node.store; | ||||
|                                     msg.columns = node.template.filter(val => val).join(','); | ||||
|                                     delete msg.parts; | ||||
|                                     node.send(msg); | ||||
|                                     node.store = []; | ||||
|                                 } | ||||
|                             } | ||||
|                             else { | ||||
|                                 msg.columns = node.template.filter(val => val).join(','); | ||||
|                                 node.send(msg); // finally send the array | ||||
|                             } | ||||
|                         } | ||||
| @@ -248,6 +264,7 @@ module.exports = function(RED) { | ||||
|                             var len = a.length; | ||||
|                             for (var i = 0; i < len; i++) { | ||||
|                                 var newMessage = RED.util.cloneMessage(msg); | ||||
|                                 newMessage.columns = node.template.filter(val => val).join(','); | ||||
|                                 newMessage.payload = a[i]; | ||||
|                                 if (!has_parts) { | ||||
|                                     newMessage.parts = { | ||||
| @@ -273,7 +290,11 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 else { node.warn(RED._("csv.errors.csv_js")); } | ||||
|             } | ||||
|             else { node.send(msg); } // If no payload - just pass it on. | ||||
|             else { | ||||
|                 if (!msg.hasOwnProperty("reset")) { | ||||
|                     node.send(msg); // If no payload and not reset - just pass it on. | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     RED.nodes.registerType("csv",CSVNode); | ||||
|   | ||||
| @@ -40,6 +40,6 @@ | ||||
|     progress will be cleared and no message triggered.</p> | ||||
|     <p>The node can be configured to resend a message at a regular interval until it | ||||
|     is reset by a received message.</p> | ||||
|     <p>Optionally, the node can be configured to treat messages with <code>msg.topic</code> as if they | ||||
|     are separate streams.</p> | ||||
|     <p>Optionally, the node can be configured to treat messages as if they are separate streams, | ||||
|     using a msg property to identify each stream. Default <code>msg.topic</code>.</p> | ||||
| </script> | ||||
|   | ||||
| @@ -37,6 +37,7 @@ | ||||
|         "stopped": "stopped", | ||||
|         "failed": "Inject failed: __error__", | ||||
|         "label": { | ||||
|             "properties": "Properties", | ||||
|             "repeat": "Repeat", | ||||
|             "flow": "flow context", | ||||
|             "global": "global context", | ||||
| @@ -79,7 +80,6 @@ | ||||
|         "on": "on", | ||||
|         "onstart": "Inject once after", | ||||
|         "onceDelay": "seconds, then", | ||||
|         "tip": "<b>Note:</b> \"interval between times\" and \"at a specific time\" will use cron.<br/>\"interval\" should be 596 hours or less.<br/>See info box for details.", | ||||
|         "success": "Successfully injected: __label__", | ||||
|         "errors": { | ||||
|             "failed": "inject failed, see log for details", | ||||
| @@ -302,7 +302,7 @@ | ||||
|         "wait-for": "wait for", | ||||
|         "wait-loop": "resend it every", | ||||
|         "for": "Handling", | ||||
|         "bytopics": "each msg.topic independently", | ||||
|         "bytopics": "each", | ||||
|         "alltopics": "all messages", | ||||
|         "duration": { | ||||
|             "ms": "Milliseconds", | ||||
| @@ -311,6 +311,7 @@ | ||||
|             "h": "Hours" | ||||
|         }, | ||||
|         "extend": " extend delay if new message arrives", | ||||
|         "second": " send second message to separate output", | ||||
|         "label": { | ||||
|             "trigger": "trigger", | ||||
|             "trigger-block": "trigger & block", | ||||
| @@ -723,6 +724,11 @@ | ||||
|             "mac": "Mac (\\r)", | ||||
|             "windows": "Windows (\\r\\n)" | ||||
|         }, | ||||
|         "hdrout": { | ||||
|             "none": "never send column headers", | ||||
|             "all": "always send column headers", | ||||
|             "once": "send headers once, until msg.reset" | ||||
|         }, | ||||
|         "errors": { | ||||
|             "csv_js": "This node only handles CSV strings or js objects.", | ||||
|             "obj_csv": "No columns template specified for object -> CSV.", | ||||
|   | ||||
| @@ -38,11 +38,13 @@ | ||||
|     <p>The column template can contain an ordered list of column names. When converting CSV to an object, the column names | ||||
|     will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.</p> | ||||
|     <p>When converting to CSV, the column template is used to identify which properties to extract from the object and in what order.</p> | ||||
|     <p>If the template is blank then the node can use a simple comma separated list of properties supplied in <code>msg.columns</code> to | ||||
|         determine what to extract. If that is not present then all the object properties are ouput in the order in which they are found.</p> | ||||
|     <p>If the input is an array then the columns template is only used to optionally generate a row of column titles.</p> | ||||
|     <p>If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,"1.5",2'.</p> | ||||
|     <p>If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '"1","",3'.</p> | ||||
|     <p>If 'include null values' option is checked, null values will be returned in result, ie. middle value '"1",,3'.</p> | ||||
|     <p>The node can accept a multi-part input as long as the <code>parts</code> property is set correctly.</p> | ||||
|     <p>The node can accept a multi-part input as long as the <code>parts</code> property is set correctly, for example from a file-in node or split node.</p> | ||||
|     <p>If outputting multiple messages they will have their <code>parts</code> property set and form a complete message sequence.</p> | ||||
|     <p><b>Note:</b> the column template must be comma separated - even if a different separator is chosen for the data.</p> | ||||
| </script> | ||||
|   | ||||
| @@ -302,7 +302,7 @@ | ||||
|         "wait-for": "指定した時間待機", | ||||
|         "wait-loop": "指定した時間間隔毎に送信を繰り返す", | ||||
|         "for": "処理対象", | ||||
|         "bytopics": "msg.topic毎", | ||||
|         "bytopics": "毎", | ||||
|         "alltopics": "全メッセージ", | ||||
|         "duration": { | ||||
|             "ms": "ミリ秒", | ||||
| @@ -719,6 +719,11 @@ | ||||
|             "mac": "Mac (\\r)", | ||||
|             "windows": "Windows (\\r\\n)" | ||||
|         }, | ||||
|         "hdrout": { | ||||
|             "none": "カラムヘッダを送信しない", | ||||
|             "all": "カラムヘッダを常に送信する", | ||||
|             "once": "ヘッダを一度だけ送信する(msg.resetの受け付けると再送)" | ||||
|         }, | ||||
|         "errors": { | ||||
|             "csv_js": "本ノードが処理できる形式は、CSV文字列またはJSONのみです", | ||||
|             "obj_csv": "オブジェクトをCSVへ変換する際の列名が設定されていません" | ||||
|   | ||||
| @@ -19,10 +19,10 @@ | ||||
|         "body-parser": "1.19.0", | ||||
|         "cheerio": "0.22.0", | ||||
|         "content-type": "1.0.4", | ||||
|         "cookie-parser": "1.4.4", | ||||
|         "cookie-parser": "1.4.5", | ||||
|         "cookie": "0.4.0", | ||||
|         "cors": "2.8.5", | ||||
|         "cron": "1.8.2", | ||||
|         "cron": "1.7.2", | ||||
|         "denque": "1.4.1", | ||||
|         "fs-extra": "8.1.0", | ||||
|         "fs.notify": "0.0.4", | ||||
| @@ -33,7 +33,7 @@ | ||||
|         "media-typer": "1.1.0", | ||||
|         "mqtt": "2.18.8", | ||||
|         "multer": "1.4.2", | ||||
|         "mustache": "4.0.0", | ||||
|         "mustache": "4.0.1", | ||||
|         "on-headers": "1.0.2", | ||||
|         "raw-body": "2.4.1", | ||||
|         "request": "2.88.0", | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "1.1.0", | ||||
|         "semver": "6.3.0", | ||||
|         "uglify-js": "3.8.0", | ||||
|         "uglify-js": "3.8.1", | ||||
|         "when": "3.7.8" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -203,10 +203,10 @@ LocalFileSystem.prototype.open = function(){ | ||||
|                 var newContext = self.cache._export(); | ||||
|                 scopes.forEach(function(scope) { | ||||
|                     var storagePath = getStoragePath(self.storageBaseDir,scope); | ||||
|                     var context = newContext[scope]; | ||||
|                     var context = newContext[scope] || {}; | ||||
|                     var stringifiedContext = stringify(context); | ||||
|                     if (stringifiedContext.circular && !self.knownCircularRefs[scope]) { | ||||
|                         log.warn(log._("error-circular",{scope:scope})); | ||||
|                         log.warn(log._("context.localfilesystem.error-circular",{scope:scope})); | ||||
|                         self.knownCircularRefs[scope] = true; | ||||
|                     } else { | ||||
|                         delete self.knownCircularRefs[scope]; | ||||
| @@ -324,7 +324,7 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) { | ||||
|             } | ||||
|             var stringifiedContext = stringify(obj); | ||||
|             if (stringifiedContext.circular && !self.knownCircularRefs[scope]) { | ||||
|                 log.warn(log._("error-circular",{scope:scope})); | ||||
|                 log.warn(log._("context.localfilesystem.error-circular",{scope:scope})); | ||||
|                 self.knownCircularRefs[scope] = true; | ||||
|             } else { | ||||
|                 delete self.knownCircularRefs[scope]; | ||||
|   | ||||
| @@ -553,7 +553,7 @@ function getFlow(id) { | ||||
|     if (flow.label) { | ||||
|         result.label = flow.label; | ||||
|     } | ||||
|     if (flow.disabled) { | ||||
|     if (flow.hasOwnProperty('disabled')) { | ||||
|         result.disabled = flow.disabled; | ||||
|     } | ||||
|     if (flow.hasOwnProperty('info')) { | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|         "clone": "2.1.2", | ||||
|         "i18next": "15.1.2", | ||||
|         "json-stringify-safe": "5.0.1", | ||||
|         "jsonata": "1.8.1", | ||||
|         "jsonata": "1.8.3", | ||||
|         "lodash.clonedeep": "^4.5.0", | ||||
|         "when": "3.7.8" | ||||
|     } | ||||
|   | ||||
							
								
								
									
										2
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							| @@ -41,7 +41,7 @@ | ||||
|         "fs-extra": "8.1.0", | ||||
|         "node-red-node-rbe": "^0.2.6", | ||||
|         "node-red-node-tail": "^0.1.0", | ||||
|         "nopt": "4.0.1", | ||||
|         "nopt": "4.0.3", | ||||
|         "semver": "6.3.0" | ||||
|     }, | ||||
|     "optionalDependencies": { | ||||
|   | ||||
| @@ -102,20 +102,20 @@ describe('trigger node', function() { | ||||
|     function basicTest(type, val, rval) { | ||||
|         it('should output 1st value when triggered ('+type+')', function(done) { | ||||
|             var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:val, op1type:type, op2:"", op2type:"null", duration:"20", wires:[["n2"]] }, | ||||
|                         {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             process.env[val] = rval; | ||||
|             helper.load(triggerNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     try { | ||||
| 			if (rval) { | ||||
| 			    msg.should.have.property("payload"); | ||||
| 			    should.deepEqual(msg.payload, rval); | ||||
| 			} | ||||
| 			else { | ||||
| 			    msg.should.have.property("payload", val); | ||||
| 			} | ||||
|                         if (rval) { | ||||
|                             msg.should.have.property("payload"); | ||||
|                             should.deepEqual(msg.payload, rval); | ||||
|                         } | ||||
|                         else { | ||||
|                             msg.should.have.property("payload", val); | ||||
|                         } | ||||
|                         delete process.env[val]; | ||||
|                         done(); | ||||
|                     } | ||||
| @@ -127,7 +127,7 @@ describe('trigger node', function() { | ||||
|  | ||||
|         it('should output 2st value when triggered ('+type+')', function(done) { | ||||
|             var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"str", op2:val, op2type:type, duration:"20", wires:[["n2"]] }, | ||||
|                         {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             process.env[val] = rval; | ||||
|             helper.load(triggerNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
| @@ -136,17 +136,17 @@ describe('trigger node', function() { | ||||
|                 n2.on("input", function(msg) { | ||||
|                     try { | ||||
|                         if (c === 0) { | ||||
| 			    msg.should.have.property("payload", "foo"); | ||||
|                             msg.should.have.property("payload", "foo"); | ||||
|                             c++; | ||||
|                         } | ||||
|                         else { | ||||
| 			    if (rval) { | ||||
| 			        msg.should.have.property("payload"); | ||||
| 			        should.deepEqual(msg.payload, rval); | ||||
| 			    } | ||||
| 			    else { | ||||
| 			        msg.should.have.property("payload", val); | ||||
| 			    } | ||||
|                             if (rval) { | ||||
|                                 msg.should.have.property("payload"); | ||||
|                                 should.deepEqual(msg.payload, rval); | ||||
|                             } | ||||
|                             else { | ||||
|                                 msg.should.have.property("payload", val); | ||||
|                             } | ||||
|                             delete process.env[val]; | ||||
|                             done(); | ||||
|                         } | ||||
| @@ -378,6 +378,51 @@ describe('trigger node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should handle multiple other properties individually if asked to do so', function(done) { | ||||
|         var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", topic:"foo", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, | ||||
|             {id:"n2", type:"helper"} ]; | ||||
|         helper.load(triggerNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             var c = 0; | ||||
|             n2.on("input", function(msg) { | ||||
|                 try { | ||||
|                     c += 1; | ||||
|                     if (c === 1) { | ||||
|                         msg.should.have.a.property("payload", 1); | ||||
|                         msg.should.have.a.property("foo", "A"); | ||||
|                     } | ||||
|                     else if (c === 2) { | ||||
|                         msg.should.have.a.property("payload", 1); | ||||
|                         msg.should.have.a.property("foo", "B"); | ||||
|                     } | ||||
|                     else if (c === 3) { | ||||
|                         msg.should.have.a.property("payload", 1); | ||||
|                         msg.should.have.a.property("foo", "C"); | ||||
|                     } | ||||
|                     else if (c === 4) { | ||||
|                         msg.should.have.a.property("payload", 0); | ||||
|                         msg.should.have.a.property("foo", "A"); | ||||
|                     } | ||||
|                     else if (c === 5) { | ||||
|                         msg.should.have.a.property("payload", 0); | ||||
|                         msg.should.have.a.property("foo", "B"); | ||||
|                     } | ||||
|                     else if (c === 6) { | ||||
|                         msg.should.have.a.property("payload", 0); | ||||
|                         msg.should.have.a.property("foo", "C"); | ||||
|                         done(); | ||||
|                     } | ||||
|                 } catch(err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             }); | ||||
|             n1.emit("input", {payload:1,foo:"A"}); | ||||
|             n1.emit("input", {payload:2,foo:"B"}); | ||||
|             n1.emit("input", {payload:3,foo:"C"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should be able to return things from flow and global context variables', function(done) { | ||||
|         var spy = sinon.stub(RED.util, 'evaluateNodeProperty', | ||||
|             function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } } | ||||
| @@ -408,8 +453,8 @@ describe('trigger node', function() { | ||||
|  | ||||
|     it('should be able to return things from persistable flow and global context variables', function (done) { | ||||
|         var flow = [{"id": "n1", "type": "trigger", "name": "triggerNode", "op1": "#:(memory1)::foo", "op1type": "flow", | ||||
|                      "op2": "#:(memory1)::bar", "op2type": "global", "duration": "20", "wires": [["n2"]], "z": "flow" }, | ||||
|                     {"id": "n2", "type": "helper"}]; | ||||
|             "op2": "#:(memory1)::bar", "op2type": "global", "duration": "20", "wires": [["n2"]], "z": "flow" }, | ||||
|         {"id": "n2", "type": "helper"}]; | ||||
|         helper.load(triggerNode, flow, function () { | ||||
|             initContext(function () { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
| @@ -442,11 +487,11 @@ describe('trigger node', function() { | ||||
|  | ||||
|     it('should be able to return things from multiple persistable global context variables', function (done) { | ||||
|         var flow = [{"id": "n1", "z": "flow", "type": "trigger", | ||||
|                      "duration": "20", "wires": [["n2"]], | ||||
|                      "op1": "#:(memory1)::val", "op1type": "global", | ||||
|                      "op2": "#:(memory2)::val", "op2type": "global" | ||||
|                     }, | ||||
|                     {"id": "n2", "type": "helper"}]; | ||||
|             "duration": "20", "wires": [["n2"]], | ||||
|             "op1": "#:(memory1)::val", "op1type": "global", | ||||
|             "op2": "#:(memory2)::val", "op2type": "global" | ||||
|         }, | ||||
|         {"id": "n2", "type": "helper"}]; | ||||
|         helper.load(triggerNode, flow, function () { | ||||
|             initContext(function () { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
| @@ -481,11 +526,11 @@ describe('trigger node', function() { | ||||
|  | ||||
|     it('should be able to return things from multiple persistable flow context variables', function (done) { | ||||
|         var flow = [{"id": "n1", "z": "flow", "type": "trigger", | ||||
|                      "duration": "20", "wires": [["n2"]], | ||||
|                      "op1": "#:(memory1)::val", "op1type": "flow", | ||||
|                      "op2": "#:(memory2)::val", "op2type": "flow" | ||||
|                     }, | ||||
|                     {"id": "n2", "type": "helper"}]; | ||||
|             "duration": "20", "wires": [["n2"]], | ||||
|             "op1": "#:(memory1)::val", "op1type": "flow", | ||||
|             "op2": "#:(memory2)::val", "op2type": "flow" | ||||
|         }, | ||||
|         {"id": "n2", "type": "helper"}]; | ||||
|         helper.load(triggerNode, flow, function () { | ||||
|             initContext(function () { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
| @@ -520,11 +565,11 @@ describe('trigger node', function() { | ||||
|  | ||||
|     it('should be able to return things from multiple persistable flow & global context variables', function (done) { | ||||
|         var flow = [{"id": "n1", "z": "flow", "type": "trigger", | ||||
|                      "duration": "20", "wires": [["n2"]], | ||||
|                      "op1": "#:(memory1)::val", "op1type": "flow", | ||||
|                      "op2": "#:(memory2)::val", "op2type": "global" | ||||
|                     }, | ||||
|                     {"id": "n2", "type": "helper"}]; | ||||
|             "duration": "20", "wires": [["n2"]], | ||||
|             "op1": "#:(memory1)::val", "op1type": "flow", | ||||
|             "op2": "#:(memory2)::val", "op2type": "global" | ||||
|         }, | ||||
|         {"id": "n2", "type": "helper"}]; | ||||
|         helper.load(triggerNode, flow, function () { | ||||
|             initContext(function () { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
| @@ -818,6 +863,40 @@ describe('trigger node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should be able to send 2nd message to a 2nd output', function(done) { | ||||
|         var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"hello",  op2:"world", duration:"50", outputs:2, wires:[["n2"],["n3"]] }, | ||||
|             {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ]; | ||||
|         helper.load(triggerNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             var n3 = helper.getNode("n3"); | ||||
|             var c = 0; | ||||
|             n2.on("input", function(msg) { | ||||
|                 try { | ||||
|                     if (c === 0) { | ||||
|                         msg.should.have.a.property("payload", "hello"); | ||||
|                         msg.should.have.a.property("topic", "test"); | ||||
|                         c+=1; | ||||
|                     } | ||||
|                     else { done(err); } | ||||
|                 } | ||||
|                 catch(err) { done(err); } | ||||
|             }); | ||||
|             n3.on("input", function(msg) { | ||||
|                 try { | ||||
|                     if (c === 1) { | ||||
|                         msg.should.have.a.property("payload", "world"); | ||||
|                         msg.should.have.a.property("topic", "test"); | ||||
|                         done(); | ||||
|                     } | ||||
|                     else { done(err); } | ||||
|                 } | ||||
|                 catch(err) { done(err); } | ||||
|             }); | ||||
|             n1.emit("input", {payload:"go",topic:"test"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should handle string null as null', function(done) { | ||||
|         var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"pay", op1:"null", op2:"null", duration:"40", wires:[["n2"]] }, | ||||
|             {id:"n2", type:"helper"} ]; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| /* eslint-disable no-undef */ | ||||
| /** | ||||
|  * Copyright JS Foundation and other contributors, http://js.foundation | ||||
|  * | ||||
| @@ -70,12 +71,13 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert a simple csv string to a javascript object', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); | ||||
|                     msg.should.have.property('columns', "a,b,c,d"); | ||||
|                     check_parts(msg, 0, 1); | ||||
|                     done(); | ||||
|                 }); | ||||
| @@ -86,7 +88,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should remove quotes and whitespace from template', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:'"a",  "b" , " c "," d  " ', wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -102,12 +104,13 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should create column names if no template provided', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:'', wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 }); | ||||
|                     msg.should.have.property('columns', "col1,col2,col3,col4"); | ||||
|                     check_parts(msg, 0, 1); | ||||
|                     done(); | ||||
|                 }); | ||||
| @@ -118,12 +121,13 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should allow dropping of fields from the template', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,,,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     msg.should.have.property('payload', { a: 1, d: 4 }); | ||||
|                     msg.should.have.property('columns', 'a,d'); | ||||
|                     check_parts(msg, 0, 1); | ||||
|                     done(); | ||||
|                 }); | ||||
| @@ -134,7 +138,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should leave numbers starting with 0, e and + as strings (except 0.)', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -150,7 +154,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should not parse numbers when told not to do so', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", strings:false, wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -166,7 +170,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should leave handle strings with scientific notation as numbers', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -183,7 +187,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should allow quotes in the input (but drop blank strings)', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g,h", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -327,12 +331,13 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should be able to output multiple lines as one array', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", multi:"yes", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     msg.should.have.property('payload', [ { a: 1, b: 2, c: 3, d: 4 },{ a: 5, b: -6, c: '07', d: '+8' },{ a: 9, b: 0, c: 'a', d: 'b' },{ a: 'c', b: 'd', c: 'e', d: 'f' } ]); | ||||
|                     msg.should.have.property('columns','a,b,c,d'); | ||||
|                     msg.should.not.have.property('parts'); | ||||
|                     done(); | ||||
|                 }); | ||||
| @@ -343,7 +348,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should handle numbers in strings but not IP addresses', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -359,7 +364,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should preserve parts property', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -403,7 +408,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should skip several lines from start if requested', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -417,9 +422,9 @@ describe('CSV node', function() { | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should skip several lines from start then use next line as a tempate', function(done) { | ||||
|         it('should skip several lines from start then use next line as a template', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, skip: 2, wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -435,7 +440,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should skip several lines from start and correct parts', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -467,11 +472,13 @@ describe('CSV node', function() { | ||||
|                 n2.on("input", function(msg) { | ||||
|                     if (c === 0) { | ||||
|                         msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); | ||||
|                         msg.should.have.property('columns', 'w,x,y,z'); | ||||
|                         check_parts(msg, 0, 2); | ||||
|                         c += 1; | ||||
|                     } | ||||
|                     else { | ||||
|                         msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); | ||||
|                         msg.should.have.property('columns', 'w,x,y,z'); | ||||
|                         check_parts(msg, 1, 2); | ||||
|                         done(); | ||||
|                     } | ||||
| @@ -495,7 +502,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert a simple object back to a csv', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -513,7 +520,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert a simple object back to a csv with no template', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:" ", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -531,7 +538,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should handle a template with spaces in the property names', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b o,c p,,e", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -549,7 +556,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert an array of objects to a multi-line csv', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -567,7 +574,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert a simple array back to a csv', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -585,7 +592,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should convert an array of arrays back to a multi-line csv', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -603,7 +610,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|         it('should be able to include column names as first row', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:true, ret:"\r\n", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -619,9 +626,36 @@ describe('CSV node', function() { | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should be able to pass in column names', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 var count = 0; | ||||
|                 n2.on("input", function(msg) { | ||||
|                     count += 1; | ||||
|                     try { | ||||
|                         if (count === 1) { | ||||
|                             msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n'); | ||||
|                         } | ||||
|                         if (count === 3) { | ||||
|                             msg.should.have.property('payload', '4,,3,4\r\n'); | ||||
|                             done() | ||||
|                         } | ||||
|                     } | ||||
|                     catch(e) { done(e); } | ||||
|                 }); | ||||
|                 var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; | ||||
|                 n1.emit("input", {payload:testJson, columns:"a,,b,a"}); | ||||
|                 n1.emit("input", {payload:testJson}); | ||||
|                 n1.emit("input", {payload:testJson}); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should handle quotes and sub-properties', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                     {id:"n2", type:"helper"} ]; | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
| @@ -641,7 +675,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|     it('should just pass through if no payload provided', function(done) { | ||||
|         var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             {id:"n2", type:"helper"} ]; | ||||
|         helper.load(csvNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
| @@ -661,7 +695,7 @@ describe('CSV node', function() { | ||||
|  | ||||
|     it('should warn if provided a number or boolean', function(done) { | ||||
|         var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             {id:"n2", type:"helper"} ]; | ||||
|         helper.load(csvNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user