diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 721c5768a..cf4761a03 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -1162,5 +1162,21 @@ "ru": "Russian", "zh-CN": "Chinese(Simplified)", "zh-TW": "Chinese(Traditional)" + }, + "validator": { + "errors": { + "invalid-json": "Invalid JSON data: __error__", + "invalid-json-prop": "__prop__: invalid JSON data: __error__", + "invalid-prop": "Invalid property expression", + "invalid-prop-prop": "__prop__: invalid property expression", + "invalid-num": "Invalid number", + "invalid-num-prop": "__prop__: invalid number", + "invalid-regexp": "Invalid input pattern", + "invalid-regex-prop": "__prop__: invalid input pattern", + "missing-required-prop": "__prop__: property value missing", + "invalid-config": "__prop__: invalid configuration node", + "missing-config": "__prop__: missing configuration node", + "validation-error": "__prop__: validation error: __node__, __id__: __error__" + } } } diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 90cae7abe..d6c5db150 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -1299,5 +1299,21 @@ "zoom-out": "ズームアウト", "zoom-reset": "ズームリセット", "toggle-navigator": "ナビゲータ表示切替" + }, + "validator": { + "errors": { + "invalid-json": "JSONデータが不正: __error__", + "invalid-json-prop": "__prop__: JSONデータが不正: __error__", + "invalid-prop": "プロパティ式が不正", + "invalid-prop-prop": "__prop__: プロパティ式が不正", + "invalid-num": "数値が不正", + "invalid-num-prop": "__prop__: 数値が不正", + "invalid-regexp": "入力パターンが不正", + "invalid-regex-prop": "__prop__: 入力パターンが不正", + "missing-required-prop": "__prop__: プロパティが未設定", + "invalid-config": "__prop__: 設定ノードが不正", + "missing-config": "__prop__: 設定ノードが存在しません", + "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" + } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js index 5871abcde..472652918 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js @@ -359,6 +359,7 @@ RED.popover = (function() { setTimeout(closePopup,delay.hide); } }); + if (trigger === 'hover') { target.on('mouseenter',function(e) { clearTimeout(timer); @@ -470,6 +471,10 @@ RED.popover = (function() { popover.setAction = function(newAction) { action = newAction; } + popover.delete = function() { + target.off("mouseenter"); + target.off("mouseleave"); + }; return popover; }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 6017fb687..f0f3a073b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -110,7 +110,11 @@ RED.editor = (function() { var result = []; for (var prop in definition) { if (definition.hasOwnProperty(prop)) { - if (!validateNodeProperty(node, definition, prop, properties[prop])) { + var valid = validateNodeProperty(node, definition, prop, properties[prop]); + if ((typeof valid) === "string") { + result.push(valid); + } + else if(!valid) { result.push(prop); } } @@ -124,7 +128,7 @@ RED.editor = (function() { * @param definition - the node property definitions (either def.defaults or def.creds) * @param property - the property name being validated * @param value - the property value being validated - * @returns {boolean} whether the node proprty is valid + * @returns {boolean|string} whether the node proprty is valid. `true`: valid `false|String`: invalid */ function validateNodeProperty(node,definition,property,value) { var valid = true; @@ -136,22 +140,74 @@ RED.editor = (function() { if (/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/.test(value)) { return true; } + var label = null; + if (("label" in definition[property]) && + ((typeof definition[property].label) == "string")) { + label = definition[property].label; + } if ("required" in definition[property] && definition[property].required) { valid = value !== ""; + if (!valid && label) { + return RED._("validator.errors.missing-required-prop", { + prop: label + }); + } } if (valid && "validate" in definition[property]) { try { - valid = definition[property].validate.call(node,value); + var opt = {}; + if (label) { + opt.label = label; + } + valid = definition[property].validate.call(node,value, opt); + // If the validator takes two arguments, it is a 3.x validator that + // can return a String to mean 'invalid' and provide a reason + if ((definition[property].validate.length === 2) && + ((typeof valid) === "string")) { + return valid; + } else { + // Otherwise, a 2.x returns a truth-like/false-like value that + // we should cooerce to a boolean. + valid = !!valid + } } catch(err) { console.log("Validation error:",node.type,node.id,"property: "+property,"value:",value,err); + return RED._("validator.errors.validation-error", { + prop: property, + node: node.type, + id: node.id, + error: err.message + }); } } if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) { if (!value || value == "_ADD_") { valid = definition[property].hasOwnProperty("required") && !definition[property].required; + if (!valid && label) { + return RED._("validator.errors.missing-required-prop", { + prop: label + }); + } } else { var configNode = RED.nodes.node(value); - valid = (configNode && (configNode.valid == null || configNode.valid)); + if (configNode) { + if ((configNode.valid == null) || configNode.valid) { + return true; + } + if (label) { + return RED._("validator.errors.invalid-config", { + prop: label + }); + } + } + else { + if (label) { + return RED._("validator.errors.missing-config", { + prop: label + }); + } + } + return false; } } return valid; @@ -179,10 +235,26 @@ RED.editor = (function() { if (defaults[property].hasOwnProperty("format") && defaults[property].format !== "" && input[0].nodeName === "DIV") { value = input.text(); } - if (!validateNodeProperty(node, defaults, property,value)) { + var valid = validateNodeProperty(node, defaults, property,value); + if (((typeof valid) === "string") || !valid) { input.addClass("input-error"); + if ((typeof valid) === "string") { + var tooltip = input.data("tooltip"); + if (tooltip) { + tooltip.setContent(valid); + } + else { + tooltip = RED.popover.tooltip(input, valid); + input.data("tooltip", tooltip); + } + } } else { input.removeClass("input-error"); + var tooltip = input.data("tooltip"); + if (tooltip) { + input.data("tooltip", null); + tooltip.delete(); + } } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/validators.js b/packages/node_modules/@node-red/editor-client/src/js/validators.js index 833472b24..4fb384cbf 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/validators.js +++ b/packages/node_modules/@node-red/editor-client/src/js/validators.js @@ -14,22 +14,72 @@ * limitations under the License. **/ RED.validators = { - number: function(blankAllowed){return function(v) { return (blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v));}}, - regex: function(re){return function(v) { return re.test(v);}}, - typedInput: function(ptypeName,isConfig) { return function(v) { - var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName]; - if (ptype === 'json') { - try { - JSON.parse(v); + number: function(blankAllowed,mopt){ + return function(v, opt) { + if ((blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v))) { return true; - } catch(err) { - return false; } - } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) { - return RED.utils.validatePropertyExpression(v); - } else if (ptype === 'num') { - return /^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v); - } - return true; - }} + if (opt && opt.label) { + return RED._("validator.errors.invalid-num-prop", { + prop: opt.label + }); + } + return opt ? RED._("validator.errors.invalid-num") : false; + }; + }, + regex: function(re, mopt) { + return function(v, opt) { + if (re.test(v)) { + return true; + } + if (opt && opt.label) { + return RED._("validator.errors.invalid-regex-prop", { + prop: opt.label + }); + } + return opt ? RED._("validator.errors.invalid-regexp") : false; + }; + }, + typedInput: function(ptypeName,isConfig,mopt) { + return function(v, opt) { + var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName]; + if (ptype === 'json') { + try { + JSON.parse(v); + return true; + } catch(err) { + if (opt && opt.label) { + return RED._("validator.errors.invalid-json-prop", { + error: err.message, + prop: opt.label, + }); + } + return opt ? RED._("validator.errors.invalid-json", { + error: err.message + }) : false; + } + } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) { + if (RED.utils.validatePropertyExpression(v)) { + return true; + } + if (opt && opt.label) { + return RED._("validator.errors.invalid-prop-prop", { + prop: opt.label + }); + } + return opt ? RED._("validator.errors.invalid-prop") : false; + } else if (ptype === 'num') { + if (/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v)) { + return true; + } + if (opt && opt.label) { + return RED._("validator.errors.invalid-num-prop", { + prop: opt.label + }); + } + return opt ? RED._("validator.errors.invalid-num") : false; + } + return true; + }; + } }; diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.html b/packages/node_modules/@node-red/nodes/core/common/20-inject.html index 6d338df00..de3990a93 100644 --- a/packages/node_modules/@node-red/nodes/core/common/20-inject.html +++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.html @@ -225,28 +225,47 @@ color:"#a6bbcf", defaults: { name: {value:""}, - props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v) { + props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v, opt) { if (!v || v.length === 0) { return true } for (var i=0;i= 0) && (v <= 2147483))) }}, + repeat: { + value:"", validate: function(v, opt) { + if ((v === "") || + (RED.validators.number(v) && + (v >= 0) && (v <= 2147483))) { + return true; + } + return RED._("node-red:inject.errors.invalid-repeat"); + } + }, crontab: {value:""}, once: {value:false}, onceDelay: {value:0.1}, topic: {value:""}, - payload: {value:"", validate: RED.validators.typedInput("payloadType")}, + payload: {value:"", validate: RED.validators.typedInput("payloadType", false) }, payloadType: {value:"date"}, }, icon: "inject.svg", diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html index f632515f2..e70f59ab6 100644 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.html +++ b/packages/node_modules/@node-red/nodes/core/common/60-link.html @@ -274,7 +274,11 @@ name: { value: "" }, links: { value: [], type:"link in[]" }, linkType: { value:"static" }, - timeout: { value: "30", validate:RED.validators.number(true) } + timeout: { + value: "30", + label: RED._("node-red:link.timeout"), + validate:RED.validators.number(true) + } }, inputs: 1, outputs: 1, diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html index d107a4bb9..4091eb22d 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html @@ -358,24 +358,38 @@ name: {value:"_DEFAULT_"}, func: {value:"\nreturn msg;"}, outputs: {value:1}, - noerr: {value:0,required:true,validate:function(v) { return !v; }}, + noerr: {value:0,required:true, + validate: function(v, opt) { + if (!v) { + return true; + } + return RED._("node-red:function.error.invalid-js"); + }}, initialize: {value:""}, finalize: {value:""}, - libs: {value: [], validate: function(v) { + libs: {value: [], validate: function(v, opt) { if (!v) { return true; } for (var i=0,l=v.length;i -1) { - return false; + return RED._("node-red:function.error.missing-module", { + module: m.module + }); } if (invalidModuleVNames.indexOf(m.var) !== -1){ - return false; + return RED._("node-red:function.error.moduleNameError", { + name: m.var + }); } } return true; diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.html b/packages/node_modules/@node-red/nodes/core/function/10-switch.html index f6fc82f0a..3ac3c67ed 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-switch.html @@ -163,7 +163,9 @@ category: 'function', defaults: { name: {value:""}, - property: {value:"payload", required:true, validate: RED.validators.typedInput("propertyType")}, + property: {value:"payload", required:true, + label:RED._("node-red:common.label.payload"), + validate: RED.validators.typedInput("propertyType", false)}, propertyType: { value:"msg" }, rules: {value:[{t:"eq", v:"", vt:"str"}]}, checkall: {value:"true", required:true}, diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.html b/packages/node_modules/@node-red/nodes/core/function/15-change.html index b40039028..e53f522d9 100644 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.html +++ b/packages/node_modules/@node-red/nodes/core/function/15-change.html @@ -19,38 +19,66 @@