diff --git a/CHANGELOG.md b/CHANGELOG.md index 6770f3158..29b8dabb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +#### 0.19.4: Maintenance Release + + - Fix race condition in non-cache lfs context Fixes #1888 + - LocalFileSystem Context: Remove extra flush code + - Prevent race condition in caching mode of lfs context (#1889) + - Allow context store name to be provided in the key + - Switch node: only use promises when absolutely necessary + - Fix dbl-click handling on webkit-based browsers + - Ensure context.flow/global cannot be deleted or enumerated + - Handle context.get with multiple levels of unknown key Fixes #1883 + - Fix global.get("foo.bar") for functionGlobalContext set values + - Fix node color bug (#1877) + - Merge pull request #1857 from cclauss/patch-1 + - Define raw_input() in Python 3 & fix time.sleep() + #### 0.19.3: Maintenance Release - Split node - fix complete to send msg for k/v object diff --git a/editor/js/ui/utils.js b/editor/js/ui/utils.js index 8e6654a7a..c944f3504 100644 --- a/editor/js/ui/utils.js +++ b/editor/js/ui/utils.js @@ -826,7 +826,11 @@ RED.utils = (function() { } result = nodeColorCache[type]; } - return result; + if (result) { + return result; + } else { + return "#ddd"; + } } function addSpinnerOverlay(container,contain) { diff --git a/editor/js/ui/view.js b/editor/js/ui/view.js index 4688c15cd..f3928351f 100644 --- a/editor/js/ui/view.js +++ b/editor/js/ui/view.js @@ -1766,7 +1766,7 @@ RED.view = (function() { clickTime = now; dblClickPrimed = (lastClickNode == mousedown_node && - d3.event.buttons === 1 && + d3.event.button === 0 && !d3.event.shiftKey && !d3.event.metaKey && !d3.event.altKey && !d3.event.ctrlKey); lastClickNode = mousedown_node; diff --git a/nodes/99-sample.html.demo b/nodes/99-sample.html.demo index 94e049e31..c7548b032 100644 --- a/nodes/99-sample.html.demo +++ b/nodes/99-sample.html.demo @@ -67,6 +67,7 @@ }, inputs:1, // set the number of inputs - only 0 or 1 outputs:1, // set the number of outputs - 0 to n + color: "#ddd", // set icon color // set the icon (held in icons dir below where you save the node) icon: "myicon.png", // saved in icons/myicon.png label: function() { // sets the default label contents diff --git a/nodes/core/hardware/nrgpio.py b/nodes/core/hardware/nrgpio.py index 0cde0e4df..7908ca117 100755 --- a/nodes/core/hardware/nrgpio.py +++ b/nodes/core/hardware/nrgpio.py @@ -21,7 +21,12 @@ import os import subprocess from time import sleep -bounce = 25; +try: + raw_input # Python 2 +except NameError: + raw_input = input # Python 3 + +bounce = 25 if len(sys.argv) > 2: cmd = sys.argv[1].lower() @@ -198,7 +203,7 @@ if len(sys.argv) > 2: elif cmd == "kbd": # catch keyboard button events try: while not os.path.isdir("/dev/input/by-path"): - time.sleep(10) + sleep(10) infile = subprocess.check_output("ls /dev/input/by-path/ | grep -m 1 'kbd'", shell=True).strip() infile_path = "/dev/input/by-path/" + infile EVENT_SIZE = struct.calcsize('llHHI') diff --git a/nodes/core/logic/10-switch.js b/nodes/core/logic/10-switch.js index 89593047e..1d9c52971 100644 --- a/nodes/core/logic/10-switch.js +++ b/nodes/core/logic/10-switch.js @@ -92,31 +92,80 @@ module.exports = function(RED) { } function getProperty(node,msg) { - return new Promise((resolve,reject) => { + if (node.useAsyncRules) { + return new Promise((resolve,reject) => { + if (node.propertyType === 'jsonata') { + RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => { + if (err) { + reject(RED._("switch.errors.invalid-expr",{error:err.message})); + } else { + resolve(value); + } + }); + } else { + RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => { + if (err) { + resolve(undefined); + } else { + resolve(value); + } + }); + } + }); + } else { if (node.propertyType === 'jsonata') { - RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => { - if (err) { - reject(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateJSONataExpression(node.property,msg); + } catch(err) { + throw new Error(RED._("switch.errors.invalid-expr",{error:err.message})) + } } else { - RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => { - if (err) { - resolve(undefined); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg); + } catch(err) { + return undefined; + } } - }); + } } function getV1(node,msg,rule,hasParts) { - return new Promise( (resolve,reject) => { + if (node.useAsyncRules) { + return new Promise( (resolve,reject) => { + if (rule.vt === 'prev') { + resolve(node.previousValue); + } else if (rule.vt === 'jsonata') { + var exp = rule.v; + if (rule.t === 'jsonata_exp') { + if (hasParts) { + exp.assign("I", msg.parts.index); + exp.assign("N", msg.parts.count); + } + } + RED.util.evaluateJSONataExpression(exp,msg,(err,value) => { + if (err) { + reject(RED._("switch.errors.invalid-expr",{error:err.message})); + } else { + resolve(value); + } + }); + } else if (rule.vt === 'json') { + resolve("json"); // TODO: ?! invalid case + } else if (rule.vt === 'null') { + resolve("null"); + } else { + RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) { + if (err) { + resolve(undefined); + } else { + resolve(value); + } + }); + } + }); + } else { if (rule.vt === 'prev') { - resolve(node.previousValue); + return node.previousValue; } else if (rule.vt === 'jsonata') { var exp = rule.v; if (rule.t === 'jsonata_exp') { @@ -125,83 +174,120 @@ module.exports = function(RED) { exp.assign("N", msg.parts.count); } } - RED.util.evaluateJSONataExpression(exp,msg,(err,value) => { - if (err) { - reject(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateJSONataExpression(exp,msg); + } catch(err) { + throw new Error(RED._("switch.errors.invalid-expr",{error:err.message})) + } } else if (rule.vt === 'json') { - resolve("json"); + return "json"; // TODO: ?! invalid case } else if (rule.vt === 'null') { - resolve("null"); + return "null"; } else { - RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) { - if (err) { - resolve(undefined); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg); + } catch(err) { + return undefined; + } } - }); + } } function getV2(node,msg,rule) { - return new Promise((resolve,reject) => { + if (node.useAsyncRules) { + return new Promise((resolve,reject) => { + var v2 = rule.v2; + if (rule.v2t === 'prev') { + resolve(node.previousValue); + } else if (rule.v2t === 'jsonata') { + RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => { + if (err) { + reject(RED._("switch.errors.invalid-expr",{error:err.message})); + } else { + resolve(value); + } + }); + } else if (typeof v2 !== 'undefined') { + RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) { + if (err) { + resolve(undefined); + } else { + resolve(value); + } + }); + } else { + resolve(v2); + } + }) + } else { var v2 = rule.v2; if (rule.v2t === 'prev') { - resolve(node.previousValue); + return node.previousValue; } else if (rule.v2t === 'jsonata') { - RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => { - if (err) { - reject(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateJSONataExpression(rule.v2,msg); + } catch(err) { + throw new Error(RED._("switch.errors.invalid-expr",{error:err.message})) + } } else if (typeof v2 !== 'undefined') { - RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) { - if (err) { - resolve(undefined); - } else { - resolve(value); - } - }); + try { + return RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg); + } catch(err) { + return undefined; + } } else { - resolve(v2); + return v2; } - }) + } } function applyRule(node, msg, property, state) { - return new Promise((resolve,reject) => { + if (node.useAsyncRules) { + return new Promise((resolve,reject) => { - var rule = node.rules[state.currentRule]; - var v1,v2; + var rule = node.rules[state.currentRule]; + var v1,v2; - getV1(node,msg,rule,state.hasParts).then(value => { - v1 = value; - }).then(()=>getV2(node,msg,rule)).then(value => { - v2 = value; - }).then(() => { - if (rule.t == "else") { - property = state.elseflag; - state.elseflag = true; - } - if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) { - state.onward.push(msg); - state.elseflag = false; - if (node.checkall == "false") { - return resolve(false); + getV1(node,msg,rule,state.hasParts).then(value => { + v1 = value; + }).then(()=>getV2(node,msg,rule)).then(value => { + v2 = value; + }).then(() => { + if (rule.t == "else") { + property = state.elseflag; + state.elseflag = true; } - } else { - state.onward.push(null); + if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) { + state.onward.push(msg); + state.elseflag = false; + if (node.checkall == "false") { + return resolve(false); + } + } else { + state.onward.push(null); + } + resolve(state.currentRule < node.rules.length - 1); + }); + }) + } else { + var rule = node.rules[state.currentRule]; + var v1 = getV1(node,msg,rule,state.hasParts); + var v2 = getV2(node,msg,rule); + if (rule.t == "else") { + property = state.elseflag; + state.elseflag = true; + } + if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) { + state.onward.push(msg); + state.elseflag = false; + if (node.checkall == "false") { + return false; } - resolve(state.currentRule < node.rules.length - 1); - }); - }) + } else { + state.onward.push(null); + } + return state.currentRule < node.rules.length - 1 + } } function applyRules(node, msg, property,state) { @@ -215,7 +301,18 @@ module.exports = function(RED) { msg.parts.hasOwnProperty("index") } } - return applyRule(node,msg,property,state).then(hasMore => { + if (node.useAsyncRules) { + return applyRule(node,msg,property,state).then(hasMore => { + if (hasMore) { + state.currentRule++; + return applyRules(node,msg,property,state); + } else { + node.previousValue = property; + return state.onward; + } + }); + } else { + var hasMore = applyRule(node,msg,property,state); if (hasMore) { state.currentRule++; return applyRules(node,msg,property,state); @@ -223,7 +320,7 @@ module.exports = function(RED) { node.previousValue = property; return state.onward; } - }); + } } @@ -248,6 +345,14 @@ module.exports = function(RED) { var valid = true; var repair = n.repair; var needsCount = repair; + this.useAsyncRules = ( + this.propertyType === 'flow' || + this.propertyType === 'global' || ( + this.propertyType === 'jsonata' && + /\$(flow|global)Context/.test(this.property) + ) + ); + for (var i=0; i applyRules(node,msg,property)) - .then(onward => { - if (!repair || !hasParts) { - node.send(onward); - } - else { - sendGroupMessages(onward, msg); - } - }).catch(err => { - node.warn(err); - }); + if (node.useAsyncRules) { + return getProperty(node,msg) + .then(property => applyRules(node,msg,property)) + .then(onward => { + if (!repair || !hasParts) { + node.send(onward); + } + else { + sendGroupMessages(onward, msg); + } + }).catch(err => { + node.warn(err); + }); + } else { + try { + var property = getProperty(node,msg); + var onward = applyRules(node,msg,property); + if (!repair || !hasParts) { + node.send(onward); + } else { + sendGroupMessages(onward, msg); + } + } catch(err) { + node.warn(err); + } + } } function clearPending() { @@ -473,7 +608,11 @@ module.exports = function(RED) { } this.on('input', function(msg) { - processMessageQueue(msg); + if (node.useAsyncRules) { + processMessageQueue(msg); + } else { + processMessage(msg,true); + } }); this.on('close', function() { diff --git a/package.json b/package.json index 59e6161c0..c0bf3b94d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "0.19.3", + "version": "0.19.4", "description": "A visual tool for wiring the Internet of Things", "homepage": "http://nodered.org", "license": "Apache-2.0", diff --git a/red/runtime/nodes/context/index.js b/red/runtime/nodes/context/index.js index be79eb00e..d08734a2f 100644 --- a/red/runtime/nodes/context/index.js +++ b/red/runtime/nodes/context/index.js @@ -17,6 +17,7 @@ var clone = require("clone"); var log = require("../../log"); var memory = require("./memory"); +var util = require("../../util"); var settings; @@ -209,104 +210,131 @@ function createContext(id,seed) { insertSeedValues = function(keys,values) { if (!Array.isArray(keys)) { if (values[0] === undefined) { - values[0] = seed[keys]; + values[0] = util.getObjectProperty(seed,keys); } } else { for (var i=0;i