From b17b68c44bae254710fc1767fc0aa1fb3aace3ba Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 22 May 2022 10:37:22 +0900 Subject: [PATCH 01/53] Add Japanese translations for v3.0-beta.2 --- .../@node-red/editor-client/locales/ja/editor.json | 3 ++- .../node_modules/@node-red/nodes/locales/ja/messages.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 9713e2bab..4c918b615 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 @@ -907,7 +907,8 @@ "uknownNodes": "未知のノード", "unusedSubflows": "未使用のサブフロー", "hiddenFlows": "非表示のフロー", - "modifiedNodes": "修正したノードやフロー" + "modifiedNodes": "修正したノードやフロー", + "thisFlow": "現在のフロー" } }, "expressionEditor": { diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index 3d6f53209..87476b15e 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -728,8 +728,8 @@ "property": "プロパティ", "rule": "条件", "repair": "メッセージ列の補正", - "value-rules": "値ルール", - "sequence-rules": "列ルール" + "value-rules": "値ルール", + "sequence-rules": "列ルール" }, "previous": "前回の値", "and": "~", From 6c15fb6978b35f02261fb965883c42d91e05bbe3 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 22 May 2022 10:51:35 +0900 Subject: [PATCH 02/53] Fix indents in message catalog --- .../editor-client/locales/en-US/editor.json | 118 +++++----- .../editor-client/locales/en-US/infotips.json | 38 ++-- .../editor-client/locales/en-US/jsonata.json | 80 +++---- .../nodes/locales/en-US/messages.json | 212 +++++++++--------- .../runtime/locales/en-US/runtime.json | 17 +- 5 files changed, 228 insertions(+), 237 deletions(-) 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 4f3a3b104..01107d49d 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 @@ -3,7 +3,7 @@ "label": { "name": "Name", "ok": "Ok", - "done":"Done", + "done": "Done", "cancel": "Cancel", "delete": "Delete", "close": "Close", @@ -66,7 +66,7 @@ "listSubflows": "List subflows", "status": "Status", "enabled": "Enabled", - "disabled":"Disabled", + "disabled": "Disabled", "info": "Description", "selectNodes": "Click nodes to select" }, @@ -114,7 +114,7 @@ "keyboardShortcuts": "Keyboard shortcuts", "login": "Login", "logout": "Logout", - "editPalette":"Manage palette", + "editPalette": "Manage palette", "other": "Other", "showTips": "Show tips", "showWelcomeTours": "Show guided tours for new versions", @@ -130,19 +130,19 @@ "ungroupSelection": "Ungroup selection", "groupMergeSelection": "Merge selection", "groupRemoveSelection": "Remove from group", - "arrange":"Arrange", - "alignLeft":"Align to left", - "alignCenter":"Align to center", - "alignRight":"Align to right", - "alignTop":"Align to top", - "alignMiddle":"Align to middle", - "alignBottom":"Align to bottom", - "distributeHorizontally":"Distribute horizontally", - "distributeVertically":"Distribute vertically", - "moveToBack":"Move to back", - "moveToFront":"Move to front", - "moveBackwards":"Move backwards", - "moveForwards":"Move forwards" + "arrange": "Arrange", + "alignLeft": "Align to left", + "alignCenter": "Align to center", + "alignRight": "Align to right", + "alignTop": "Align to top", + "alignMiddle": "Align to middle", + "alignBottom": "Align to bottom", + "distributeHorizontally": "Distribute horizontally", + "distributeVertically": "Distribute vertically", + "moveToBack": "Move to back", + "moveToFront": "Move to front", + "moveBackwards": "Move backwards", + "moveForwards": "Move forwards" } }, "actions": { @@ -176,10 +176,10 @@ "nodeActionDisabledSubflow": "node actions disabled within subflow", "missing-types": "

Flows stopped due to missing node types.

", "missing-modules": "

Flows stopped due to missing modules.

", - "safe-mode":"

Flows stopped in safe mode.

You can modify your flows and deploy the changes to restart.

", + "safe-mode": "

Flows stopped in safe mode.

You can modify your flows and deploy the changes to restart.

", "restartRequired": "Node-RED must be restarted to enable upgraded modules", "credentials_load_failed": "

Flows stopped as the credentials could not be decrypted.

The flow credential file is encrypted, but the project's encryption key is missing or invalid.

", - "credentials_load_failed_reset":"

Credentials could not be decrypted

The flow credential file is encrypted, but the project's encryption key is missing or invalid.

The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.

", + "credentials_load_failed_reset": "

Credentials could not be decrypted

The flow credential file is encrypted, but the project's encryption key is missing or invalid.

The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.

", "missing_flow_file": "

Project flow file not found.

The project is not configured with a flow file.

", "missing_package_file": "

Project package file not found.

The project is missing a package.json file.

", "project_empty": "

The project is empty.

Do you want to create a default set of project files?
Otherwise, you will have to manually add files to the project outside of the editor.

", @@ -257,11 +257,11 @@ "recoveredNodesInfo": "The nodes on this flow were missing a valid flow id when they were imported. They have been added to this flow so you can either restore or delete them.", "recoveredNodesNotification": "

Imported nodes without a valid flow id

They have been added to a new flow called '__flowName__'.

", "export": { - "selected":"selected nodes", - "current":"current flow", - "all":"all flows", - "compact":"compact", - "formatted":"formatted", + "selected": "selected nodes", + "current": "current flow", + "all": "all flows", + "compact": "compact", + "formatted": "formatted", "copy": "Copy to clipboard", "export": "Export to library", "exportAs": "Export as", @@ -301,10 +301,10 @@ "successfulDeploy": "Successfully deployed", "successfulRestart": "Successfully restarted flows", "deployFailed": "Deploy failed: __message__", - "unusedConfigNodes":"You have some unused configuration nodes.", - "unusedConfigNodesButton":"Search unused config nodes", - "unknownNodesButton":"Search for unknown nodes", - "invalidNodesButton":"Search for invalid nodes", + "unusedConfigNodes": "You have some unused configuration nodes.", + "unusedConfigNodesButton": "Search unused config nodes", + "unknownNodesButton": "Search for unknown nodes", + "invalidNodesButton": "Search for invalid nodes", "errors": { "noResponse": "no response from server" }, @@ -351,8 +351,8 @@ }, "nodeCount": "__count__ node", "nodeCount_plural": "__count__ nodes", - "local":"Local changes", - "remote":"Remote changes", + "local": "Local changes", + "remote": "Remote changes", "reviewChanges": "Review Changes", "noBinaryFileShowed": "Cannot show binary file contents", "viewCommitDiff": "View Commit Changes", @@ -436,7 +436,7 @@ "inputType": "Input type", "selectType": "select types...", "loadCredentials": "Loading node credentials", - "inputs" : { + "inputs": { "input": "input", "select": "select", "checkbox": "checkbox", @@ -617,19 +617,19 @@ }, "confirm": { "install": { - "body":"

Installing '__module__'

Before installing, please read the node's documentation. Some nodes have dependencies that cannot be automatically resolved and can require a restart of Node-RED.

", + "body": "

Installing '__module__'

Before installing, please read the node's documentation. Some nodes have dependencies that cannot be automatically resolved and can require a restart of Node-RED.

", "title": "Install nodes" }, "remove": { - "body":"

Removing '__module__'

Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.

", + "body": "

Removing '__module__'

Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.

", "title": "Remove nodes" }, "update": { - "body":"

Updating '__module__'

Updating the node will require a restart of Node-RED to complete the update. This must be done manually.

", + "body": "

Updating '__module__'

Updating the node will require a restart of Node-RED to complete the update. This must be done manually.

", "title": "Update nodes" }, "cannotUpdate": { - "body":"An update for this node is available, but it is not installed in a location that the palette manager can update.

Please refer to the documentation for how to update this node." + "body": "An update for this node is available, but it is not installed in a location that the palette manager can update.

Please refer to the documentation for how to update this node." }, "button": { "review": "Open node information", @@ -663,14 +663,14 @@ "showMore": "show more", "showLess": "show less", "flow": "Flow", - "selection":"Selection", - "nodes":"__count__ nodes", + "selection": "Selection", + "nodes": "__count__ nodes", "flowDesc": "Flow Description", "subflowDesc": "Subflow Description", "nodeHelp": "Node Help", - "none":"None", + "none": "None", "arrayItems": "__count__ items", - "showTips":"You can open the tips from the settings panel", + "showTips": "You can open the tips from the settings panel", "outline": "Outline", "empty": "empty", "globalConfig": "Global Configuration Nodes", @@ -701,8 +701,8 @@ "filtered": "__count__ hidden" }, "context": { - "name":"Context Data", - "label":"context", + "name": "Context Data", + "label": "context", "none": "none selected", "refresh": "refresh to load", "empty": "empty", @@ -740,9 +740,9 @@ "files": "Files", "flow": "Flow", "credentials": "Credentials", - "package":"Package", - "packageCreate":"File will be created when changes are saved", - "fileNotExist":"File does not exist", + "package": "Package", + "packageCreate": "File will be created when changes are saved", + "fileNotExist": "File does not exist", "selectFile": "Select File", "invalidEncryptionKey": "Invalid encryption key", "encryptionEnabled": "Encryption enabled", @@ -978,7 +978,7 @@ "title": "Buffer editor", "modeString": "Handle as UTF-8 String", "modeArray": "Handle as JSON array", - "modeDesc":"

Buffer editor

The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.

For example, a value of Hello World will be converted to the JSON array:

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" + "modeDesc": "

Buffer editor

The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.

For example, a value of Hello World will be converted to the JSON array:

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" }, "projects": { "config-git": "Configure Git client", @@ -1140,7 +1140,7 @@ "no-empty": "Cannot create default file set on a non-empty project", "git-error": "git error" }, - "errors" : { + "errors": { "no-username-email": "Your Git client is not configured with a username/email.", "unexpected": "An unexpected error occurred", "code": "code" @@ -1163,7 +1163,7 @@ "diagnostics": { "title": "System Info" }, - "languages" : { + "languages": { "de": "German", "en-US": "English", "ja": "Japanese", @@ -1174,18 +1174,18 @@ }, "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__" - } + "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/en-US/infotips.json b/packages/node_modules/@node-red/editor-client/locales/en-US/infotips.json index 21a7e735f..a958868ee 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/infotips.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/infotips.json @@ -1,23 +1,23 @@ { "info": { - "tip0" : "You can remove the selected nodes or links with {{core:delete-selection}}", - "tip1" : "Search for nodes using {{core:search}}", - "tip2" : "{{core:toggle-sidebar}} will toggle the view of this sidebar", - "tip3" : "You can manage your palette of nodes with {{core:manage-palette}}", - "tip4" : "Your flow configuration nodes are listed in the sidebar panel. It can be accessed from the menu or with {{core:show-config-tab}}", - "tip5" : "Enable or disable these tips from the option in the settings", - "tip6" : "Move the selected nodes using the [left] [up] [down] and [right] keys. Hold [shift] to nudge them further", - "tip7" : "Dragging a node onto a wire will splice it into the link", - "tip8" : "Export the selected nodes, or the current tab with {{core:show-export-dialog}}", - "tip9" : "Import a flow by dragging its JSON into the editor, or with {{core:show-import-dialog}}", - "tip10" : "[shift] [click] and drag on a node port to move all of the attached wires or just the selected one", - "tip11" : "Show the Info tab with {{core:show-info-tab}} or the Debug tab with {{core:show-debug-tab}}", - "tip12" : "[ctrl] [click] in the workspace to open the quick-add dialog", - "tip13" : "Hold down [ctrl] when you [click] on a node port to enable quick-wiring", - "tip14" : "Hold down [shift] when you [click] on a node to also select all of its connected nodes", - "tip15" : "Hold down [ctrl] when you [click] on a node to add or remove it from the current selection", - "tip16" : "Switch flow tabs with {{core:show-previous-tab}} and {{core:show-next-tab}}", - "tip17" : "You can confirm your changes in the node edit tray with {{core:confirm-edit-tray}} or cancel them with {{core:cancel-edit-tray}}", - "tip18" : "Pressing {{core:edit-selected-node}} will edit the first node in the current selection" + "tip0": "You can remove the selected nodes or links with {{core:delete-selection}}", + "tip1": "Search for nodes using {{core:search}}", + "tip2": "{{core:toggle-sidebar}} will toggle the view of this sidebar", + "tip3": "You can manage your palette of nodes with {{core:manage-palette}}", + "tip4": "Your flow configuration nodes are listed in the sidebar panel. It can be accessed from the menu or with {{core:show-config-tab}}", + "tip5": "Enable or disable these tips from the option in the settings", + "tip6": "Move the selected nodes using the [left] [up] [down] and [right] keys. Hold [shift] to nudge them further", + "tip7": "Dragging a node onto a wire will splice it into the link", + "tip8": "Export the selected nodes, or the current tab with {{core:show-export-dialog}}", + "tip9": "Import a flow by dragging its JSON into the editor, or with {{core:show-import-dialog}}", + "tip10": "[shift] [click] and drag on a node port to move all of the attached wires or just the selected one", + "tip11": "Show the Info tab with {{core:show-info-tab}} or the Debug tab with {{core:show-debug-tab}}", + "tip12": "[ctrl] [click] in the workspace to open the quick-add dialog", + "tip13": "Hold down [ctrl] when you [click] on a node port to enable quick-wiring", + "tip14": "Hold down [shift] when you [click] on a node to also select all of its connected nodes", + "tip15": "Hold down [ctrl] when you [click] on a node to add or remove it from the current selection", + "tip16": "Switch flow tabs with {{core:show-previous-tab}} and {{core:show-next-tab}}", + "tip17": "You can confirm your changes in the node edit tray with {{core:confirm-edit-tray}} or cancel them with {{core:cancel-edit-tray}}", + "tip18": "Pressing {{core:edit-selected-node}} will edit the first node in the current selection" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json index 6a00a3c09..b24a898b7 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json @@ -52,52 +52,52 @@ "desc": "Finds occurrences of `pattern` within `str` and replaces them with `replacement`.\n\nThe optional `limit` parameter is the maximum number of replacements." }, "$now": { - "args":"$[picture [, timezone]]", - "desc":"Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional picture and timezone parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function" + "args": "$[picture [, timezone]]", + "desc": "Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional picture and timezone parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function" }, "$base64encode": { - "args":"string", - "desc":"Converts an ASCII string to a base 64 representation. Each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported." + "args": "string", + "desc": "Converts an ASCII string to a base 64 representation. Each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported." }, "$base64decode": { - "args":"string", - "desc":"Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage." + "args": "string", + "desc": "Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage." }, "$number": { "args": "arg", "desc": "Casts the `arg` parameter to a number using the following casting rules:\n\n - Numbers are unchanged\n - Strings that contain a sequence of characters that represent a legal JSON number are converted to that number\n - All other values cause an error to be thrown." }, "$abs": { - "args":"number", - "desc":"Returns the absolute value of the `number` parameter." + "args": "number", + "desc": "Returns the absolute value of the `number` parameter." }, "$floor": { - "args":"number", - "desc":"Returns the value of `number` rounded down to the nearest integer that is smaller or equal to `number`." + "args": "number", + "desc": "Returns the value of `number` rounded down to the nearest integer that is smaller or equal to `number`." }, "$ceil": { - "args":"number", - "desc":"Returns the value of `number` rounded up to the nearest integer that is greater than or equal to `number`." + "args": "number", + "desc": "Returns the value of `number` rounded up to the nearest integer that is greater than or equal to `number`." }, "$round": { - "args":"number [, precision]", - "desc":"Returns the value of the `number` parameter rounded to the number of decimal places specified by the optional `precision` parameter." + "args": "number [, precision]", + "desc": "Returns the value of the `number` parameter rounded to the number of decimal places specified by the optional `precision` parameter." }, "$power": { - "args":"base, exponent", - "desc":"Returns the value of `base` raised to the power of `exponent`." + "args": "base, exponent", + "desc": "Returns the value of `base` raised to the power of `exponent`." }, "$sqrt": { - "args":"number", - "desc":"Returns the square root of the value of the `number` parameter." + "args": "number", + "desc": "Returns the square root of the value of the `number` parameter." }, "$random": { - "args":"", - "desc":"Returns a pseudo random number greater than or equal to zero and less than one." + "args": "", + "desc": "Returns a pseudo random number greater than or equal to zero and less than one." }, "$millis": { - "args":"", - "desc":"Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value." + "args": "", + "desc": "Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value." }, "$sum": { "args": "array", @@ -136,20 +136,20 @@ "desc": "Appends two arrays" }, "$sort": { - "args":"array [, function]", - "desc":"Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values left and right. If the value of left should be placed after the value of right in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`." + "args": "array [, function]", + "desc": "Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values left and right. If the value of left should be placed after the value of right in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`." }, "$reverse": { - "args":"array", - "desc":"Returns an array containing all the values from the `array` parameter, but in reverse order." + "args": "array", + "desc": "Returns an array containing all the values from the `array` parameter, but in reverse order." }, "$shuffle": { - "args":"array", - "desc":"Returns an array containing all the values from the `array` parameter, but shuffled into random order." + "args": "array", + "desc": "Returns an array containing all the values from the `array` parameter, but shuffled into random order." }, "$zip": { - "args":"array, ...", - "desc":"Returns a convolved (zipped) array containing grouped arrays of values from the `array1` … `arrayN` arguments from index 0, 1, 2...." + "args": "array, ...", + "desc": "Returns a convolved (zipped) array containing grouped arrays of values from the `array1` … `arrayN` arguments from index 0, 1, 2...." }, "$keys": { "args": "object", @@ -168,24 +168,24 @@ "desc": "Merges an array of `objects` into a single `object` containing all the key/value pairs from each of the objects in the input array. If any of the input objects contain the same key, then the returned `object` will contain the value of the last one in the array. It is an error if the input array contains an item that is not an object." }, "$sift": { - "args":"object, function", - "desc":"Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`" + "args": "object, function", + "desc": "Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`" }, "$each": { - "args":"object, function", - "desc":"Returns an array containing the values return by the `function` when applied to each key/value pair in the `object`." + "args": "object, function", + "desc": "Returns an array containing the values return by the `function` when applied to each key/value pair in the `object`." }, "$map": { - "args":"array, function", - "desc":"Returns an array containing the results of applying the `function` parameter to each value in the `array` parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`" + "args": "array, function", + "desc": "Returns an array containing the results of applying the `function` parameter to each value in the `array` parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`" }, "$filter": { - "args":"array, function", - "desc":"Returns an array containing only the values in the `array` parameter that satisfy the `function` predicate.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`" + "args": "array, function", + "desc": "Returns an array containing only the values in the `array` parameter that satisfy the `function` predicate.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`" }, "$reduce": { - "args":"array, function [, init]", - "desc":"Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`. The signature of `function` must be of the form: `myfunc($accumulator, $value[, $index[, $array]])`\n\nThe optional `init` parameter is used as the initial value in the aggregation." + "args": "array, function [, init]", + "desc": "Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`. The signature of `function` must be of the form: `myfunc($accumulator, $value[, $index[, $array]])`\n\nThe optional `init` parameter is used as the initial value in the aggregation." }, "$flowContext": { "args": "string[, string]", diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index e3a435619..a9ebae904 100755 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -193,22 +193,22 @@ "key": "Private Key", "passphrase": "Passphrase", "ca": "CA Certificate", - "verify-server-cert":"Verify server certificate", + "verify-server-cert": "Verify server certificate", "servername": "Server Name", "alpnprotocol": "ALPN Protocol" }, "placeholder": { - "cert":"path to certificate (PEM format)", - "key":"path to private key (PEM format)", - "ca":"path to CA certificate (PEM format)", - "passphrase":"private key passphrase (optional)", - "servername":"for use with SNI", - "alpnprotocol":"for use with ALPN" + "cert": "path to certificate (PEM format)", + "key": "path to private key (PEM format)", + "ca": "path to CA certificate (PEM format)", + "passphrase": "private key passphrase (optional)", + "servername": "for use with SNI", + "alpnprotocol": "for use with ALPN" }, "error": { "missing-file": "No certificate/key file provided", - "invalid-cert": "Certificate not specified", - "invalid-key": "Private key not specified" + "invalid-cert": "Certificate not specified", + "invalid-key": "Private key not specified" } }, "exec": { @@ -262,10 +262,10 @@ "moduleLoadError": "Failed to load module __module__: __error__", "moduleNameError": "Invalid module variable name: __name__", "moduleNameReserved": "Reserved variable name: __name__", - "inputListener":"Cannot add listener to 'input' event within Function", - "non-message-returned":"Function tried to send a message of type __type__", - "invalid-js": "Error in JavaScript code", - "missing-module": "Module __module__ missing" + "inputListener": "Cannot add listener to 'input' event within Function", + "non-message-returned": "Function tried to send a message of type __type__", + "invalid-js": "Error in JavaScript code", + "missing-module": "Module __module__ missing" } }, "template": { @@ -319,35 +319,35 @@ "limit": "limit", "limitTopic": "limit topic", "random": "random", - "rate": "rate", - "random-first": "first random value", - "random-last": "last random value", - "units" : { + "rate": "rate", + "random-first": "first random value", + "random-last": "last random value", + "units": { "second": { - "plural" : "Seconds", + "plural": "Seconds", "singular": "Second" }, "minute": { - "plural" : "Minutes", + "plural": "Minutes", "singular": "Minute" }, "hour": { - "plural" : "Hours", + "plural": "Hours", "singular": "Hour" }, "day": { - "plural" : "Days", + "plural": "Days", "singular": "Day" } } }, "errors": { - "too-many" : "too many pending messages in delay node", - "invalid-timeout": "Invalid delay value", - "invalid-rate": "Invalid rate value", - "invalid-rate-unit": "Invalid rate unit value", - "invalid-random-first": "Invalid first random value", - "invalid-random-last": "Invalid last random value" + "too-many": "too many pending messages in delay node", + "invalid-timeout": "Invalid delay value", + "invalid-rate": "Invalid rate value", + "invalid-rate-unit": "Invalid rate unit value", + "invalid-random-first": "Invalid first random value", + "invalid-random-last": "Invalid last random value" } }, "trigger": { @@ -382,11 +382,11 @@ "trigger-block": "trigger & block", "trigger-loop": "resend every", "reset": "Reset the trigger if:", - "resetMessage":"msg.reset is set", - "resetPayload":"msg.payload equals", + "resetMessage": "msg.reset is set", + "resetPayload": "msg.payload equals", "resetprompt": "optional", - "duration": "duration", - "topic": "topic" + "duration": "duration", + "topic": "topic" } }, "comment": { @@ -411,8 +411,8 @@ "cleansession": "Use clean session", "cleanstart": "Use clean start", "use-tls": "Use TLS", - "tls-config":"TLS Configuration", - "verify-server-cert":"Verify server certificate", + "tls-config": "TLS Configuration", + "verify-server-cert": "Verify server certificate", "compatmode": "Use legacy MQTT 3.1 support", "userProperties": "User Properties", "subscriptionIdentifier": "Subscription ID", @@ -447,10 +447,10 @@ "auto-connect": "Connect automatically", "auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode." }, - "sections-label":{ + "sections-label": { "birth-message": "Message sent on connection (birth message)", - "will-message":"Message sent on an unexpected disconnection (will message)", - "close-message":"Message sent before disconnecting (close message)" + "will-message": "Message sent on an unexpected disconnection (will message)", + "close-message": "Message sent before disconnecting (close message)" }, "tabs-label": { "connection": "Connection", @@ -459,7 +459,7 @@ }, "placeholder": { "clientid": "Leave blank for auto generated", - "clientid-nonclean":"Must be set for non-clean sessions", + "clientid-nonclean": "Must be set for non-clean sessions", "will-topic": "Leave blank to disable will message", "birth-topic": "Leave blank to disable birth message", "close-topic": "Leave blank to disable close message" @@ -492,8 +492,7 @@ "invalid-action-action": "Invalid action specified", "invalid-action-alreadyconnected": "Disconnect from broker before connecting", "invalid-action-badsubscription": "msg.topic is missing or invalid", - "invalid-client-id": "Missing Client ID" - + "invalid-client-id": "Missing Client ID" } }, "httpin": { @@ -506,7 +505,7 @@ "status": "Status code", "headers": "Headers", "other": "other", - "paytoqs" : { + "paytoqs": { "ignore": "Ignore", "query": "Append to query-string parameters", "body": "Send as request body" @@ -520,7 +519,7 @@ "setby": "- set by msg.method -", "basicauth": "Use authentication", "use-tls": "Enable secure (SSL/TLS) connection", - "tls-config":"TLS Configuration", + "tls-config": "TLS Configuration", "basic": "basic authentication", "digest": "digest authentication", "bearer": "bearer authentication", @@ -545,8 +544,8 @@ "no-response": "No response object", "json-error": "JSON parse error", "no-url": "No url specified", - "deprecated-call":"Deprecated call to __method__", - "invalid-transport":"non-http transport requested", + "deprecated-call": "Deprecated call to __method__", + "invalid-transport": "non-http transport requested", "timeout-isnan": "Timeout value is not a valid number, ignoring", "timeout-isnegative": "Timeout value is negative, ignoring", "invalid-payload": "Invalid payload", @@ -584,8 +583,8 @@ "send-error": "An error occurred while sending: ", "missing-conf": "Missing server configuration", "duplicate-path": "Cannot have two WebSocket listeners on the same path: __path__", - "missing-server": "Missing server configuration", - "missing-client": "Missing client configuration" + "missing-server": "Missing server configuration", + "missing-client": "Missing client configuration" } }, "watch": { @@ -645,7 +644,6 @@ "connection-closed": "connection closed from __host__:__port__", "connections": "__count__ connection", "connections_plural": "__count__ connections" - }, "errors": { "connection-lost": "connection lost to __host__:__port__", @@ -657,8 +655,8 @@ "connect-timeout": "connect timeout", "connect-fail": "connect failed", "bad-string": "failed to convert to string", - "invalid-host": "Invalid host", - "invalid-port": "Invalid port" + "invalid-host": "Invalid host", + "invalid-port": "Invalid port" } }, "udp": { @@ -673,7 +671,7 @@ "toport": "to port", "address": "Address", "decode-base64": "Decode Base64 encoded payload?", - "port": "port" + "port": "port" }, "placeholder": { "interface": "(optional) local interface or address to bind to", @@ -721,7 +719,7 @@ "port-invalid": "udp: port number not valid", "alreadyused": "udp: port __port__ already in use", "ifnotfound": "udp: interface __iface__ not found", - "invalid-group": "invalid multicast group" + "invalid-group": "invalid multicast group" } }, "switch": { @@ -730,8 +728,8 @@ "property": "Property", "rule": "rule", "repair": "recreate message sequences", - "value-rules": "value rules", - "sequence-rules": "sequence rules" + "value-rules": "value rules", + "sequence-rules": "sequence rules" }, "previous": "previous value", "and": "and", @@ -788,8 +786,8 @@ "invalid-json": "Invalid 'to' JSON property", "invalid-expr": "Invalid JSONata expression: __error__", "no-override": "Cannot set property of non-object type: __property__", - "invalid-prop": "Invalid property expression: __property__", - "invalid-json-data": "Invalid JSON data: __error__" + "invalid-prop": "Invalid property expression: __property__", + "invalid-json-data": "Invalid JSON data: __error__" } }, "range": { @@ -801,10 +799,10 @@ "from": "from", "to": "to", "roundresult": "Round result to the nearest integer?", - "minin": "input from", - "maxin": "input to", - "minout": "target from", - "maxout": "target to" + "minin": "input from", + "maxin": "input to", + "minout": "target from", + "maxout": "target to" }, "placeholder": { "min": "e.g. 0", @@ -901,8 +899,8 @@ "property": "Property", "actions": { "toggle": "Convert between JSON String & Object", - "str":"Always convert to JSON String", - "obj":"Always convert to JavaScript Object" + "str": "Always convert to JSON String", + "obj": "Always convert to JavaScript Object" } } }, @@ -988,15 +986,15 @@ }, "split": { "split": "split", - "intro":"Split msg.payload based on type:", - "object":"Object", - "objectSend":"Send a message for each key/value pair", - "strBuff":"String / Buffer", - "array":"Array", - "splitUsing":"Split using", - "splitLength":"Fixed length of", - "stream":"Handle as a stream of messages", - "addname":" Copy key to " + "intro": "Split msg.payload based on type:", + "object": "Object", + "objectSend": "Send a message for each key/value pair", + "strBuff": "String / Buffer", + "array": "Array", + "splitUsing": "Split using", + "splitLength": "Fixed length of", + "stream": "Handle as a stream of messages", + "addname": " Copy key to " }, "join": { "join": "join", @@ -1029,7 +1027,7 @@ "complete": "After a message with the msg.complete property set", "tip": "This mode assumes this node is either paired with a split node or the received messages will have a properly configured msg.parts property.", "too-many": "too many pending messages in join node", - "message-prop": "message property", + "message-prop": "message property", "merge": { "topics-label": "Merged Topics", "topics": "topics", @@ -1048,51 +1046,51 @@ "invalid-type": "Cannot join __error__ to buffer" } }, - "sort" : { + "sort": { "sort": "sort", - "target" : "Sort", - "seq" : "message sequence", - "key" : "Key", - "elem" : "element value", - "order" : "Order", - "ascending" : "ascending", - "descending" : "descending", - "as-number" : "as number", - "invalid-exp" : "Invalid JSONata expression in sort node: __message__", - "too-many" : "Too many pending messages in sort node", - "clear" : "clear pending message in sort node" + "target": "Sort", + "seq": "message sequence", + "key": "Key", + "elem": "element value", + "order": "Order", + "ascending": "ascending", + "descending": "descending", + "as-number": "as number", + "invalid-exp": "Invalid JSONata expression in sort node: __message__", + "too-many": "Too many pending messages in sort node", + "clear": "clear pending message in sort node" }, - "batch" : { + "batch": { "batch": "batch", "mode": { - "label" : "Mode", - "num-msgs" : "Group by number of messages", - "interval" : "Group by time interval", - "concat" : "Concatenate sequences" + "label": "Mode", + "num-msgs": "Group by number of messages", + "interval": "Group by time interval", + "concat": "Concatenate sequences" }, "count": { - "label" : "Number of messages", - "overlap" : "Overlap", - "count" : "count", - "invalid" : "Invalid count and overlap" + "label": "Number of messages", + "overlap": "Overlap", + "count": "count", + "invalid": "Invalid count and overlap" }, "interval": { - "label" : "Interval", - "seconds" : "seconds", - "empty" : "send empty message when no message arrives" + "label": "Interval", + "seconds": "seconds", + "empty": "send empty message when no message arrives" }, "concat": { "topics-label": "Topics", - "topic" : "topic" + "topic": "topic" }, - "too-many" : "too many pending messages in batch node", - "unexpected" : "unexpected mode", - "no-parts" : "no parts property in message", - "error": { - "invalid-count": "Invalid count", - "invalid-overlap": "Invalid overlap", - "invalid-interval": "Invalid interval" - } + "too-many": "too many pending messages in batch node", + "unexpected": "unexpected mode", + "no-parts": "no parts property in message", + "error": { + "invalid-count": "Invalid count", + "invalid-overlap": "Invalid overlap", + "invalid-interval": "Invalid interval" + } }, "rbe": { "rbe": "filter", @@ -1102,11 +1100,11 @@ "start": "Start value", "name": "Name", "septopics": "Apply mode separately for each ", - "gap": "value change", - "property": "property", - "topic": "topic" + "gap": "value change", + "property": "property", + "topic": "topic" }, - "placeholder":{ + "placeholder": { "bandgap": "e.g. 10 or 5%", "start": "leave blank to use first data received" }, diff --git a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json index cb2e08aab..3b46d0c9f 100644 --- a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json +++ b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json @@ -8,7 +8,6 @@ "httpStatic": "HTTP Static : __path__" } }, - "server": { "loading": "Loading palette nodes", "palette-editor": { @@ -61,7 +60,6 @@ "function-required": "httpsRefreshInterval requires https property to be a function" } }, - "api": { "flows": { "error-save": "Error saving flows: __message__", @@ -79,18 +77,16 @@ "error-enable": "Failed to enable node:" } }, - "comms": { "error": "Communication channel error: __message__", "error-server": "Communication server error: __message__", "error-send": "Communication send error: __message__" }, - "settings": { "user-not-available": "Cannot save user settings: __message__", "not-available": "Settings not available", "property-read-only": "Property '__prop__' is read-only", - "readonly-mode" : "Runtime in read-only mode. Changes will not be saved." + "readonly-mode": "Runtime in read-only mode. Changes will not be saved." }, "library": { "unknownLibrary": "Unknown library: __library__", @@ -101,12 +97,12 @@ }, "nodes": { "credentials": { - "error":"Error loading credentials: __message__", - "error-saving":"Error saving credentials: __message__", + "error": "Error loading credentials: __message__", + "error-saving": "Error saving credentials: __message__", "not-registered": "Credential type '__type__' is not registered", "system-key-warning": "\n\n---------------------------------------------------------------------\nYour flow credentials file is encrypted using a system-generated key.\n\nIf the system-generated key is lost for any reason, your credentials\nfile will not be recoverable, you will have to delete it and re-enter\nyour credentials.\n\nYou should set your own key using the 'credentialSecret' option in\nyour settings file. Node-RED will then re-encrypt your credentials\nfile using your chosen key the next time you deploy a change.\n---------------------------------------------------------------------\n", - "unencrypted" : "Using unencrypted credentials", - "encryptedNotFound" : "Encrypted credentials not found" + "unencrypted": "Using unencrypted credentials", + "encryptedNotFound": "Encrypted credentials not found" }, "flows": { "safe-mode": "Flows stopped in safe mode. Deploy to start.", @@ -150,7 +146,6 @@ } } }, - "storage": { "index": { "forbidden-flow-name": "forbidden flow name" @@ -180,7 +175,6 @@ } } }, - "context": { "log-store-init": "Context store : '__name__' [__info__]", "error-loading-module": "Error loading context store: __message__", @@ -195,5 +189,4 @@ "error-write": "Error writing context: __message__" } } - } From dda84c5cd5f2f36a44ae7754f4324037619c6b39 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 22 May 2022 11:18:40 +0900 Subject: [PATCH 03/53] Fix indents in other code except message catalog --- .../editor-client/src/sass/jquery.scss | 4 +- .../@node-red/nodes/core/common/21-debug.html | 2 +- packages/node_modules/node-red/settings.js | 142 +++++++++--------- 3 files changed, 74 insertions(+), 74 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/jquery.scss b/packages/node_modules/@node-red/editor-client/src/sass/jquery.scss index a884d6641..b7278b332 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/jquery.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/jquery.scss @@ -27,10 +27,10 @@ } .ui-widget.ui-widget-content { - border: 1px solid $tertiary-border-color; + border: 1px solid $tertiary-border-color; } .ui-widget-content { - border: 1px solid $secondary-border-color; + border: 1px solid $secondary-border-color; } .ui-widget-header { diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.html b/packages/node_modules/@node-red/nodes/core/common/21-debug.html index 51e47e2f2..62da1b259 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.html +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.html @@ -499,7 +499,7 @@ types:['msg', fullType, "jsonata"], typeField: $("#node-input-targetType") }); - if (this.targetType === "jsonata") { + if (this.targetType === "jsonata") { var property = this.complete || ""; $("#node-input-typed-complete").typedInput('type','jsonata'); $("#node-input-typed-complete").typedInput('value',property); diff --git a/packages/node_modules/node-red/settings.js b/packages/node_modules/node-red/settings.js index 2e2b7035e..d8f879851 100644 --- a/packages/node_modules/node-red/settings.js +++ b/packages/node_modules/node-red/settings.js @@ -249,18 +249,18 @@ module.exports = { * - externalModules ******************************************************************************/ - /** Uncomment the following to run node-red in your preferred language. - * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko - * Some languages are more complete than others. - */ - // lang: "de", + /** Uncomment the following to run node-red in your preferred language. + * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko + * Some languages are more complete than others. + */ + // lang: "de", /** Configure diagnostics options * - enabled: When `enabled` is `true` (or unset), diagnostics data will * be available at http://localhost:1880/diagnostics * - ui: When `ui` is `true` (or unset), the action `show-system-info` will * be available to logged in users of node-red editor - */ + */ diagnostics: { /** enable or disable diagnostics endpoint. Must be set to `false` to disable */ enabled: true, @@ -268,74 +268,74 @@ module.exports = { ui: true, }, - /** Configure the logging output */ - logging: { - /** Only console logging is currently supported */ - console: { - /** Level of logging to be recorded. Options are: - * fatal - only those errors which make the application unusable should be recorded - * error - record errors which are deemed fatal for a particular request + fatal errors - * warn - record problems which are non fatal + errors + fatal errors - * info - record information about the general running of the application + warn + error + fatal errors - * debug - record information which is more verbose than info + info + warn + error + fatal errors - * trace - record very detailed logging + debug + info + warn + error + fatal errors - * off - turn off all logging (doesn't affect metrics or audit) - */ - level: "info", - /** Whether or not to include metric events in the log output */ - metrics: false, - /** Whether or not to include audit events in the log output */ - audit: false - } - }, + /** Configure the logging output */ + logging: { + /** Only console logging is currently supported */ + console: { + /** Level of logging to be recorded. Options are: + * fatal - only those errors which make the application unusable should be recorded + * error - record errors which are deemed fatal for a particular request + fatal errors + * warn - record problems which are non fatal + errors + fatal errors + * info - record information about the general running of the application + warn + error + fatal errors + * debug - record information which is more verbose than info + info + warn + error + fatal errors + * trace - record very detailed logging + debug + info + warn + error + fatal errors + * off - turn off all logging (doesn't affect metrics or audit) + */ + level: "info", + /** Whether or not to include metric events in the log output */ + metrics: false, + /** Whether or not to include audit events in the log output */ + audit: false + } + }, - /** Context Storage - * The following property can be used to enable context storage. The configuration - * provided here will enable file-based context that flushes to disk every 30 seconds. - * Refer to the documentation for further options: https://nodered.org/docs/api/context/ - */ - //contextStorage: { - // default: { - // module:"localfilesystem" - // }, - //}, + /** Context Storage + * The following property can be used to enable context storage. The configuration + * provided here will enable file-based context that flushes to disk every 30 seconds. + * Refer to the documentation for further options: https://nodered.org/docs/api/context/ + */ + //contextStorage: { + // default: { + // module:"localfilesystem" + // }, + //}, - /** `global.keys()` returns a list of all properties set in global context. - * This allows them to be displayed in the Context Sidebar within the editor. - * In some circumstances it is not desirable to expose them to the editor. The - * following property can be used to hide any property set in `functionGlobalContext` - * from being list by `global.keys()`. - * By default, the property is set to false to avoid accidental exposure of - * their values. Setting this to true will cause the keys to be listed. - */ - exportGlobalContextKeys: false, + /** `global.keys()` returns a list of all properties set in global context. + * This allows them to be displayed in the Context Sidebar within the editor. + * In some circumstances it is not desirable to expose them to the editor. The + * following property can be used to hide any property set in `functionGlobalContext` + * from being list by `global.keys()`. + * By default, the property is set to false to avoid accidental exposure of + * their values. Setting this to true will cause the keys to be listed. + */ + exportGlobalContextKeys: false, - /** Configure how the runtime will handle external npm modules. - * This covers: - * - whether the editor will allow new node modules to be installed - * - whether nodes, such as the Function node are allowed to have their - * own dynamically configured dependencies. - * The allow/denyList options can be used to limit what modules the runtime - * will install/load. It can use '*' as a wildcard that matches anything. - */ - externalModules: { - // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ - // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ - // palette: { /** Configuration for the Palette Manager */ - // allowInstall: true, /** Enable the Palette Manager in the editor */ - // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */ - // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ - // allowList: ['*'], - // denyList: [], - // allowUpdateList: ['*'], - // denyUpdateList: [] - // }, - // modules: { /** Configuration for node-specified modules */ - // allowInstall: true, - // allowList: [], - // denyList: [] - // } - }, + /** Configure how the runtime will handle external npm modules. + * This covers: + * - whether the editor will allow new node modules to be installed + * - whether nodes, such as the Function node are allowed to have their + * own dynamically configured dependencies. + * The allow/denyList options can be used to limit what modules the runtime + * will install/load. It can use '*' as a wildcard that matches anything. + */ + externalModules: { + // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ + // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ + // palette: { /** Configuration for the Palette Manager */ + // allowInstall: true, /** Enable the Palette Manager in the editor */ + // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */ + // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ + // allowList: ['*'], + // denyList: [], + // allowUpdateList: ['*'], + // denyUpdateList: [] + // }, + // modules: { /** Configuration for node-specified modules */ + // allowInstall: true, + // allowList: [], + // denyList: [] + // } + }, /******************************************************************************* From 6182e22d1868b5fdea18bdb6bfec2c276df9af08 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 22 May 2022 11:56:21 +0900 Subject: [PATCH 04/53] Add string label to typedInput in file nodes --- .../node_modules/@node-red/nodes/core/storage/10-file.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.html b/packages/node_modules/@node-red/nodes/core/storage/10-file.html index ddddcb687..69e91e3c5 100755 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/core/storage/10-file.html @@ -237,7 +237,8 @@ }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ default: "msg", - types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], + types:[{ value: "str", label: RED._("common.type.string"), + icon: "red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { @@ -342,7 +343,8 @@ }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ default: "msg", - types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], + types:[{ value: "str", label: RED._("common.type.string"), + icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { From bc80569fe9711f0407583f26a236a208c30d8c0b Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sun, 22 May 2022 17:59:27 +0900 Subject: [PATCH 05/53] fix buffer parse error message of evaluateNodeProperty --- packages/node_modules/@node-red/util/lib/util.js | 7 ++++++- test/unit/@node-red/util/lib/util_spec.js | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js index a6fec8f98..d82de0d1d 100644 --- a/packages/node_modules/@node-red/util/lib/util.js +++ b/packages/node_modules/@node-red/util/lib/util.js @@ -641,7 +641,12 @@ function evaluateNodeProperty(value, type, node, msg, callback) { result = Date.now(); } else if (type === 'bin') { var data = JSON.parse(value); - result = Buffer.from(data); + if (Array.isArray(data) || (typeof(data) === "string")) { + result = Buffer.from(data); + } + else { + throw createError("INVALID_BUFFER_DATA", "Not string or array"); + } } else if (type === 'msg' && msg) { try { result = getMessageProperty(msg,value); diff --git a/test/unit/@node-red/util/lib/util_spec.js b/test/unit/@node-red/util/lib/util_spec.js index 2013f143e..3bab2739a 100644 --- a/test/unit/@node-red/util/lib/util_spec.js +++ b/test/unit/@node-red/util/lib/util_spec.js @@ -388,6 +388,19 @@ describe("@node-red/util/util", function() { result[0].should.eql(1); result[1].should.eql(2); }); + it('throws an error if buffer data is not array or string', function (done) { + try { + var result = util.evaluateNodeProperty('12','bin'); + done("should throw an error"); + } catch (err) { + if (err.code === "INVALID_BUFFER_DATA") { + done(); + } + else { + done("should throw an error"); + } + } + }); it('returns msg property',function() { var result = util.evaluateNodeProperty('foo.bar','msg',{},{foo:{bar:"123"}}); result.should.eql("123"); From f424f07e54a0e6bb16d1a923ad8ea628d7de362e Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 25 May 2022 22:56:58 +0900 Subject: [PATCH 06/53] Fix typos in message catalog --- .../@node-red/editor-client/locales/ja/infotips.json | 2 +- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 2 +- packages/node_modules/@node-red/nodes/locales/ja/messages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 packages/node_modules/@node-red/nodes/locales/en-US/messages.json diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/infotips.json b/packages/node_modules/@node-red/editor-client/locales/ja/infotips.json index 45ba8450e..14459fa10 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/infotips.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/infotips.json @@ -17,7 +17,7 @@ "tip14": "[shift] を押しながらノードを [click] すると、接続された全てのノードを選択できます。", "tip15": "[ctrl] を押しながらノードを [click] すると、選択/非選択を切り替えできます。", "tip16": "{{core:show-previous-tab}} や {{core:show-next-tab}} で、タブの切り替えができます。", - "tip17": "ノードのプロバティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。", + "tip17": "ノードのプロパティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。", "tip18": "ノードを選択し、 {{core:edit-selected-node}} を押すとプロパティ設定画面が表示されます。" } } diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json old mode 100755 new mode 100644 index f077e1430..77d832abd --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -137,7 +137,7 @@ "toConsole": "system console", "toStatus": "node status (32 characters)", "severity": "Level", - "node": "node", + "node": "node", "notification": { "activated": "Successfully activated: __label__", "deactivated": "Successfully deactivated: __label__" diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index d9f5ee4a4..c508a3e76 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -137,7 +137,7 @@ "toConsole": "システムコンソール", "toStatus": "ノードステータス(32 文字)", "severity": "Level", - "node": "ノード", + "node": "ノード", "notification": { "activated": "有効化しました: __label__", "deactivated": "無効化しました: __label__" From 121372802f41bd333b9b52f8f3e90affa6cd940a Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Thu, 26 May 2022 22:50:52 +0900 Subject: [PATCH 07/53] Use built-in type in typedInput --- .../node_modules/@node-red/nodes/core/storage/10-file.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.html b/packages/node_modules/@node-red/nodes/core/storage/10-file.html index 69e91e3c5..b726b537d 100755 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/core/storage/10-file.html @@ -237,8 +237,7 @@ }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ default: "msg", - types:[{ value: "str", label: RED._("common.type.string"), - icon: "red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], + types: ["str", "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { @@ -343,8 +342,7 @@ }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ default: "msg", - types:[{ value: "str", label: RED._("common.type.string"), - icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"], + types: ["str", "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { From 7fbebbf3611050aeb74a1aa61f5e16c912d65713 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Fri, 27 May 2022 11:44:56 +0900 Subject: [PATCH 08/53] fix JSONata evaluation of inject button --- .../node_modules/@node-red/nodes/core/common/20-inject.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.js b/packages/node_modules/@node-red/nodes/core/common/20-inject.js index 92b8122df..734bce765 100644 --- a/packages/node_modules/@node-red/nodes/core/common/20-inject.js +++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.js @@ -109,9 +109,10 @@ module.exports = function(RED) { if (!property) return; if (valueType === "jsonata") { - if (p.exp) { + if (p.v) { try { - var val = RED.util.evaluateJSONataExpression(p.exp, msg); + var exp = RED.util.prepareJSONataExpression(p.v, node); + var val = RED.util.evaluateJSONataExpression(exp, msg); RED.util.setMessageProperty(msg, property, val, true); } catch (err) { From 7e6dfa7b927a513dea3c28beabc6b760e078748e Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Fri, 27 May 2022 12:11:53 +0900 Subject: [PATCH 09/53] update test for inject node --- test/nodes/core/common/20-inject_spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/nodes/core/common/20-inject_spec.js b/test/nodes/core/common/20-inject_spec.js index cb14690d0..a4dbdd9e4 100644 --- a/test/nodes/core/common/20-inject_spec.js +++ b/test/nodes/core/common/20-inject_spec.js @@ -906,6 +906,7 @@ describe('inject node', function() { msg.should.have.property("str1", "1"); //injected prop msg.should.have.property("num1", 1); //injected prop msg.should.have.property("bool1", true); //injected prop + msg.should.have.property("jsonata1", "AB"); //injected prop helper.clearFlows().then(function() { done(); @@ -919,6 +920,7 @@ describe('inject node', function() { {p:"str1", v:"1", vt:"str"}, //new prop {p:"num1", v:"1", vt:"num"}, //new prop {p:"bool1", v:"true", vt:"bool"}, //new prop + {p:"jsonata1", v:'"A" & "B"', vt:"jsonata"}, //new prop ]}) .expect(200).end(function(err) { if (err) { From 50e3da084973b444e37684fbc353be707e0082e3 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 28 May 2022 20:16:53 +0900 Subject: [PATCH 10/53] fix new folder menu of save to library dialog --- .../editor-client/src/js/ui/library.js | 200 +++++++++--------- 1 file changed, 103 insertions(+), 97 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js index ee176dee2..d276b1572 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js @@ -363,106 +363,112 @@ RED.library = (function() { options.onconfirm(item); } }); - var itemTools = $("
").css({position: "absolute",bottom:"6px",right:"8px"}); - var menuButton = $('') - .on("click", function(evt) { - evt.preventDefault(); - evt.stopPropagation(); - var elementPos = menuButton.offset(); - - var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu", - options: [ - {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() { - var defaultFolderName = "new-folder"; - var defaultFolderNameMatches = {}; - - var selected = dirList.treeList('selected'); - if (!selected.children) { - selected = selected.parent; - } - var complete = function() { - selected.children.forEach(function(c) { - if (/^new-folder/.test(c.label)) { - defaultFolderNameMatches[c.label] = true - } - }); - var folderIndex = 2; - while(defaultFolderNameMatches[defaultFolderName]) { - defaultFolderName = "new-folder-"+(folderIndex++) - } - - selected.treeList.expand(); - var input = $('').val(defaultFolderName); - var newItem = { - icon: "fa fa-folder-o", - children:[], - path: selected.path, - element: input - } - var confirmAdd = function() { - var val = input.val().trim(); - if (val === "") { - cancelAdd(); - return; - } else { - for (var i=0;i").css({position: "absolute",bottom:"6px",right:"8px"}); + var menuButton = $('') + .on("click", function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + var elementPos = menuButton.offset(); + + var menuOptionMenu + = RED.menu.init({id:"red-ui-library-browser-menu", + options: [ + {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() { + var defaultFolderName = "new-folder"; + var defaultFolderNameMatches = {}; + + var selected = dirList.treeList('selected'); + if (!selected.children) { + selected = selected.parent; + } + var complete = function() { + selected.children.forEach(function(c) { + if (/^new-folder/.test(c.label)) { + defaultFolderNameMatches[c.label] = true + } + }); + var folderIndex = 2; + while(defaultFolderNameMatches[defaultFolderName]) { + defaultFolderName = "new-folder-"+(folderIndex++) + } + + selected.treeList.expand(); + var input = $('').val(defaultFolderName); + var newItem = { + icon: "fa fa-folder-o", + children:[], + path: selected.path, + element: input + } + var confirmAdd = function() { + var val = input.val().trim(); + if (val === "") { + cancelAdd(); + return; + } else { + for (var i=0;i Date: Wed, 8 Jun 2022 09:36:32 +0900 Subject: [PATCH 11/53] place node dragged from palette within workspace --- .../@node-red/editor-client/src/js/ui/view.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index adab87e38..a15ec32f4 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -526,6 +526,23 @@ RED.view = (function() { nn.x = mousePos[0]; nn.y = mousePos[1]; + var minX = nn.w/2 -5; + if (nn.x < minX) { + nn.x = minX; + } + var minY = nn.h/2 -5; + if (nn.y < minY) { + nn.y = minY; + } + var maxX = space_width -nn.w/2 +5; + if (nn.x > maxX) { + nn.x = maxX; + } + var maxY = space_height -nn.h +5; + if (nn.y > maxY) { + nn.y = maxY; + } + if (snapGrid) { var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn); nn.x -= gridOffset.x; From 3046798ec552d6fbe194521234e44dda20e19292 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Wed, 8 Jun 2022 14:50:14 +0900 Subject: [PATCH 12/53] fix layer of palette node --- .../node_modules/@node-red/editor-client/src/sass/palette.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss index 397dd4765..800f34079 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss @@ -131,6 +131,7 @@ width: 120px; background-size: contain; position: relative; + z-index: 4; &:not(.red-ui-palette-node-config):not(.red-ui-palette-node-small):first-child { margin-top: 15px; } From deb9c4ecc098d2cdbecdd777be5a5819ea54dad2 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 9 Jun 2022 15:47:16 -0500 Subject: [PATCH 13/53] Reset mouse state when switching tabs Fixes #3639 --- .../@node-red/editor-client/src/js/ui/view.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index adab87e38..7d4d950f3 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -385,6 +385,9 @@ RED.view = (function() { drag_lines = []; RED.events.on("workspace:change",function(event) { + // Just in case the mouse left the workspace whilst doing an action, + // put us back into default mode so the refresh works + mouse_mode = 0 if (event.old !== 0) { workspaceScrollPositions[event.old] = { left:chart.scrollLeft(), @@ -955,7 +958,7 @@ RED.view = (function() { } function canvasMouseDown() { - if (RED.view.DEBUG) { + if (RED.view.DEBUG) { console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); } if (mouse_mode === RED.state.SELECTING_NODE) { @@ -1719,7 +1722,7 @@ RED.view = (function() { function canvasMouseUp() { lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor]; - if (RED.view.DEBUG) { + if (RED.view.DEBUG) { console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event }); } var i; @@ -3719,7 +3722,7 @@ RED.view = (function() { function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) } function linkMouseDown(d) { - if (RED.view.DEBUG) { + if (RED.view.DEBUG) { console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); } if (mouse_mode === RED.state.SELECTING_NODE) { From 64061c3440ca04552b59b4a0885b299e860883bd Mon Sep 17 00:00:00 2001 From: Mauricio Bonani Date: Thu, 9 Jun 2022 17:44:11 -0400 Subject: [PATCH 14/53] Fix selector placement --- .../node_modules/@node-red/editor-client/src/sass/panels.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss index 95d9210f4..e820992b4 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/panels.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/panels.scss @@ -87,8 +87,7 @@ display: inline-block; cursor: ew-resize; background-color: $primary-background; - } - + &:before { content: ''; display: block; @@ -104,4 +103,5 @@ mask-repeat: no-repeat; background-color: $grip-color; } + } } From 71a272f0a64c61cbbd3e180f39ad45a0568d8d9c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 9 Jun 2022 22:11:48 -0500 Subject: [PATCH 15/53] Fix ESM module loading in Function node Fixes #3627 --- .../@node-red/registry/lib/externalModules.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/registry/lib/externalModules.js b/packages/node_modules/@node-red/registry/lib/externalModules.js index ca1e4bfd8..b61392d9b 100644 --- a/packages/node_modules/@node-red/registry/lib/externalModules.js +++ b/packages/node_modules/@node-red/registry/lib/externalModules.js @@ -11,6 +11,7 @@ const exec = require("@node-red/util").exec; const log = require("@node-red/util").log; const hooks = require("@node-red/util").hooks; const url = require("url"); +const { createRequire } = require("module"); const BUILTIN_MODULES = require('module').builtinModules; @@ -139,10 +140,15 @@ function importModule(module) { } const externalModuleDir = getInstallDir(); const moduleDir = path.join(externalModuleDir,"node_modules",module); + // To handle both CJS and ESM we need to resolve the module to the + // specific file that is loaded when the module is required/imported + // As this won't be on the natural module search path, we use createRequire + // to access the module + const modulePath = createRequire(moduleDir).resolve(module) // Import needs the full path to the module's main .js file // It also needs to be a file:// url for Windows - const moduleFile = url.pathToFileURL(require.resolve(moduleDir)); - return import(moduleFile); + const moduleUrl = url.pathToFileURL(modulePath); + return import(moduleUrl); } function parseModuleName(module) { From 99d32c475838ea5fcb09810a6867b553025e3af9 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Fri, 10 Jun 2022 20:22:09 +0900 Subject: [PATCH 16/53] sanitize tab name --- .../@node-red/editor-client/src/js/ui/common/tabs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js index d471d206a..8901cf11f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js @@ -828,7 +828,7 @@ RED.tabs = (function() { } // link.attr("title",tab.label); - RED.popover.tooltip(link,function() { return tab.label}) + RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); }); if (options.onadd) { options.onadd(tab); From 30d88bbe7efcbf72519e18bbbca246de6a5e0484 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 11 Jun 2022 09:50:28 +0900 Subject: [PATCH 17/53] extend escaped subflow category characters --- .../node_modules/@node-red/editor-client/src/js/ui/palette.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js index 1e23d578a..9f20cc674 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js @@ -208,7 +208,7 @@ RED.palette = (function() { } function escapeCategory(category) { - return category.replace(/[ /.]/g,"_"); + return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_"); } function addNodeType(nt,def) { if (getPaletteNode(nt).length) { From 3e717862a4eed98547d4aa3468e116e8d1a91d2c Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sat, 11 Jun 2022 20:52:58 +0900 Subject: [PATCH 18/53] Fix z-index value for shade to cover nodes in palette --- .../node_modules/@node-red/editor-client/src/sass/base.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/base.scss b/packages/node_modules/@node-red/editor-client/src/sass/base.scss index c22a1d606..a08e752d4 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/base.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/base.scss @@ -44,7 +44,7 @@ body { #red-ui-palette-shade, #red-ui-editor-shade, #red-ui-header-shade, #red-ui-sidebar-shade { @include shade; - z-index: 2; + z-index: 5; } #red-ui-sidebar-shade { left: -8px; From 7eed9c0584c37eaac1dc0a73780389d30842075e Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sun, 12 Jun 2022 10:08:13 +0900 Subject: [PATCH 19/53] include junction to exported nodes --- .../node_modules/@node-red/editor-client/src/js/ui/clipboard.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js index 4906f68a1..f547203d4 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js @@ -709,6 +709,7 @@ RED.clipboard = (function() { } else if (type === 'flow') { var activeWorkspace = RED.workspaces.active(); nodes = RED.nodes.groups(activeWorkspace); + nodes = nodes.concat(RED.nodes.junctions(activeWorkspace)); nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace})); RED.nodes.eachConfig(function(n) { if (n.z === RED.workspaces.active() && n._def.hasUsers === false) { From 94471b6d07854ec1775cf6a2d1541ad3912b4ab1 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 13 Jun 2022 08:47:41 +0900 Subject: [PATCH 20/53] fix conversion of junction to subflow --- .../@node-red/editor-client/src/js/nodes.js | 36 +++++++++++++++++++ .../editor-client/src/js/ui/subflow.js | 16 ++++++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index de6d5a2d8..ab31e5038 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -738,6 +738,10 @@ RED.nodes = (function() { moveGroupToTab(node,z); return; } + if (node.type === "junction") { + moveJunctionToTab(node,z); + return; + } var oldZ = node.z; allNodes.moveNode(node,z); var nl = nodeLinks[node.id]; @@ -772,6 +776,38 @@ RED.nodes = (function() { RED.events.emit("groups:change",group); } + function moveJunctionToTab(junction, z) { + var index = junctionsByZ[junction.z].indexOf(junction); + junctionsByZ[junction.z].splice(junction,1); + junctionsByZ[z] = junctionsByZ[z] || []; + junctionsByZ[z].push(junction); + junction.z = z; + + var oldZ = junction.z; + var nl = nodeLinks[junction.id]; + if (nl) { + nl.in.forEach(function(l) { + var idx = linkTabMap[oldZ].indexOf(l); + if (idx != -1) { + linkTabMap[oldZ].splice(idx, 1); + } + if ((l.source.z === z) && linkTabMap[z]) { + linkTabMap[z].push(l); + } + }); + nl.out.forEach(function(l) { + var idx = linkTabMap[oldZ].indexOf(l); + if (idx != -1) { + linkTabMap[oldZ].splice(idx, 1); + } + if ((l.target.z === z) && linkTabMap[z]) { + linkTabMap[z].push(l); + } + }); + } + RED.events.emit("junctions:change",junction); + } + function removeLink(l) { var index = links.indexOf(l); if (index != -1) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index 58fca2cb4..3aeb7151f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -604,6 +604,14 @@ RED.subflow = (function() { return x; } + function nodeOrJunction(id) { + var node = RED.nodes.node(id); + if (node) { + return node; + } + return RED.nodes.junction(id); + } + function convertToSubflow() { var selection = RED.view.selection(); if (!selection.nodes) { @@ -792,14 +800,15 @@ RED.subflow = (function() { subflow.in.forEach(function(input) { input.wires.forEach(function(wire) { - var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) } + var link = {source: input, sourcePort: 0, target: nodeOrJunction(wire.id) } new_links.push(link); RED.nodes.addLink(link); }); }); + subflow.out.forEach(function(output,i) { output.wires.forEach(function(wire) { - var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output } + var link = {source: nodeOrJunction(wire.id), sourcePort: wire.port , target: output } new_links.push(link); RED.nodes.addLink(link); }); @@ -815,7 +824,7 @@ RED.subflow = (function() { n.links = n.links.filter(function(id) { var isLocalLink = nodes.hasOwnProperty(id); if (!isLocalLink) { - var otherNode = RED.nodes.node(id); + var otherNode = nodeOrJunction(id); if (otherNode && otherNode.links) { var i = otherNode.links.indexOf(n.id); if (i > -1) { @@ -831,7 +840,6 @@ RED.subflow = (function() { RED.nodes.moveNodeToTab(n, subflow.id); } - var historyEvent = { t:'createSubflow', nodes:[subflowInstance.id], From 6f6f67829be75fa41a081dc80093d69af7f587ed Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 13 Jun 2022 14:12:04 +0900 Subject: [PATCH 21/53] fix undoing junction to subflow --- .../@node-red/editor-client/src/js/history.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index f900276a0..b23071239 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -22,6 +22,14 @@ RED.history = (function() { var undoHistory = []; var redoHistory = []; + function nodeOrJunction(id) { + var node = RED.nodes.node(id); + if (node) { + return node; + } + return RED.nodes.junction(id); + } + function undoEvent(ev) { var i; var len; @@ -514,6 +522,7 @@ RED.history = (function() { var z = ev.activeWorkspace; var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id}); fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id)) + fullNodeList = fullNodeList.concat(RED.nodes.junctions(ev.subflow.subflow.id)) fullNodeList.forEach(function(n) { n.x += ev.subflow.offsetX; n.y += ev.subflow.offsetY; @@ -523,7 +532,7 @@ RED.history = (function() { }); inverseEv.subflows = []; for (i=0;i Date: Mon, 13 Jun 2022 21:01:34 +0100 Subject: [PATCH 22/53] Track mouse release outside workspace so current action completes --- .../@node-red/editor-client/src/js/ui/view.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 7d4d950f3..a3bc16e9d 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -230,6 +230,7 @@ RED.view = (function() { .on("mousedown", canvasMouseDown) .on("mouseup", canvasMouseUp) .on("mouseenter", function() { + d3.select(document).on('mouseup.red-ui-workspace-tracker', null) if (lasso) { if (d3.event.buttons !== 1) { lasso.remove(); @@ -245,6 +246,7 @@ RED.view = (function() { } } }) + .on("mouseleave", canvasMouseLeave) .on("touchend", function() { d3.event.preventDefault(); clearTimeout(touchStartTime); @@ -1719,7 +1721,14 @@ RED.view = (function() { redraw(); } } - + function canvasMouseLeave() { + if (mouse_mode !== 0 && d3.event.buttons !== 0) { + d3.select(document).on('mouseup.red-ui-workspace-tracker', function() { + d3.select(document).on('mouseup.red-ui-workspace-tracker', null) + canvasMouseUp.call(this) + }) + } + } function canvasMouseUp() { lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor]; if (RED.view.DEBUG) { From 2b91edeb7437be104afd7a740a4e0a54aaf127f2 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Tue, 14 Jun 2022 16:49:14 +0900 Subject: [PATCH 23/53] fix uncorrect fix of junction to subflow conversion --- .../node_modules/@node-red/editor-client/src/js/nodes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index ab31e5038..052fad558 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -778,12 +778,13 @@ RED.nodes = (function() { function moveJunctionToTab(junction, z) { var index = junctionsByZ[junction.z].indexOf(junction); - junctionsByZ[junction.z].splice(junction,1); + junctionsByZ[junction.z].splice(index,1); junctionsByZ[z] = junctionsByZ[z] || []; junctionsByZ[z].push(junction); - junction.z = z; var oldZ = junction.z; + junction.z = z; + var nl = nodeLinks[junction.id]; if (nl) { nl.in.forEach(function(l) { From 3ab93ecdd4845303a69146901aa3f84668da8a1d Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Thu, 16 Jun 2022 00:00:02 +0900 Subject: [PATCH 24/53] fix disable junction --- .../node_modules/@node-red/editor-client/src/js/ui/view.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index a15ec32f4..7b42c7e21 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -5736,7 +5736,12 @@ RED.view = (function() { node.dirty = true; node.dirtyStatus = true; node.changed = true; - RED.events.emit("nodes:change",node); + if (node.type === "junction") { + RED.events.emit("junctions:change",node); + } + else { + RED.events.emit("nodes:change",node); + } } } } From 8d99a42307c08cb7a3a3082e2e2b0bb75fd9d47d Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Thu, 16 Jun 2022 00:32:20 +0900 Subject: [PATCH 25/53] Add Japanese translations (Backport #3576 to v2.x) --- .../node_modules/@node-red/runtime/locales/ja/runtime.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/runtime/locales/ja/runtime.json b/packages/node_modules/@node-red/runtime/locales/ja/runtime.json index fdf8b2249..df5f9fa08 100644 --- a/packages/node_modules/@node-red/runtime/locales/ja/runtime.json +++ b/packages/node_modules/@node-red/runtime/locales/ja/runtime.json @@ -100,7 +100,9 @@ "error": "クレデンシャルの読み込みエラー: __message__", "error-saving": "クレデンシャルの保存エラー: __message__", "not-registered": "クレデンシャル '__type__' は登録されていません", - "system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n" + "system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n", + "unencrypted": "暗号化されていないクレデンシャルを使用", + "encryptedNotFound": "暗号化されたクレデンシャルが存在しません" }, "flows": { "safe-mode": "セーフモードでフローを停止しました。開始するためにはデプロイしてください", From d1312703c5d8d46269a8a4893cb8247f680259f7 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Thu, 16 Jun 2022 14:16:40 +0900 Subject: [PATCH 26/53] fix initial cursor position of init/finalize --- .../@node-red/nodes/core/function/10-function.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 6309ae9ad..156abd29d 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 @@ -460,7 +460,7 @@ } }); - var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs) { + var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs, offset) { var editor = RED.editor.createEditor({ id: id, mode: 'ace/mode/nrjavascript', @@ -484,14 +484,14 @@ extraLibs: extraLibs }); if (defaultValue && value === "") { - editor.moveCursorTo(defaultValue.split("\n").length - 1, 0); + editor.moveCursorTo(defaultValue.split("\n").length +offset, 0); } editor.__stateId = stateId; return editor; } - this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize")) - this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || []) - this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize")) + this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize"), undefined, 0); + this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || [], undefined, -1); + this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize"), undefined, 0); RED.library.create({ url:"functions", // where to get the data from From ba22b07dcea0bf5bdbd93e25e58632733597925e Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 16 Jun 2022 10:57:29 +0100 Subject: [PATCH 27/53] align functionality of `nodesDir` with coreNodesDir and userDir --- .../@node-red/registry/lib/localfilesystem.js | 195 +++++++++++++----- 1 file changed, 146 insertions(+), 49 deletions(-) diff --git a/packages/node_modules/@node-red/registry/lib/localfilesystem.js b/packages/node_modules/@node-red/registry/lib/localfilesystem.js index 62080b4a8..da4006ecc 100644 --- a/packages/node_modules/@node-red/registry/lib/localfilesystem.js +++ b/packages/node_modules/@node-red/registry/lib/localfilesystem.js @@ -88,9 +88,10 @@ function getLocalFile(file) { /** * Synchronously walks the directory looking for node files. * @param dir the directory to search + * @param skipValidNodeRedModules a flag to skip lading icons & files if the directory a valid node-red module * @return an array of fully-qualified paths to .js files */ -function getLocalNodeFiles(dir) { +function getLocalNodeFiles(dir, skipValidNodeRedModules) { dir = path.resolve(dir); var result = []; @@ -102,6 +103,14 @@ function getLocalNodeFiles(dir) { return {files: [], icons: []}; } files.sort(); + // when loading local files, if the path is a valid node-red module + // dont include it (will be picked up in scanTreeForNodesModules) + if(skipValidNodeRedModules && files.indexOf("package.json") >= 0) { + const package = getPackageDetails(dir) + if(package.isNodeRedModule) { + return {files: [], icons: []}; + } + } files.forEach(function(fn) { var stats = fs.statSync(path.join(dir,fn)); if (stats.isFile()) { @@ -114,7 +123,7 @@ function getLocalNodeFiles(dir) { } else if (stats.isDirectory()) { // Ignore /.dirs/, /lib/ /node_modules/ if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) { - var subDirResults = getLocalNodeFiles(path.join(dir,fn)); + var subDirResults = getLocalNodeFiles(path.join(dir,fn), skipValidNodeRedModules); result = result.concat(subDirResults.files); icons = icons.concat(subDirResults.icons); } else if (fn === "icons") { @@ -126,21 +135,30 @@ function getLocalNodeFiles(dir) { return {files: result, icons: icons} } -function scanDirForNodesModules(dir,moduleName) { - var results = []; - var scopeName; +function scanDirForNodesModules(dir,moduleName,package) { + let results = []; + let scopeName; + let files try { - var files = fs.readdirSync(dir); - if (moduleName) { - var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName); - if (m) { - scopeName = m[1]; - moduleName = m[2]; + let isNodeRedModule = false + if(package) { + dir = path.join(package.moduleDir,'..') + files = [path.basename(package.moduleDir)] + moduleName = (package.package ? package.package.name : null) || moduleName + isNodeRedModule = package.isNodeRedModule + } else { + files = fs.readdirSync(dir); + if (moduleName) { + var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName); + if (m) { + scopeName = m[1]; + moduleName = m[2]; + } } } - for (var i=0;i look for node_modules + 2. exist(package.json) && package.json.has(node-red) => load this only + 3. in original scan of nodesDir, ignore if:(exist(package.json) && package.json.has(node-red)) + */ + if (nodesDir) { + for (let dirIndex = 0; dirIndex < nodesDir.length; dirIndex++) { + const nodeDir = nodesDir[dirIndex]; + const packageDetails = getPackageDetails(nodeDir) + if(packageDetails.isNodeRedModule) { + //we have found a node-red module, scan it + const nrModules = scanDirForNodesModules(nodeDir, packageDetails.package.name, packageDetails); + results = results.concat(nrModules); + + } else if (packageDetails.has_node_modules) { + //If this dir has a `node_modues` dir, scan it + const nodeModulesDir = path.join(nodeDir, 'node_modules') + const nrModules = scanDirForNodesModules(nodeModulesDir, moduleName ); + results = results.concat(nrModules); + + } else { + //If this is not a node-red module AND it does NOT have a node_modules dir, + //it may be a directory of project directories or a node_modules dir? + //scan this instead + const nrModules = scanDirForNodesModules(nodeDir, moduleName); + results = results.concat(nrModules); + } } } return results; @@ -274,24 +328,26 @@ function getModuleNodeFiles(module) { } function getNodeFiles(disableNodePathScan) { - var dir; // Find all of the nodes to load - var nodeFiles = []; - var results; - - var dir; - var iconList = []; + let results; + let nodesDir; + if(settings.nodesDir) { + nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir] + } + let dir; + let nodeFiles = []; + let iconList = []; if (settings.coreNodesDir) { results = getLocalNodeFiles(path.resolve(settings.coreNodesDir)); nodeFiles = nodeFiles.concat(results.files); iconList = iconList.concat(results.icons); - var defaultLocalesPath = path.join(settings.coreNodesDir,"locales"); + let defaultLocalesPath = path.join(settings.coreNodesDir,"locales"); i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json"); } if (settings.userDir) { dir = path.join(settings.userDir,"lib","icons"); - var icons = scanIconDir(dir); + let icons = scanIconDir(dir); if (icons.length > 0) { iconList.push({path:dir,icons:icons}); } @@ -301,13 +357,9 @@ function getNodeFiles(disableNodePathScan) { nodeFiles = nodeFiles.concat(results.files); iconList = iconList.concat(results.icons); } - if (settings.nodesDir) { - dir = settings.nodesDir; - if (typeof settings.nodesDir == "string") { - dir = [dir]; - } - for (var i=0;i Date: Thu, 16 Jun 2022 11:00:31 +0100 Subject: [PATCH 28/53] =?UTF-8?q?improve=20tests=20for=20nodeDir=20Adds=20?= =?UTF-8?q?new=20resources=20(loose=20files,=20non=20NR=20pkgs,=20NR=20mod?= =?UTF-8?q?ules,=20NR=20Plugins)=20Adds=20new=20tests=20#getNodeFiles=20-?= =?UTF-8?q?=20new=20tests=20below=20=20=20=E2=88=9A=20Finds=20nodes=20and?= =?UTF-8?q?=20icons=20only=20in=20nodesDir=20with=20files,=20icons=20and?= =?UTF-8?q?=20valid=20node-red=20packages=20=20=20=E2=88=9A=20Should=20not?= =?UTF-8?q?=20find=20node-red=20node=20in=20nodesDir=20with=20files,=20ico?= =?UTF-8?q?ns=20and=20valid=20node-red=20packages=20=20=20=E2=88=9A=20Shou?= =?UTF-8?q?ld=20not=20find=20node-red=20node=20in=20nodesDir=20when=20regu?= =?UTF-8?q?lar=20package=20and=20valid=20node-red=20packages=20#getModuleF?= =?UTF-8?q?iles=20-=20new=20tests=20below=20=20=20=E2=88=9A=20gets=20a=20n?= =?UTF-8?q?odes=20module=20files=20=20=20=E2=88=9A=20Finds=20only=201=20no?= =?UTF-8?q?de-red=20node=20in=20nodesDir=20amongst=20legacy=20nodes=20and?= =?UTF-8?q?=20regular=20nodes=20=20=20=E2=88=9A=20Finds=20a=20node-red=20n?= =?UTF-8?q?ode=20in=20nodesDir=20with=20a=20sub=20dir=20containing=20valid?= =?UTF-8?q?=20node-red=20package=20=20=20=E2=88=9A=20Finds=202=20node-red?= =?UTF-8?q?=20node=20and=201=20plugin=20in=20nodesDir=20(in=20root=20of=20?= =?UTF-8?q?dir)=20=20=20=E2=88=9A=20Finds=202=20node-red=20node=20and=201?= =?UTF-8?q?=20plugin=20in=20nodesDir=20pointing=20to=20a=20node=5Fmodules?= =?UTF-8?q?=20dir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../registry/lib/localfilesystem_spec.js | 226 ++++++++++++++---- .../lib/resources/nodesDir1/icons/loose1.svg | 1 + .../lib/resources/nodesDir1/loose1.html | 5 + .../lib/resources/nodesDir1/loose1.js | 4 + .../nodesDir1/loose2/icons/loose2.svg | 1 + .../nodesDir1/loose2/icons/loose2b.svg | 1 + .../resources/nodesDir1/loose2/loose2.html | 5 + .../lib/resources/nodesDir1/loose2/loose2.js | 4 + .../node-red-node-testnode/icons/test.svg | 1 + .../nodesDir1/node-red-node-testnode/main.js | 4 + .../node-red-node-testnode/package.json | 19 ++ .../nodesDir1/regular_module/icons/test.svg | 1 + .../nodesDir1/regular_module/main.js | 4 + .../nodesDir1/regular_module/package.json | 14 ++ .../nodesDir2/@test/testnode/index.html | 5 + .../nodesDir2/@test/testnode/index.js | 4 + .../nodesDir2/@test/testnode/package.json | 20 ++ .../resources/nodesDir2/testnode2/index.html | 5 + .../resources/nodesDir2/testnode2/index.js | 4 + .../nodesDir2/testnode2/package.json | 20 ++ .../theme-plugin2/files/clientside.js | 3 + .../nodesDir2/theme-plugin2/files/plugin.js | 14 ++ .../nodesDir2/theme-plugin2/files/theme.css | 1 + .../nodesDir2/theme-plugin2/package.json | 24 ++ .../node_modules/@test/testnode/index.html | 5 + .../node_modules/@test/testnode/index.js | 4 + .../node_modules/@test/testnode/package.json | 20 ++ .../@test/theme-plugin3/files/clientside.js | 3 + .../@test/theme-plugin3/files/plugin.js | 14 ++ .../@test/theme-plugin3/files/theme.css | 1 + .../@test/theme-plugin3/package.json | 24 ++ .../node_modules/testnode3/index.html | 5 + .../nodesDir3/node_modules/testnode3/index.js | 4 + .../node_modules/testnode3/package.json | 20 ++ 34 files changed, 441 insertions(+), 49 deletions(-) create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/icons/loose1.svg create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2.svg create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2b.svg create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/icons/test.svg create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/main.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/icons/test.svg create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/main.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/clientside.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/plugin.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/theme.css create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/clientside.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/plugin.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/theme.css create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/package.json create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.html create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.js create mode 100644 test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/package.json diff --git a/test/unit/@node-red/registry/lib/localfilesystem_spec.js b/test/unit/@node-red/registry/lib/localfilesystem_spec.js index e84761657..3b1bb63f3 100644 --- a/test/unit/@node-red/registry/lib/localfilesystem_spec.js +++ b/test/unit/@node-red/registry/lib/localfilesystem_spec.js @@ -14,26 +14,41 @@ * limitations under the License. **/ -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); +const should = require("should"); +const sinon = require("sinon"); +const path = require("path"); -var NR_TEST_UTILS = require("nr-test-utils"); +const NR_TEST_UTILS = require("nr-test-utils"); -var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); +const localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); -var resourcesDir = path.resolve(path.join(__dirname,"resources","local")); -var userDir = path.resolve(path.join(__dirname,"resources","userDir")); -var moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule")); +const resourcesDir = path.resolve(path.join(__dirname,"resources","local")); +const userDir = path.resolve(path.join(__dirname,"resources","userDir")); -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; +const nodesDir1 = path.resolve(path.join(__dirname,"resources","nodesDir1")) +const nodesDir2 = path.resolve(path.join(__dirname,"resources","nodesDir2")) +const nodesDir3 =path.resolve(path.join(__dirname,"resources","nodesDir3")) + +const moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule")); + +const i18n = NR_TEST_UTILS.require("@node-red/util").i18n; describe("red/nodes/registry/localfilesystem",function() { + var stubs = []; + function stubPathJoin() { + var _join = path.join; + stubs.push(sinon.stub(path,"join").callsFake(function() { + if (arguments[0] == resourcesDir) { + // This stops the module tree scan from going any higher + // up the tree than resourcesDir. + return arguments[0]; + } + return _join.apply(null,arguments); + })); + } beforeEach(function() { stubs.push(sinon.stub(i18n,"registerMessageCatalog").callsFake(function() { return Promise.resolve(); })); }) - - var stubs = []; afterEach(function() { while(stubs.length) { stubs.pop().restore(); @@ -129,16 +144,76 @@ describe("red/nodes/registry/localfilesystem",function() { checkNodes(nm.nodes,['TestNode5'],['TestNode1']); done(); }); + it("Finds nodes and icons only in nodesDir with files, icons and valid node-red packages",function(done) { + localfilesystem.init({nodesDir:nodesDir1}); + const nodeList = localfilesystem.getNodeFiles(true); + nodeList.should.have.a.property("node-red"); + const nm = nodeList['node-red']; + nm.should.have.a.property('name','node-red'); + nm.should.have.a.property("nodes"); + nm.should.have.a.property("icons"); + checkNodes(nm.nodes,['loose1', 'loose2'], []); + //1 icon in nodesDir1/icons/ - should be found + //2 icons in nodesDir1/loose2/icons/ - should be found + //1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found + //1 icons in nodesDir1/regular_module/icons/ - should NOT be found + //total icon sets 3, total icons 4 + nm.icons.should.have.a.property("length", 3); + nm.icons[0].should.have.a.property("path") + nm.icons[0].should.have.a.property("icons", ['loose1.svg']) + nm.icons[1].should.have.a.property("path") + nm.icons[1].should.have.a.property("icons", ['loose2.svg', 'loose2b.svg']) + nm.icons[2].should.have.a.property("path") + nm.icons[2].should.have.a.property("icons", ['test.svg']) + done(); + }); + it("Should not find node-red node in nodesDir with files, icons and valid node-red packages",function(done) { + // path contains a regular node module and a node-red node module + localfilesystem.init({nodesDir:path.join(nodesDir1)}); + const nodeList = localfilesystem.getNodeFiles(true); + nodeList.should.have.a.property("node-red"); + const nm = nodeList['node-red']; + nm.should.have.a.property('name','node-red'); + nm.should.have.a.property("nodes"); + nm.nodes.should.have.a.property("loose1"); + nm.nodes.should.have.a.property("loose2"); + nm.nodes.should.not.have.a.property("regular_module"); + nm.nodes.should.not.have.a.property("node-red-node-testnode"); + for (let key of Object.keys(nm.nodes)) { + const n = nm.nodes[key]; + n.file.indexOf("regular_module").should.eql(-1, `found icons in a node-red module`) + n.file.indexOf("node-red-node-testnode").should.eql(-1, `found icons in a node-red module`) + } + //1 icon in nodesDir1/icons/ - should be found + //2 icons in nodesDir1/loose2/icons/ - should be found + //1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found + //1 icons in nodesDir1/regular_module/icons/ - should NOT be found + //total icon sets 3, total icons 4 + nm.should.have.a.property("icons"); + nm.icons.should.have.a.property("length", 3); + let iconCount = 0; + for (let index = 0; index < nm.icons.length; index++) { + const iconDir = nm.icons[index]; + iconCount += iconDir.icons.length + iconDir.path.indexOf("node-red-node-testnode").should.eql(-1, `should not find icons in a node-red module`) + } + should(iconCount).eql(4, "Should find only 4 icons") + done(); + }); + it("Should not find node-red node in nodesDir when regular package and valid node-red packages",function(done) { + localfilesystem.init({nodesDir:path.join(nodesDir1,"regular_module")}); + const nodeList = localfilesystem.getNodeFiles(true); + nodeList.should.have.a.property("node-red"); + const nm = nodeList['node-red']; + nm.should.have.a.property('name','node-red'); + nm.should.have.a.property("nodes", {}); + nm.should.have.a.property("icons"); + nm.icons.should.have.a.property("length", 1); //should find 1 icons folder + nm.icons[0].should.have.a.property("icons", [ 'test.svg' ]); //should find 1 icon in regular package + done(); + }); it("Finds nodes module path",function(done) { - var _join = path.join; - stubs.push(sinon.stub(path,"join").callsFake(function() { - if (arguments[0] == resourcesDir) { - // This stops the module tree scan from going any higher - // up the tree than resourcesDir. - return arguments[0]; - } - return _join.apply(null,arguments); - })); + stubPathJoin() localfilesystem.init({coreNodesDir:moduleDir}); var nodeList = localfilesystem.getNodeFiles(); nodeList.should.have.a.property("node-red"); @@ -166,8 +241,6 @@ describe("red/nodes/registry/localfilesystem",function() { i18n.registerMessageCatalog.lastCall.args[1].should.eql(path.resolve(path.join(moduleDir,"locales"))); i18n.registerMessageCatalog.lastCall.args[2].should.eql('messages.json'); - - done(); }); it.skip("finds locales directory"); @@ -205,15 +278,7 @@ describe("red/nodes/registry/localfilesystem",function() { }); describe("#getModuleFiles",function() { it("gets a nodes module files",function(done) { - var _join = path.join; - stubs.push(sinon.stub(path,"join").callsFake(function() { - if (arguments[0] == resourcesDir) { - // This stops the module tree scan from going any higher - // up the tree than resourcesDir. - return arguments[0]; - } - return _join.apply(null,arguments); - })); + stubPathJoin() localfilesystem.init({coreNodesDir:moduleDir}); var nodeModule = localfilesystem.getModuleFiles('TestNodeModule'); nodeModule.should.have.a.property('TestNodeModule'); @@ -230,16 +295,87 @@ describe("red/nodes/registry/localfilesystem",function() { done(); }); + it("Finds only 1 node-red node in nodesDir amongst legacy nodes and regular nodes",function(done) { + stubPathJoin() + localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]}); + const nodeModule = localfilesystem.getModuleFiles(); + const loaded = Object.keys(nodeModule) + loaded.should.have.a.property("length", 1) + loaded.indexOf('node-red-node-testnode').should.greaterThan(-1, "Should load node-red-node-testnode") + + nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode'); + nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0'); + nodeModule['node-red-node-testnode'].should.have.a.property('nodes'); + nodeModule['node-red-node-testnode'].should.have.a.property('path'); + nodeModule['node-red-node-testnode'].should.have.a.property('user', false); + checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode'); + done(); + }); + it("Finds a node-red node in nodesDir with a sub dir containing valid node-red package",function(done) { + stubPathJoin() + localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]}); + const nodeModule = localfilesystem.getModuleFiles(); + const loaded = Object.keys(nodeModule) + nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode'); + nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0'); + nodeModule['node-red-node-testnode'].should.have.a.property('nodes'); + nodeModule['node-red-node-testnode'].should.have.a.property('path'); + nodeModule['node-red-node-testnode'].should.have.a.property('user', false); + checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode'); + done(); + }); + it("Finds 2 node-red modules and 1 plugin in nodesDir (in root of dir)",function(done) { + stubPathJoin() + localfilesystem.init({nodesDir:[nodesDir2]}); + const nodeModule = localfilesystem.getModuleFiles(); + const loaded = Object.keys(nodeModule) + loaded.should.have.a.property("length", 3) + loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode") + loaded.indexOf('testnode2').should.greaterThan(-1, "Should load testnode2") + loaded.indexOf('test-theme2').should.greaterThan(-1, "Should load test-theme2") + + nodeModule['@test/testnode'].should.have.a.property('name','@test/testnode'); + nodeModule['@test/testnode'].should.have.a.property('version','1.0.0'); + nodeModule['@test/testnode'].should.have.a.property('nodes'); + nodeModule['@test/testnode'].should.have.a.property('path'); + nodeModule['@test/testnode'].should.have.a.property('user', false); + + nodeModule['testnode2'].should.have.a.property('name','testnode2'); + nodeModule['testnode2'].should.have.a.property('version','1.0.0'); + nodeModule['testnode2'].should.have.a.property('nodes'); + nodeModule['testnode2'].should.have.a.property('path'); + nodeModule['testnode2'].should.have.a.property('user', false); + + nodeModule['test-theme2'].should.have.a.property('name','test-theme2'); + + nodeModule['test-theme2'].should.have.a.property('version','0.0.1'); + nodeModule['test-theme2'].should.have.a.property('nodes', {}); + nodeModule['test-theme2'].should.have.a.property('path'); + nodeModule['test-theme2'].should.have.a.property('user', false); + nodeModule['test-theme2'].should.have.a.property('plugins'); + nodeModule['test-theme2'].plugins.should.have.a.property('test-theme2'); + nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('name','test-theme2'); + nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('module','test-theme2'); + nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('version', '0.0.1'); + nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('file'); + nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('local', false); + + done(); + }); + it("Finds 2 node-red modules and 1 plugin in nodesDir pointing to a node_modules dir",function(done) { + stubPathJoin() + localfilesystem.init({nodesDir:[path.join(nodesDir3, "node_modules")]}); + const nodeModule = localfilesystem.getModuleFiles(); + const loaded = Object.keys(nodeModule) + loaded.should.have.a.property("length", 3) + + loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode") + loaded.indexOf('@test/test-theme3').should.greaterThan(-1, "Should load test-theme3") + loaded.indexOf('testnode3').should.greaterThan(-1, "Should load testnode3") + done(); + }); it("throws an error if a node isn't found",function(done) { - var _join = path.join; - stubs.push(sinon.stub(path,"join").callsFake(function() { - if (arguments[0] == resourcesDir) { - // This stops the module tree scan from going any higher - // up the tree than resourcesDir. - return arguments[0]; - } - return _join.apply(null,arguments); - })); + stubPathJoin() localfilesystem.init({coreNodesDir:moduleDir}); /*jshint immed: false */ (function(){ @@ -250,15 +386,7 @@ describe("red/nodes/registry/localfilesystem",function() { it.skip("finds locales directory"); it.skip("finds icon path directory"); it("scans icon files with a module file",function(done) { - var _join = path.join; - stubs.push(sinon.stub(path,"join").callsFake(function() { - if (arguments[0] == resourcesDir) { - // This stops the module tree scan from going any higher - // up the tree than resourcesDir. - return arguments[0]; - } - return _join.apply(null,arguments); - })); + stubPathJoin() localfilesystem.init({ coreNodesDir: moduleDir }); diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/icons/loose1.svg b/test/unit/@node-red/registry/lib/resources/nodesDir1/icons/loose1.svg new file mode 100644 index 000000000..927680ee3 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/icons/loose1.svg @@ -0,0 +1 @@ + diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.html b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.html new file mode 100644 index 000000000..8f392056c --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.js b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.js new file mode 100644 index 000000000..624cfe6d8 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose1.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from loose1.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2.svg b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2.svg new file mode 100644 index 000000000..04bebc370 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2.svg @@ -0,0 +1 @@ + diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2b.svg b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2b.svg new file mode 100644 index 000000000..250348861 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/icons/loose2b.svg @@ -0,0 +1 @@ + diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.html b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.html new file mode 100644 index 000000000..e23e2880a --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.js b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.js new file mode 100644 index 000000000..e1cde2a82 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/loose2/loose2.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from loose2.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/icons/test.svg b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/icons/test.svg new file mode 100644 index 000000000..04bebc370 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/icons/test.svg @@ -0,0 +1 @@ + diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/main.js b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/main.js new file mode 100644 index 000000000..e2e0781d1 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/main.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from regular module main.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/package.json new file mode 100644 index 000000000..26d4151fb --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/node-red-node-testnode/package.json @@ -0,0 +1,19 @@ +{ + "name": "node-red-node-testnode", + "version": "1.0.0", + "description": "A node-red node that does nothing other than exist", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "node-red" + ], + "node-red": { + "nodes": { + "testnode": "index.js" + } + }, + "author": "@testyMcTersterson", + "license": "MIT" +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/icons/test.svg b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/icons/test.svg new file mode 100644 index 000000000..04bebc370 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/icons/test.svg @@ -0,0 +1 @@ + diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/main.js b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/main.js new file mode 100644 index 000000000..e2e0781d1 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/main.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from regular module main.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/package.json new file mode 100644 index 000000000..23cfd7e86 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir1/regular_module/package.json @@ -0,0 +1,14 @@ +{ + "name": "regular_node", + "version": "1.0.0", + "description": "A regular node that does nothing other than exist", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "test" + ], + "author": "@testyMcTersterson", + "license": "MIT" +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.html b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.html new file mode 100644 index 000000000..0e45b3007 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.js new file mode 100644 index 000000000..1fe9d89a3 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/index.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from @test/testnode index.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/package.json new file mode 100644 index 000000000..a32462631 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@test/testnode/package.json @@ -0,0 +1,20 @@ +{ + "name": "@test/testnode", + "version": "1.0.0", + "description": "A test node that does nothing other than exist", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "node-red", + "test" + ], + "node-red": { + "nodes": { + "testnode": "index.js" + } + }, + "author": "@testyMcTersterson", + "license": "MIT" +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.html b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.html new file mode 100644 index 000000000..5d9f2b0ec --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.js new file mode 100644 index 000000000..0f0eba392 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/index.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from testnode2 index.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/package.json new file mode 100644 index 000000000..f9e8aecbe --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/testnode2/package.json @@ -0,0 +1,20 @@ +{ + "name": "testnode2", + "version": "1.0.0", + "description": "A test node that does nothing other than exist", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "node-red", + "test" + ], + "node-red": { + "nodes": { + "testnode2": "index.js" + } + }, + "author": "@testyMcTersterson", + "license": "MIT" +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/clientside.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/clientside.js new file mode 100644 index 000000000..fb2e22289 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/clientside.js @@ -0,0 +1,3 @@ +(function() { + console.log("Hi from test plugin client side") +})() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/plugin.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/plugin.js new file mode 100644 index 000000000..3202aeb8b --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/plugin.js @@ -0,0 +1,14 @@ +module.exports = function (RED) { + RED.plugins.registerPlugin('test-theme', { + type: 'node-red-theme', + scripts: [ + 'files/clientside.js' + ], + css: [ + 'files/theme.css', + ], + monacoOptions: { + theme: "vs" + } + }) +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/theme.css b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/theme.css new file mode 100644 index 000000000..872c93e10 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/files/theme.css @@ -0,0 +1 @@ +:root{--red-ui-primary-background: #f2f3fb;} \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/package.json new file mode 100644 index 000000000..b8608c175 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/theme-plugin2/package.json @@ -0,0 +1,24 @@ +{ + "name": "test-theme2", + "version": "0.0.1", + "description": "test theme for Node-RED", + + "keywords": [ + "node-red", + "plugin", + "theme" + ], + "author": { + "name": "testy-McTesterson" + }, + "license": "MIT", + "node-red": { + "version": ">=2.2.0", + "plugins": { + "test-theme2": "files/plugin.js" + } + }, + "engines": { + "node": ">=12.x" + } +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.html b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.html new file mode 100644 index 000000000..0e45b3007 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.js b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.js new file mode 100644 index 000000000..1fe9d89a3 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/index.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from @test/testnode index.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/package.json new file mode 100644 index 000000000..a32462631 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/testnode/package.json @@ -0,0 +1,20 @@ +{ + "name": "@test/testnode", + "version": "1.0.0", + "description": "A test node that does nothing other than exist", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "node-red", + "test" + ], + "node-red": { + "nodes": { + "testnode": "index.js" + } + }, + "author": "@testyMcTersterson", + "license": "MIT" +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/clientside.js b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/clientside.js new file mode 100644 index 000000000..fb2e22289 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/clientside.js @@ -0,0 +1,3 @@ +(function() { + console.log("Hi from test plugin client side") +})() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/plugin.js b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/plugin.js new file mode 100644 index 000000000..3202aeb8b --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/plugin.js @@ -0,0 +1,14 @@ +module.exports = function (RED) { + RED.plugins.registerPlugin('test-theme', { + type: 'node-red-theme', + scripts: [ + 'files/clientside.js' + ], + css: [ + 'files/theme.css', + ], + monacoOptions: { + theme: "vs" + } + }) +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/theme.css b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/theme.css new file mode 100644 index 000000000..872c93e10 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/files/theme.css @@ -0,0 +1 @@ +:root{--red-ui-primary-background: #f2f3fb;} \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/package.json new file mode 100644 index 000000000..56b1bfb6d --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/@test/theme-plugin3/package.json @@ -0,0 +1,24 @@ +{ + "name": "@test/test-theme3", + "version": "0.0.1", + "description": "test theme for Node-RED", + + "keywords": [ + "node-red", + "plugin", + "theme" + ], + "author": { + "name": "testy-McTesterson" + }, + "license": "MIT", + "node-red": { + "version": ">=2.2.0", + "plugins": { + "test-theme3": "files/plugin.js" + } + }, + "engines": { + "node": ">=12.x" + } +} diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.html b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.html new file mode 100644 index 000000000..a4034f340 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.js b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.js new file mode 100644 index 000000000..855768ad8 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/index.js @@ -0,0 +1,4 @@ + + (function() { + console.log("hello from testnode3 index.js") + })() diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/package.json new file mode 100644 index 000000000..41e9bb8f8 --- /dev/null +++ b/test/unit/@node-red/registry/lib/resources/nodesDir3/node_modules/testnode3/package.json @@ -0,0 +1,20 @@ +{ + "name": "testnode3", + "version": "1.0.0", + "description": "A test node that does nothing other than exist", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "node-red", + "test" + ], + "node-red": { + "nodes": { + "testnode3": "index.js" + } + }, + "author": "@testyMcTersterson", + "license": "MIT" +} From 62a2a4a9f5b90b660d790eaf626ac20d16e91db4 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 16 Jun 2022 11:38:19 +0100 Subject: [PATCH 29/53] Further simplify file node filename entry UX (v3) fixes #3668 --- .../@node-red/nodes/core/storage/10-file.html | 16 ++++++++-------- .../@node-red/nodes/locales/en-US/messages.json | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.html b/packages/node_modules/@node-red/nodes/core/storage/10-file.html index b726b537d..6b19ebaa8 100755 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/core/storage/10-file.html @@ -198,8 +198,8 @@ category: 'storage', defaults: { name: {value:""}, - filename: {value:"filename"}, - filenameType: {value:"msg"}, + filename: {value:""}, + filenameType: {value:"str"}, appendNewline: {value:true}, createDir: {value:false}, overwriteFile: {value:"false"}, @@ -236,8 +236,8 @@ label: node._("file.encoding.setbymsg") }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ - default: "msg", - types: ["str", "msg", "jsonata", "env"], + default: "str", + types: [{label:RED._("node-red:file.label.path"), value:"str", icon:""}, "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { @@ -297,8 +297,8 @@ category: 'storage', defaults: { name: {value:""}, - filename: {value:"filename"}, - filenameType: {value:"msg"}, + filename: {value:""}, + filenameType: {value:"str"}, format: {value:"utf8"}, chunk: {value:false}, sendError: {value: false}, @@ -341,8 +341,8 @@ label: label }).text(label).appendTo(encSel); $("#node-input-filename").typedInput({ - default: "msg", - types: ["str", "msg", "jsonata", "env"], + default: "str", + types: [{label:RED._("node-red:file.label.path"), value:"str", icon:""}, "msg", "jsonata", "env"], typeField: $("#node-input-filenameType") }); if(typeof node.filenameType == 'undefined') { diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index 77d832abd..62d5f351f 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -928,6 +928,7 @@ "write": "write file", "read": "read file", "filename": "Filename", + "path": "path", "action": "Action", "addnewline": "Add newline (\\n) to each payload?", "createdir": "Create directory if it doesn't exist?", From ea469470541de61bcb13dc8dc4a120a9c6905673 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 13:48:36 +0100 Subject: [PATCH 30/53] Add RED.actions.getLabel to retrieve action i18n label --- .../editor-client/src/js/ui/actions.js | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/actions.js b/packages/node_modules/@node-red/editor-client/src/js/ui/actions.js index 2273ae9ab..5bd9ea034 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/actions.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/actions.js @@ -21,6 +21,34 @@ RED.actions = (function() { function getAction(name) { return actions[name].handler; } + function getActionLabel(name) { + let def = actions[name] + if (!def) { + return '' + } + if (!def.label) { + var options = def.options; + var key = options ? options.label : undefined; + if (!key) { + key = "action-list." +name.replace(/^.*:/,""); + } + var label = RED._(key); + if (label === key) { + // no translation. convert `name` to description + label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() { + if (arguments[5] === 0) { + return arguments[2].toUpperCase(); + } else { + return " "+arguments[4].toUpperCase(); + } + }); + } + def.label = label; + } + return def.label + } + + function invokeAction() { var args = Array.prototype.slice.call(arguments); var name = args.shift(); @@ -31,7 +59,7 @@ RED.actions = (function() { } function listActions() { var result = []; - var missing = []; + Object.keys(actions).forEach(function(action) { var def = actions[action]; var shortcut = RED.keyboard.getShortcut(action); @@ -42,28 +70,8 @@ RED.actions = (function() { isUser = !!RED.keyboard.getUserShortcut(action); } if (!def.label) { - var name = action; - var options = def.options; - var key = options ? options.label : undefined; - if (!key) { - key = "action-list." +name.replace(/^.*:/,""); - } - var label = RED._(key); - if (label === key) { - // no translation. convert `name` to description - label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() { - if (arguments[5] === 0) { - return arguments[2].toUpperCase(); - } else { - return " "+arguments[4].toUpperCase(); - } - }); - missing.push(key); - } - def.label = label; + def.label = getActionLabel(action) } - //console.log("; missing:", missing); - result.push({ id:action, scope:shortcut?shortcut.scope:undefined, @@ -79,6 +87,7 @@ RED.actions = (function() { add: addAction, remove: removeAction, get: getAction, + getLabel: getActionLabel, invoke: invokeAction, list: listActions } From 6bea3dabbb4a999a4c3fb77926ec0b0b154664b6 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 13:49:25 +0100 Subject: [PATCH 31/53] Allow popover position to be set via absolute pos rather than relative --- .../@node-red/editor-client/src/js/ui/common/popover.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 f1728ed83..9ddd3d866 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 @@ -610,10 +610,13 @@ RED.popover = (function() { var target = options.target; var align = options.align || "right"; var offset = options.offset || [0,0]; + var xPos = options.x; + var yPos = options.y; + var isAbsolutePosition = (xPos !== undefined && yPos !== undefined) - var pos = target.offset(); - var targetWidth = target.width(); - var targetHeight = target.outerHeight(); + var pos = isAbsolutePosition?{left:xPos, top: yPos}:target.offset(); + var targetWidth = isAbsolutePosition?0:target.width(); + var targetHeight = isAbsolutePosition?0:target.outerHeight(); var panelHeight = panel.height(); var panelWidth = panel.width(); From cce4f6f7f764675d6c9eecd611fde310f657f054 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 13:50:05 +0100 Subject: [PATCH 32/53] Add onpre/postselect, direction opts to menu and make id optional --- .../editor-client/src/js/ui/common/menu.js | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js index 417189b33..6327f5268 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js @@ -16,6 +16,7 @@ RED.menu = (function() { var menuItems = {}; + let menuItemCount = 0 function createMenuItem(opt) { var item; @@ -59,15 +60,16 @@ RED.menu = (function() { item = $('
  • '); } else { item = $('
  • '); - + if (!opt.id) { + opt.id = 'red-ui-menu-item-'+(menuItemCount++) + } if (opt.group) { item.addClass("red-ui-menu-group-"+opt.group); - } var linkContent = ''; if (opt.toggle) { - linkContent += ''; - linkContent += ''; + linkContent += ''; + linkContent += ''; } if (opt.icon !== undefined) { @@ -77,12 +79,15 @@ RED.menu = (function() { linkContent += ' '; } } - + let label = opt.label + if (!opt.label && typeof opt.onselect === 'string') { + label = RED.actions.getLabel(opt.onselect) + } if (opt.sublabel) { - linkContent += ''+opt.label+''+ + linkContent += ''+label+''+ ''+opt.sublabel+'' } else { - linkContent += ''+opt.label+'' + linkContent += ''+label+'' } linkContent += ''; @@ -126,10 +131,21 @@ RED.menu = (function() { }); } if (opt.options) { - item.addClass("red-ui-menu-dropdown-submenu pull-left"); + item.addClass("red-ui-menu-dropdown-submenu"+(opt.direction!=='right'?" pull-left":"")); var submenu = $('
      ').appendTo(item); for (var i=0;i",{class:"red-ui-menu red-ui-menu-dropdown pull-right"}); - + if (options.direction) { + topMenu.addClass("red-ui-menu-dropdown-direction-"+options.direction) + } if (options.id) { topMenu.attr({id:options.id+"-submenu"}); var menuParent = $("#"+options.id); @@ -175,6 +193,15 @@ RED.menu = (function() { var lastAddedSeparator = false; for (var i=0;i Date: Thu, 16 Jun 2022 13:57:50 +0100 Subject: [PATCH 33/53] Allow typeSearch to list actions A proof-of-concept which needs a bit more UI polish, but capturing the current code for the future. We do not add actions to the list, so the code is unused. --- .../editor-client/src/js/ui/typeSearch.js | 33 +++++++++++++++---- .../editor-client/src/js/ui/utils.js | 2 ++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js index 0fc633071..fc5b8e99e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js @@ -104,7 +104,9 @@ RED.typeSearch = (function() { var index = Math.max(0,selected); if (index < children.length) { var n = $(children[index]).find(".red-ui-editableList-item-content").data('data'); - typesUsed[n.type] = Date.now(); + if (!/^_action_:/.test(n.type)) { + typesUsed[n.type] = Date.now(); + } if (n.def.outputs === 0) { confirm(n); } else { @@ -173,6 +175,8 @@ RED.typeSearch = (function() { var nodeDiv = $('
      ',{class:"red-ui-search-result-node"}).appendTo(div); if (object.type === "junction") { nodeDiv.addClass("red-ui-palette-icon-junction"); + } else if (/^_action_:/.test(object.type)) { + nodeDiv.addClass("red-ui-palette-icon-junction") } else { var colour = RED.utils.getNodeColor(object.type,def); nodeDiv.css('backgroundColor',colour); @@ -182,11 +186,14 @@ RED.typeSearch = (function() { var iconContainer = $('
      ',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); RED.utils.createIconElement(icon_url, iconContainer, false); - if (object.type !== "junction" && def.inputs > 0) { - $('
      ',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv); - } - if (object.type !== "junction" && def.outputs > 0) { - $('
      ',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv); + + if (!/^_action_:/.test(object.type) && object.type !== "junction") { + if (def.inputs > 0) { + $('
      ',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv); + } + if (def.outputs > 0) { + $('
      ',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv); + } } var contentDiv = $('
      ',{class:"red-ui-search-result-description"}).appendTo(div); @@ -207,7 +214,9 @@ RED.typeSearch = (function() { } function confirm(def) { hide(); - typesUsed[def.type] = Date.now(); + if (!/^_action_:/.test(def.type)) { + typesUsed[def.type] = Date.now(); + } addCallback(def.type); } @@ -316,6 +325,7 @@ RED.typeSearch = (function() { function applyFilter(filter,type,def) { return !filter || ( + (!filter.spliceMultiple) && (!filter.type || type === filter.type) && (!filter.input || type === 'junction' || def.inputs > 0) && (!filter.output || type === 'junction' || def.outputs > 0) @@ -330,6 +340,13 @@ RED.typeSearch = (function() { 'inject','debug','function','change','switch','junction' ].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); }); + // if (opts.filter && opts.filter.input && opts.filter.output && !opts.filter.type) { + // if (opts.filter.spliceMultiple) { + // common.push('_action_:core:split-wires-with-junctions') + // } + // common.push('_action_:core:split-wire-with-link-nodes') + // } + var recentlyUsed = Object.keys(typesUsed); recentlyUsed.sort(function(a,b) { return typesUsed[b]-typesUsed[a]; @@ -354,6 +371,8 @@ RED.typeSearch = (function() { var itemDef = RED.nodes.getType(common[i]); if (common[i] === 'junction') { itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'} + } else if (/^_action_:/.test(common[i]) ) { + itemDef = { inputs:1, outputs: 1, label: common[i], type: common[i]} } if (itemDef) { item = { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index c3ea0ddd1..2c4cdca6b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -1032,6 +1032,8 @@ RED.utils = (function() { return "font-awesome/fa-circle-o" } else if (def.category === 'config') { return RED.settings.apiRootUrl+"icons/node-red/cog.svg" + } else if ((node && /^_action_:/.test(node.type)) || /^_action_:/.test(def.type)) { + return "font-awesome/fa-cogs" } else if (node && node.type === 'tab') { return "red-ui-icons/red-ui-icons-flow" // return RED.settings.apiRootUrl+"images/subflow_tab.svg" From 0eba4bdd61e196b446981b786d30edb5ccbd3665 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 13:59:14 +0100 Subject: [PATCH 34/53] Add right-click context menu to workspace --- Gruntfile.js | 1 + .../editor-client/src/js/ui/contextMenu.js | 175 +++++++++++ .../editor-client/src/js/ui/view-tools.js | 155 +++++++++- .../@node-red/editor-client/src/js/ui/view.js | 272 ++++++++++-------- .../editor-client/src/sass/dropdownMenu.scss | 35 ++- 5 files changed, 505 insertions(+), 133 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js diff --git a/Gruntfile.js b/Gruntfile.js index 979b38051..a168d7ce4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -192,6 +192,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/library.js", "packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js", "packages/node_modules/@node-red/editor-client/src/js/ui/search.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js", "packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js", "packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js", "packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js new file mode 100644 index 000000000..1a0dee2bb --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -0,0 +1,175 @@ +RED.contextMenu = (function() { + + let menu; + function createMenu() { + // menu = RED.popover.menu({ + // options: [ + // { + // label: 'delete selection', + // onselect: function() { + // RED.actions.invoke('core:delete-selection') + // RED.view.focus() + // } + // }, + // { label: 'world' } + // ], + // width: 200, + // }) + + + + + } + + function disposeMenu() { + $(document).off("mousedown.red-ui-workspace-context-menu"); + if (menu) { + menu.remove(); + } + menu = null; + } + function show(options) { + if (menu) { + menu.remove() + } + + const selection = RED.view.selection() + const hasSelection = (selection.nodes && selection.nodes.length > 0); + const hasMultipleSelection = hasSelection && selection.nodes.length > 1; + const hasLinks = selection.links && selection.links.length > 0; + const isSingleLink = !hasSelection && hasLinks && selection.links.length === 1 + const isMultipleLinks = !hasSelection && hasLinks && selection.links.length > 1 + const canDelete = hasSelection || hasLinks + const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group' + + const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g + + + const menuItems = [ + { onselect: 'core:show-action-list', onpostselect: function() {} }, + { + label: 'Insert', + options: [ + { + label: 'Node', + onselect: function() { + RED.view.showQuickAddDialog({ + position: [ options.x - offset.left, options.y - offset.top ], + touchTrigger: true, + splice: isSingleLink?selection.links[0]:undefined, + // spliceMultiple: isMultipleLinks + }) + } + }, + { + label: 'Junction', + onselect: 'core:split-wires-with-junctions', + disabled: hasSelection || !hasLinks + }, + { + label: 'Link Nodes', + onselect: 'core:split-wire-with-link-nodes', + disabled: hasSelection || !hasLinks + } + ] + + + + } + ] + // menuItems.push( + // { + // label: (isSingleLink || isMultipleLinks)?'Insert into wire...':'Add node...', + // onselect: function() { + // RED.view.showQuickAddDialog({ + // position: [ options.x - offset.left, options.y - offset.top ], + // touchTrigger: true, + // splice: isSingleLink?selection.links[0]:undefined, + // spliceMultiple: isMultipleLinks + // }) + // } + // }, + // ) + // if (hasLinks && !hasSelection) { + // menuItems.push({ onselect: 'core:split-wires-with-junctions', label: 'Insert junction'}) + // } + menuItems.push( + null, + { onselect: 'core:undo', disabled: RED.history.list().length === 0 }, + { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 }, + null, + { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection}, + { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection }, + { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() }, + { onselect: 'core:delete-selection', disabled: !canDelete }, + { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }, + { onselect: 'core:select-all-nodes' }, + ) + + if (hasSelection) { + menuItems.push( + null, + isGroup + ? { onselect: 'core:ungroup-selection', disabled: !isGroup } + : { onselect: 'core:group-selection', disabled: !hasSelection } + ) + if (canRemoveFromGroup) { + menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }) + } + + } + const offset = $("#red-ui-workspace-chart").offset() + menu = RED.menu.init({ + direction: 'right', + onpreselect: function() { + disposeMenu() + }, + onpostselect: function() { + RED.view.focus() + }, + options: menuItems + }); + + menu.attr("id","red-ui-workspace-context-menu"); + menu.css({ + position: "absolute" + }) + menu.appendTo("body"); + + // TODO: prevent the menu from overflowing the window. + + var top = options.y + var left = options.x + + if (top+menu.height()-$(document).scrollTop() > $(window).height()) { + top -= (top+menu.height())-$(window).height() + 22; + } + if (left+menu.width()-$(document).scrollLeft() > $(window).width()) { + left -= (left+menu.width())-$(window).width() + 18; + } + menu.css({ + top: top+"px", + left: left+"px" + }) + $(".red-ui-menu.red-ui-menu-dropdown").hide(); + $(document).on("mousedown.red-ui-workspace-context-menu", function(evt) { + if (menu && menu[0].contains(evt.target)) { + return + } + disposeMenu() + }); + menu.show(); + + // menu.show({ + // target: $('#red-ui-main-container'), + // x: options.x, + // y: options.y + // }) + + + } + + return { + show: show + } +})() diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 699e5f222..888fb4d7f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -336,17 +336,17 @@ RED.view.tools = (function() { } - function addNode() { - var selection = RED.view.selection(); - if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) { - var selectedNode = selection.nodes[0]; - RED.view.showQuickAddDialog([ - selectedNode.x + selectedNode.w + 50,selectedNode.y - ]) - } else { - RED.view.showQuickAddDialog(); - } - } + // function addNode() { + // var selection = RED.view.selection(); + // if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) { + // var selectedNode = selection.nodes[0]; + // RED.view.showQuickAddDialog([ + // selectedNode.x + selectedNode.w + 50,selectedNode.y + // ]) + // } else { + // RED.view.showQuickAddDialog(); + // } + // } function gotoNearestNode(direction) { @@ -815,6 +815,9 @@ RED.view.tools = (function() { */ function splitWiresWithLinkNodes(wires) { let wiresToSplit = wires || RED.view.selection().links; + if (!wiresToSplit) { + return + } if (!Array.isArray(wiresToSplit)) { wiresToSplit = [wiresToSplit]; } @@ -1047,6 +1050,135 @@ RED.view.tools = (function() { } } + function addJunctionsToWires(wires) { + let wiresToSplit = wires || RED.view.selection().links; + if (!wiresToSplit) { + return + } + if (!Array.isArray(wiresToSplit)) { + wiresToSplit = [wiresToSplit]; + } + if (wiresToSplit.length === 0) { + return; + } + + var removedLinks = new Set() + var addedLinks = [] + var addedJunctions = [] + + var groupedLinks = {} + wiresToSplit.forEach(function(l) { + var sourceId = l.source.id+":"+l.sourcePort + groupedLinks[sourceId] = groupedLinks[sourceId] || [] + groupedLinks[sourceId].push(l) + + groupedLinks[l.target.id] = groupedLinks[l.target.id] || [] + groupedLinks[l.target.id].push(l) + }); + var linkGroups = Object.keys(groupedLinks) + linkGroups.sort(function(A,B) { + return groupedLinks[B].length - groupedLinks[A].length + }) + linkGroups.forEach(function(gid) { + var links = groupedLinks[gid] + var junction = { + _def: {defaults:{}}, + type: 'junction', + z: RED.workspaces.active(), + id: RED.nodes.id(), + x: 0, + y: 0, + w: 0, h: 0, + outputs: 1, + inputs: 1, + dirty: true + } + links = links.filter(function(l) { return !removedLinks.has(l) }) + if (links.length === 0) { + return + } + let pointCount = 0 + links.forEach(function(l) { + if (l._sliceLocation) { + junction.x += l._sliceLocation.x + junction.y += l._sliceLocation.y + delete l._sliceLocation + pointCount++ + } else { + junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2 + junction.y += l.source.y + l.target.y + pointCount += 2 + } + }) + junction.x = Math.round(junction.x/pointCount) + junction.y = Math.round(junction.y/pointCount) + if (RED.view.snapGrid) { + let gridSize = RED.view.gridSize() + junction.x = (gridSize*Math.round(junction.x/gridSize)); + junction.y = (gridSize*Math.round(junction.y/gridSize)); + } + + var nodeGroups = new Set() + + RED.nodes.addJunction(junction) + addedJunctions.push(junction) + let newLink + if (gid === links[0].source.id+":"+links[0].sourcePort) { + newLink = { + source: links[0].source, + sourcePort: links[0].sourcePort, + target: junction + } + } else { + newLink = { + source: junction, + sourcePort: 0, + target: links[0].target + } + } + addedLinks.push(newLink) + RED.nodes.addLink(newLink) + links.forEach(function(l) { + removedLinks.add(l) + RED.nodes.removeLink(l) + let newLink + if (gid === l.target.id) { + newLink = { + source: l.source, + sourcePort: l.sourcePort, + target: junction + } + } else { + newLink = { + source: junction, + sourcePort: 0, + target: l.target + } + } + addedLinks.push(newLink) + RED.nodes.addLink(newLink) + nodeGroups.add(l.source.g || "__NONE__") + nodeGroups.add(l.target.g || "__NONE__") + }) + if (nodeGroups.size === 1) { + var group = nodeGroups.values().next().value + if (group !== "__NONE__") { + RED.group.addToGroup(RED.nodes.group(group), junction) + } + } + }) + if (addedJunctions.length > 0) { + RED.history.push({ + t: 'add', + links: addedLinks, + junctions: addedJunctions, + removedLinks: Array.from(removedLinks) + }) + RED.nodes.dirty(true) + } + RED.view.redraw(true); + } + return { init: function() { RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); }) @@ -1109,6 +1241,7 @@ RED.view.tools = (function() { RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() }) RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() }); + RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() }); RED.actions.add("core:generate-node-names", generateNodeNames ) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 5ac9525ff..9f28d7191 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -206,7 +206,15 @@ RED.view = (function() { function init() { chart = $("#red-ui-workspace-chart"); - + chart.on('contextmenu', function(evt) { + evt.preventDefault() + evt.stopPropagation() + RED.contextMenu.show({ + x:evt.clientX-5, + y:evt.clientY-5 + }) + return false + }) outer = d3.select("#red-ui-workspace-chart") .append("svg:svg") .attr("width", space_width) @@ -992,7 +1000,10 @@ RED.view = (function() { scroll_position = [chart.scrollLeft(),chart.scrollTop()]; return; } - if (!mousedown_node && !mousedown_link && !mousedown_group) { + if (d3.event.button === 2) { + return + } + if (!mousedown_node && !mousedown_link && !mousedown_group && !d3.event.shiftKey) { selectedLinks.clear(); updateSelection(); } @@ -1046,6 +1057,7 @@ RED.view = (function() { options = options || {}; var point = options.position || lastClickPosition; var spliceLink = options.splice; + var spliceMultipleLinks = options.spliceMultiple var targetGroup = options.group; var touchTrigger = options.touchTrigger; @@ -1058,6 +1070,10 @@ RED.view = (function() { var ox = point[0]; var oy = point[1]; + const offset = $("#red-ui-workspace-chart").offset() + var clientX = ox + offset.left + var clientY = oy + offset.top + if (RED.settings.get("editor").view['view-snap-grid']) { // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') point[0] = Math.round(point[0] / gridSize) * gridSize; @@ -1109,8 +1125,12 @@ RED.view = (function() { } hideDragLines(); } - if (spliceLink) { - filter = {input:true, output:true} + if (spliceLink || spliceMultipleLinks) { + filter = { + input:true, + output:true, + spliceMultiple: spliceMultipleLinks + } } var rebuildQuickAddLink = function() { @@ -1135,8 +1155,8 @@ RED.view = (function() { var lastAddedWidth; RED.typeSearch.show({ - x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]), - y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]), + x:clientX-mainPos.left-node_width/2 - (ox-point[0]), + y:clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]), disableFocus: touchTrigger, filter: filter, move: function(dx,dy) { @@ -1164,7 +1184,7 @@ RED.view = (function() { hideDragLines(); redraw(); }, - add: function(type,keepAdding) { + add: function(type, keepAdding) { if (touchTrigger) { keepAdding = false; resetMouseVars(); @@ -1172,7 +1192,13 @@ RED.view = (function() { var nn; var historyEvent; - if (type === 'junction') { + if (/^_action_:/.test(type)) { + const actionName = type.substring(9) + quickAddActive = false; + ghostNode.remove(); + RED.actions.invoke(actionName) + return + } else if (type === 'junction') { nn = { _def: {defaults:{}}, type: 'junction', @@ -1844,8 +1870,20 @@ RED.view = (function() { } } }) - - + activeLinks.forEach(function(link) { + if (!link.selected) { + var sourceY = link.source.y + var targetY = link.target.y + var sourceX = link.source.x+(link.source.w/2) + 10 + var targetX = link.target.x-(link.target.w/2) - 10 + if ( + sourceX > x && sourceX < x2 && sourceY > y && sourceY < y2 && + targetX > x && targetX < x2 && targetY > y && targetY < y2 + ) { + selectedLinks.add(link); + } + } + }) // var selectionChanged = false; // do { @@ -1893,114 +1931,118 @@ RED.view = (function() { slicePath = null; RED.view.redraw(true); } else if (mouse_mode == RED.state.SLICING_JUNCTION) { - var removedLinks = new Set() - var addedLinks = [] - var addedJunctions = [] - - var groupedLinks = {} - selectedLinks.forEach(function(l) { - var sourceId = l.source.id+":"+l.sourcePort - groupedLinks[sourceId] = groupedLinks[sourceId] || [] - groupedLinks[sourceId].push(l) - - groupedLinks[l.target.id] = groupedLinks[l.target.id] || [] - groupedLinks[l.target.id].push(l) - }); - var linkGroups = Object.keys(groupedLinks) - linkGroups.sort(function(A,B) { - return groupedLinks[B].length - groupedLinks[A].length - }) - linkGroups.forEach(function(gid) { - var links = groupedLinks[gid] - var junction = { - _def: {defaults:{}}, - type: 'junction', - z: RED.workspaces.active(), - id: RED.nodes.id(), - x: 0, - y: 0, - w: 0, h: 0, - outputs: 1, - inputs: 1, - dirty: true - } - links = links.filter(function(l) { return !removedLinks.has(l) }) - if (links.length === 0) { - return - } - links.forEach(function(l) { - junction.x += l._sliceLocation.x - junction.y += l._sliceLocation.y - }) - junction.x = Math.round(junction.x/links.length) - junction.y = Math.round(junction.y/links.length) - if (snapGrid) { - junction.x = (gridSize*Math.round(junction.x/gridSize)); - junction.y = (gridSize*Math.round(junction.y/gridSize)); - } - - var nodeGroups = new Set() - - RED.nodes.addJunction(junction) - addedJunctions.push(junction) - let newLink - if (gid === links[0].source.id+":"+links[0].sourcePort) { - newLink = { - source: links[0].source, - sourcePort: links[0].sourcePort, - target: junction - } - } else { - newLink = { - source: junction, - sourcePort: 0, - target: links[0].target - } - } - addedLinks.push(newLink) - RED.nodes.addLink(newLink) - links.forEach(function(l) { - removedLinks.add(l) - RED.nodes.removeLink(l) - let newLink - if (gid === l.target.id) { - newLink = { - source: l.source, - sourcePort: l.sourcePort, - target: junction - } - } else { - newLink = { - source: junction, - sourcePort: 0, - target: l.target - } - } - addedLinks.push(newLink) - RED.nodes.addLink(newLink) - nodeGroups.add(l.source.g || "__NONE__") - nodeGroups.add(l.target.g || "__NONE__") - }) - if (nodeGroups.size === 1) { - var group = nodeGroups.values().next().value - if (group !== "__NONE__") { - RED.group.addToGroup(RED.nodes.group(group), junction) - } - } - }) + RED.actions.invoke("core:split-wires-with-junctions") slicePath.remove(); slicePath = null; - if (addedJunctions.length > 0) { - RED.history.push({ - t: 'add', - links: addedLinks, - junctions: addedJunctions, - removedLinks: Array.from(removedLinks) - }) - RED.nodes.dirty(true) - } - RED.view.redraw(true); + // var removedLinks = new Set() + // var addedLinks = [] + // var addedJunctions = [] + // + // var groupedLinks = {} + // selectedLinks.forEach(function(l) { + // var sourceId = l.source.id+":"+l.sourcePort + // groupedLinks[sourceId] = groupedLinks[sourceId] || [] + // groupedLinks[sourceId].push(l) + // + // groupedLinks[l.target.id] = groupedLinks[l.target.id] || [] + // groupedLinks[l.target.id].push(l) + // }); + // var linkGroups = Object.keys(groupedLinks) + // linkGroups.sort(function(A,B) { + // return groupedLinks[B].length - groupedLinks[A].length + // }) + // linkGroups.forEach(function(gid) { + // var links = groupedLinks[gid] + // var junction = { + // _def: {defaults:{}}, + // type: 'junction', + // z: RED.workspaces.active(), + // id: RED.nodes.id(), + // x: 0, + // y: 0, + // w: 0, h: 0, + // outputs: 1, + // inputs: 1, + // dirty: true + // } + // links = links.filter(function(l) { return !removedLinks.has(l) }) + // if (links.length === 0) { + // return + // } + // links.forEach(function(l) { + // junction.x += l._sliceLocation.x + // junction.y += l._sliceLocation.y + // }) + // junction.x = Math.round(junction.x/links.length) + // junction.y = Math.round(junction.y/links.length) + // if (snapGrid) { + // junction.x = (gridSize*Math.round(junction.x/gridSize)); + // junction.y = (gridSize*Math.round(junction.y/gridSize)); + // } + // + // var nodeGroups = new Set() + // + // RED.nodes.addJunction(junction) + // addedJunctions.push(junction) + // let newLink + // if (gid === links[0].source.id+":"+links[0].sourcePort) { + // newLink = { + // source: links[0].source, + // sourcePort: links[0].sourcePort, + // target: junction + // } + // } else { + // newLink = { + // source: junction, + // sourcePort: 0, + // target: links[0].target + // } + // } + // addedLinks.push(newLink) + // RED.nodes.addLink(newLink) + // links.forEach(function(l) { + // removedLinks.add(l) + // RED.nodes.removeLink(l) + // let newLink + // if (gid === l.target.id) { + // newLink = { + // source: l.source, + // sourcePort: l.sourcePort, + // target: junction + // } + // } else { + // newLink = { + // source: junction, + // sourcePort: 0, + // target: l.target + // } + // } + // addedLinks.push(newLink) + // RED.nodes.addLink(newLink) + // nodeGroups.add(l.source.g || "__NONE__") + // nodeGroups.add(l.target.g || "__NONE__") + // }) + // if (nodeGroups.size === 1) { + // var group = nodeGroups.values().next().value + // if (group !== "__NONE__") { + // RED.group.addToGroup(RED.nodes.group(group), junction) + // } + // } + // }) + // slicePath.remove(); + // slicePath = null; + // + // if (addedJunctions.length > 0) { + // RED.history.push({ + // t: 'add', + // links: addedLinks, + // junctions: addedJunctions, + // removedLinks: Array.from(removedLinks) + // }) + // RED.nodes.dirty(true) + // } + // RED.view.redraw(true); } if (mouse_mode == RED.state.MOVING_ACTIVE) { if (movingSet.length() > 0) { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss index 98ab3bd3b..4104fd83e 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss @@ -46,7 +46,7 @@ & > li > a, & > li > a:focus { display: block; - padding: 4px 12px 4px 32px; + padding: 4px 20px 4px 12px; clear: both; font-weight: normal; line-height: 20px; @@ -54,7 +54,10 @@ white-space: normal !important; outline: none; } - + & > li.pull-left > a, + & > li.pull-left > a:focus { + padding: 4px 12px 4px 32px; + } & > .active > a, & > .active > a:hover, & > .active > a:focus { @@ -145,8 +148,8 @@ position: relative; & > .red-ui-menu-dropdown { top: 0; - left: 100%; - margin-top: -6px; + left: calc(100% - 5px); + margin-top: 0; margin-left: -1px; } &.open > .red-ui-menu-dropdown, @@ -175,10 +178,10 @@ } } -.red-ui-menu-dropdown-submenu>a:after { +.red-ui-menu-dropdown-submenu.pull-left>a:after { display: none; } -.red-ui-menu-dropdown-submenu>a:before { +.red-ui-menu-dropdown-submenu.pull-left>a:before { display: block; float: left; width: 0; @@ -192,7 +195,25 @@ border-width: 5px 5px 5px 0; content: " "; } - +.red-ui-menu-dropdown-direction-right { + .red-ui-menu-dropdown-submenu>a:after { + display: none; + } + .red-ui-menu-dropdown-submenu>a:before { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -15px; + /* Caret Arrow */ + border-color: transparent; + border-left-color: $menuCaret; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; + } +} .red-ui-menu-dropdown-submenu.disabled > a:before { border-right-color: $menuCaret; } From ad32677263c3219161f0382973df9be550f52f13 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 14:37:48 +0100 Subject: [PATCH 35/53] Fix lint errors in contextMenu --- .../@node-red/editor-client/src/js/ui/contextMenu.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 1a0dee2bb..379ed5433 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -103,14 +103,14 @@ RED.contextMenu = (function() { { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() }, { onselect: 'core:delete-selection', disabled: !canDelete }, { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }, - { onselect: 'core:select-all-nodes' }, + { onselect: 'core:select-all-nodes' } ) if (hasSelection) { menuItems.push( null, - isGroup - ? { onselect: 'core:ungroup-selection', disabled: !isGroup } + isGroup ? + { onselect: 'core:ungroup-selection', disabled: !isGroup } : { onselect: 'core:group-selection', disabled: !hasSelection } ) if (canRemoveFromGroup) { From 1780cb9b91a3ac0d954cf2f5ddb821f82f5535a4 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 14:47:11 +0100 Subject: [PATCH 36/53] Update dependencies --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index d22cf89dd..87518820e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "hash-sum": "2.0.0", "hpagent": "1.0.0", "https-proxy-agent": "5.0.1", - "i18next": "21.8.2", + "i18next": "21.8.10", "iconv-lite": "0.6.3", "is-utf8": "0.2.1", "js-yaml": "4.1.0", @@ -62,7 +62,7 @@ "moment": "2.29.3", "moment-timezone": "0.5.34", "mqtt": "4.3.7", - "multer": "1.4.4", + "multer": "1.4.5-lts.1", "mustache": "4.2.0", "node-red-admin": "^3.0.0", "node-watch": "0.7.3", @@ -76,7 +76,7 @@ "semver": "7.3.7", "tar": "6.1.11", "tough-cookie": "4.0.0", - "uglify-js": "3.15.5", + "uglify-js": "3.16.0", "uuid": "8.3.2", "ws": "7.5.6", "xml2js": "0.4.23" @@ -105,16 +105,16 @@ "grunt-sass": "~3.1.0", "grunt-simple-mocha": "~0.4.1", "grunt-simple-nyc": "^3.0.1", - "i18next-http-backend": "1.4.0", + "i18next-http-backend": "1.4.1", "jquery-i18next": "1.2.1", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", - "marked": "4.0.15", + "marked": "4.0.17", "minami": "1.2.3", "mocha": "9.2.2", "node-red-node-test-helper": "^0.2.7", "nodemon": "2.0.16", "proxy": "^1.0.2", - "sass": "1.51.0", + "sass": "1.52.3", "should": "13.2.3", "sinon": "11.1.2", "stoppable": "^1.1.0", From 16644284296faf6aea7108533cf430bde41c42f2 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 15:37:07 +0100 Subject: [PATCH 37/53] Update tour for 3.0-beta.3 --- .../src/tours/images/context-menu.png | Bin 0 -> 67976 bytes .../editor-client/src/tours/welcome.js | 35 ++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/tours/images/context-menu.png diff --git a/packages/node_modules/@node-red/editor-client/src/tours/images/context-menu.png b/packages/node_modules/@node-red/editor-client/src/tours/images/context-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..1acaab48b867480c8927079552f7e9431374573d GIT binary patch literal 67976 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≧h`;Ao2cW=E&fS`~X;_=(6n8!UyaT&;l z#E7Cu(yRZ<16bDLrm=f@S0)Q7jUMN^S{ic*)x%_3YfaS&HK*|X1yGP*o<#4H)OXII zO&uejsE#W4FZW(zQ%|BYLiMQ<1dA;nV|6Yk{K)b823C;uzi{{0CWE|$IiDbWqy1;S zM<3*Uj>>zJhF+jLtPhd(qC9iKSra9y8xRDae+NiP4;~Ldn3QwT z%rsvnyol7VYm}3M*>>2aQv~u7cs~SL|MO_-? zGyDK2epiNygUe)}a%9l(oOfvF#V5I!PsaMvwF8FFM#ja@^09k@`X0<CZT$x?M%l9gKORL%S)Y0!;v5x8}}4^69zr z@H@WyjtsH0FgG}Q|#(b1$Z+m=%laWcNrZ!tgj`Y_WP^Rb{8fem9R`!&*K&?cs&$6TpD z-rk>6;-PQ7rJN7R#o{4l;OvWOW1oYHxLjoP#vTnhP%~5X*T3BL6CSlv!c;QxPud*6eKU4^+c2RQ%sJwbK7x@ zz5Mns+}y7&PEl#N;FRJFADZq5sJ*B@n(s(%2=%e)Be?qJuPlmMk;p5`5!Rux2j}&bz&7KCx2CeUp zfI??q>&f%>Dppv$1eK&YP2i8Zq zWwbn~?4xXmYPSwuGp57G9qYl2@Hh1S^RsX>gxDaQ4KS{}j|XmmQYRn$vt!5mzY-Tj zNDkdXzdh)jx}i*eXg2W01z_gJSzMc=Pbm@pO+Qs*Zc$zNMgWv(v~=p@iQ#(qN(W!U zs<;0POccESH^#eF3#VPhpyD2Zc10KrJarbZ{x%fbKw5YAC(<}KR#@Fr43y>LxI8%U z5$y5zl+pa{gW0Z2Q^qf75e-;UdhXvyF5RG;oAdo^nt<}=iRbt-j;ZX;HygeDBh{m# z-_70u#K`QE$YY4qgJ+TbNiUUw*H{%OrQlzMrRNSsazX7qQn^SjW(@EixpAkJ*#xQt zcvtjX{#QeKL~L4GzZJT`;-`NDnAB>;tv4(D_BNC-7-<*@tc^z7RGQ92sMCENjS<4<^*4JMm zEwP=~i@iY_Y)eYUWp%Q=)M9^5SReDH?aB4vsTK=t`I>lb{WjD)5@z%D}mv_0Ijr}HLc@UE=9 z3;Fyni^q|E+|5o`g@p9S+I9ffSx1A_e6TVa5XRhg;QKaTXME=pen4>QUO6vw&-OlO zfClPR=U+i)Oar&AK+O((n122=J$Yb~nL@$niJWBmv1c4`^dqI*XEVq9KuMlLW68l| zl3XvJLPhKO*Z%w+3S4*gXA5n#FxU?n0VuiV5z>D6l7a^^VSBPMhZ#7wt@%!xO4)Fm z4|63$EgBWxkMDLvaK*;?&5L>0*>{dby7ITsOq!4D6k~u-NhAEo(_@XI2VepID)%Ey zXBe2UZ(6D?eW4zr`{ogd0kURC#ykz~(Un$+qyYS_`DC|e9Y@DLgGy>Mz&AA?-JHt# zTjLe`)b*Y3`7faS=vyfcFop|uwKOCM-LrIrbMA=a{Oa1@JOrx(@A%C z?&YM0)9(F?p`7R6-vTffkYm(60NBJp?&sITW)KJjOFnr9LTuk}ceUGSWu9oShs;-@eOD|$SD1cgVeO;d1w6`g+hlb9#_^=8f_ zuBR?G*xsP!3QBN?mcS_QkHYYAAAndag6kfjekLvQc5YmPM<=qPR+@Zar{#ddaw$N* z_Tu>MRX7fHF1^q3pSqQtP1FJOHo$Piil6kKoK<+XBT}|svj(^59>niE6uG_+AdU?! zuqkYLJpTl2-2XaCJG9m3pxCEMj9gw5Q66iIQJ+_n`*?|idygm5$CZ%L&Fj~PQaPG- zzkNUIoNb*k;A+;J{0)PZfc+8z45i*@DhINPbkq}Jn^e$=$IQ+)`Yq&2);r7I=K#p+ ziQFm3vs`BgR?O1gecFyl)aY6ZI<0#=Iv3v_jysM^Rmn9yMal`EWpkqG(CyfS7M2z( zth?I0fbu||o2kKQnJ7%j>-Fq-&5vh>^U)rD3Ic2eWHrq^%_0`=o`22OAU?u=q{**} zZW}EU(#Sj)4DaRRj&+>g`*WfK=Aj_0Ijp~4ch-({4ChEDi5H6%qiiZE zC-~>wA56twxH9&phd?+3Inh)b7q!>}Kc6l!sE+)jFeWPPeVZmXzr4YZ0RwDId`dv* z!ML+f`>z)QcKRG1FuR5FWqcSj=%1l00ps9PTB-flwga}WH?$-Xg*)d_3n4iaw7ByuPNTJR)Hqgz)D(ZpU(ujq@jC1WrTY zF2jHksJj3N$)@%o1b3lgf1rE~BJvRGm6#t7Gx$)%2iRaejUSdUER+GB4$M*c^^wV- zj&y(7Ck2kStC@I?LgnMo6Z`s&y4W9n*jYB`H+~&;^qGK_YXUOyT9(V5lc@X~-wKTOzs&mOYwGl;*qv&}3e!j7U zO!H9{svq)oUa8&#-gvCsnu#6=c^_PBFN$HgR=Tvn9t*zXlgIB;+MVw!l1G zBh9#5&2CYJ;NzwP7Gw3}A#mtSxGDKJprxD{i}8*jQ@9{vETJ8$FRk(XiE3FM;0XDW z`Ibv?l#+|cQ9EM7KvG0>0wR_$wYB<9r1Z#|w8`^hmprR$0G^@=LH7Q*(%sXb@in=} z96*#XHWL{D#ZEHn;*0|7r75(qKx13AeIO(I%HtI*Y9XTwFafe2@3dB@##%EhG~*r1 z#KmI(jBEhVs2&=pSJ&G_>Tb!M-#J?>8s$fV=hiJ5{E-p>H5z_N%a=bb#g7LMYTjt$j68tSn;dpWlF1-VFSk%$psezhiOV6{vbJ#AwCOdbCkv%Pck-WT)Cat zjzCG^W)-O`-_(^8A%IGt!60jcd!;Voc7>7O*4=s_R`N(UJ5vsDRn&=&(`3Jg;;et) zHrH5u<#P8<5%cJ3dGoq5w`yzuN`8Du4H|lhB0zWU@%})-<*jD;s?asgWi*~Eye~5y zQO-e~2zZa=2AV|#@-RyCB2VDMna&9tCr>eJ&CB#JCSnOb!!lb|4DDs>wa9jT<5IQx zA%L}a)2=A3=DUcqHW_wr+7j&d8RC(ecQgoKo~Jjhy_ejcf{7{$F_rp+$2@%kH~*QY z@UETLmSk#*3l~5plF-gnXwvP<)X3U0m{M?pC%W~p{_4(RIG>d2N)E$7$N;OkOs)Do z5gH|HqnXx_VE-JBH&=?+C!~L8s?M8zqG&lVwIy-nsx2AkMz?BUVc`Mt?cS-TX5puS zbPtLL$2Tw(@|xLE)&juToW$uc-)XaK7cJOx=%s(Y#+%Pj_n0RGq!|3DQ{SLM8h(wC z08eUg2pl~OV2WIbQOSr<+#FcMyDrVU%={|=8w#`I?7H$o7dPNNIU;dVY{ zmHsw{3JTZ_|Ct5h(J&>8ui3#<+J#{EX%i(*;q(KJrj96&PpjQl;1DYR+v88hanZy% z5)l5F@VYO>z_D-3tbkaLb;TdhALVFYq?4xjU*x@ITvc8FHz){5r$|V5cXR0O?w0OO z>F(|hX{1FEM5G%8q(M5BMo^4d2fgn5`p@&si)JLmYy|Yokrm)Y7LPaRNNrC4X3DNOH_?} z|C&jILxgOH&Euhp^&PWaN~O9w@$E(txyf3pQ_8RF7sb_EXr-vG6UYx(?H73O4G4X! z8Dmn2>jLXTKVrg!=)hc0%tpgD2Lg2Oaw&nNo1l%5N0~QIeUXV7J`Yw%xMG09XnKXhd7DNBGLU~VH#s0S>9;eN9_ZkrdT7ckep#HVLLyrX`RtN`(NCAsI>C&eX5d8(>K0=$pAHD&yW8Q>16FRE@HXZ*FY}rQiCm@XghBB5< z^Ig7|=psMP&`nmpfvl441m4J9P`?T2e)T^1y5gKvxDie2F)j8}>lyw1`}n?PA+BWL z@$mVN6_x?Q1Q+Jy$Y8l--&bc$g5ht)8Gr-zGkv0U7K0uowy_n*+I`lGlH%LgW-*fL z|4dqVg#f%uz#GuT2B={^0e>Ua9}PdYO>8jMd4;Tg()~na2-f+V5zfscG?Lv@{QLRP zdN1w$Z{eR;v)@*MIox$RC(dojGHc>vTI;7jJ>m`#AItf?Ao*xwa88BgEi*bEL;ceD zuT_^mw9caMaw!H*9p!nWabAx>5loJ)+u=t2iq*Kw2Z%X(DQN||?LIQ(Uw~OYk(5cJ z=VrxOWJg1pAAXG3dlk$4%DT|kDoWV#)kkf?&y5m~9zq8j^sIwxSVOcRq#*5^;38Or z=*sVYZkg)-_5Jv~cbhegZBh8{bIV~xp7t+QHZrTh2!2j^JSN>9Y6z*d&gZ3$BWy#GRC&^1hDcmiV_vWy%8u#;VrQgQ{5e{8n_tk zF^O5-f#nDQ3IaJz8`Y1G6D}0?vGmr>FYT4G{6Mt}AzcCF*tfSbDtx2Ne&sr=Q$sjB zSNJ-K_Z10vHdP?C?l%f1%OI%7*@W3^jJ(%?7u9FRFsb9DROGbla%*`U$1c8uN5(h3 zzw7+vZ}l~S!@8D9WOkOXInm;n#6`mX-s;aXnGM?N!+(Q?!K_m7%C_!ZQJx)%FV?qp zP!%F3yBEc)BJ{3Y$D3SLR~8b==y^wEA+@$>LZ!2y4T!MyxgbLz9JG|T>rBV&NVvPzqx8IH!rWJ4b&XjJg_fH*&p5kF=K+E%bvY45SpCdGP?DDdzI=q z<0SA1uhT82dWhe0 zq&Qx2VMHW_^`2L&N&-h-qU94hf1F1g!jcVChT9$_|;D@F8Y_*rY$>0-T}%})80 zx-Gq?{wD+o7@^sa6Mb&7-kw#CU8?Zcw|A_lo2pwKbcny^s(OA2x7eE+(P!|)?3v90 zRl%LTs4m)-ff8d)&*(X7Q&>#8^FK+l%~ ziDtdd_a#iFf4eFobRx8-;SKbv_nE#xQGU%A*UDvIsc8Mih*2+SK3QjXv^Ux|`ImQ& za3PdY2MHILhjgx#6Q!lC_{R}Lr@W;bu2V#rOVqSBG|2aa4E8~o=<%=EPIA7#~i}ghb8jsc6*yAFDBd4I=@(N=Dq?X3o%AwIuY#(Z<%HN4tW&O%uKwFTX-R(eGt~UM}LRr zw)?_cUm72+T;-=U0T(u%ZYPbg)?4XzS>DFg(?CgF>46#nX{Va8N0{3_Le#V0je01=Z)`3aU&{e;ht9A9yI3cG7r z{fFeW(#N#b!r zu=k71d3!777M~Ux9jDmFesbcn+AB_yPz{SDp|4ejzmBh~fT|!;sd#Owv3{z0#UtjU z<`MCBq=?C5)qg^S_Ap_#Y77x+9&TMK{IQWJ*FcLWpBIOLNwSjpvy_WJpnMJNxX%dF zPlMl~VvW${pxNg)O4ewbj!(2U-i$dSIAU2&i3-Ahi)>%2n(N6LszFJ?ml*QlwH|oj zyzb%?5z~pCy$k1-?6@#RkG70^KRKDUVO5EoS$~U-p-Q0P2~EZQgk)lmsq->MS;>UB zY&-grESq|87~}o!OaHQvv^1jEK@7rh7KUp$#R(S$nge!G6m?;}1CQ?o=^k6D(J$f3%RSgh(jnkV~>guHJX1p2q=9_0m{=Qik~oI-M*Z^EB@B zl>88Pau3${wm{9~1c$er@Lc0-jxWa|8~V^0UwOb!gLy|W)WGs6yGZ!3jv5s zexb{UO?`JDDX{JY{ZG^ymU*pZQcC*pK$d^&+W=c#i;{#Q>`gB7$`|SEF8amRrwaG( z5m}JwrP#Iki@c9w4Q~!CSq?-WL@_wGjR=~CRHF|D5N#>hsl`4&%(R6MnB$L%`rNE!G^wpW<^c8D7R)GOwvHBz?_&Qo*)zrEe7&0Tpz`V~^^*2YVj=E>t zYQ3Dzfrud6dZcdH@U%?o`;vyvb0Kc47oJPJ71(ONmIwx<))rVunuuq3lb^Jl6tffG z$q8%3VB#<^SgsKxfLB*vv0VZ5JtkfBAWr7o7%@{6l)K%IDHI$HDaub{Q`&sQ=av;xVIxZQw zS@@PnzM0D>G?QBA_%MS{EsSQ7tuv~r?A2uLh9%|mP`VuPqZ?()G@rb6<4(C+H)&1p z=-MyDMs2vDko1JCw`7eq7eG}!#^F?wWOq5)_D-AWqYx0W3;<@Sn`DhO`}6^-S`BFuj#Wvb!h97$wGH@tB^q>f z+b#rnh#ni7b>S4wT}H;bebe{<3Fw1Y9_iJ|@`#LXkHp{0|NOial-7}`L?XV25Q#>( zOX%3Xu^UI%iJ#a1Sl;e0Wr3~OP z?E_|%jXizc+tJE=H~FP$sE_ulm-myudpv)qZe>KH_o1T<{g0GU;jXi)Ot=ipvzFOT$E9b|FusS9UaUZ0qw#Ep zDNAKS_0fvUU6Jc0!QLqF4wkxmtx%#ID8 zLy?2-^tMKkV+pLGj%3`8ZIwqAOLi}!ue1C>+{gs8GsDa^?K`5pt2@_EBObIvLeG^K zQ+botcUip2U5rmF2|@fKS|gHJai++yIaUQ;#BK~OF0(@e zBtxx6C-U?2dOfri*y5)zw70i7H=CWN$a${n9)Fm5Pu&pkJ`^4+yn?}N1ob^hNx2Y~ z{Up0XDL?j>geg3JTk>(y2e^UPi_hZD)=FGYcBCJv4t{=v{@~cNCPg$0j)IOPs21{L z8{lpoC&nX*_Z4>6%wL@GwrSM(`; zhB)JnQm>C9r<~_0C_SOi@Z=Bh=2*@t)0p!S}ty&t0f!(z44vKK6HhJt|a7`%cepkB$9M$I%UTU`I(W# zcAs6F&sNGj^vi3D)*Nz=_g{z?b2>2RiR!H*C13R-z^yvbcnrcRIUG=(@FfZ#e*SW# z#v1Yq@oZ@1MPmD%rMjo`5(G%P1T_~oqh?Dqv!WvPeX|0xc{qvBZ6zGQp$`h_ z;w&!Z@b7N7lZIe1vghJQ;f9`23)aY9ST)mVZITXQ)0Oj2NYNn4bR1>59&n9$cwI=V zdbK6_H8a8fQW}3^iQ7Al$cnZ+g5z}eVq3W}|2fjUJLU!rO+PCztWoBTlZ{wd-7;(P@kPjAhmW=2?ZHYI{M?hlW zUut@X)8{-1hYLHN;lk~?r&TmCcC6I@jOKuIlj@86`_pDkAruH6m<{}GW@w)EYZs*@%ctf1S2twdr%Q3;H{_PAuTmc@{Z9oE29kB zN-N9y?eD~f3x7HPpr_a6Vc2fH@wP+{0#}j0)w!+SSkJGd8M;L^F6renu1I~5XrFi~ zw#%ido#73e!wJnX1uxX>(rG1e#9D0Em2pyrphd{Ltp))n)o+}Wrr*eZni2oZo;!~c z`4Q4N?ZXv*L)tJw_+n4bKwHLtK9Jk%-lr3fMW!GquEG4gXP~)T)J$PaIGxE2+qq^R z>)Qqr!_I5APfeB{Xru*fJjNUaR7?}Fq3zrG++PX$24BlQB7EK~c_!jg>^nfie~>~K z8c_dMm)}G`ib{T-%miQ;+#4z&{f_%Zon$8waw-?GFH`u+`EWJSLcWuR@8I5qLiL8{ zYcbd#yF+2slb+88O}JmDWS<)h4_xa#SIHrxCH$(3a}yHX%PKY}#2_J7W|vhz#pbEb9dBrptT!P5?n6*tO}0 zwQd%Jk|^??+}LeIdwCa)Prz>QZrTQ?pE3sGzq<(zsMp}?v9Acn5X%uY7p!k$9KN;YGv*Y5P{YY6?F zPNJae_T+6U&`;t|&d1-ZR~|vj zm^JKd{F8-ogD~0?;0f$n&*ojSeAzziU_@v>coP{n#bb#D@ob}?fZ7Nv(t7$AJ13`{ z8jZ489WX1j{z6}W=1l{DuCArdDE}956@^6u)U@;e-7ne{_OMkv;{K#y|A$ju2O#Us zc0Z_q#Ax5U&B#LKH-SR4G;GY|T20r>C} zQk39-z6Ops5G{WH`7!Z1dcF`9^l{K2aq-Ph?bt3t>GhBg)NC7~FDZrJ1Q8bI04TB- zZZF}i!`JS<9e5!yZ@-|^mQk+5Bt}6`JB|#SVJ~Xwt?lta8QK~k_qfkUy{SHqzDbx2 zg&svnqe{BONa)3w8~NPS^S&k1k)i=`B>tktjdmYvcqU${Ht4lfl$P_2PDdqQ9DS@! z`9}(uhe^YI-0&{2JoUY|4ggQS%-4o~kbdge3P43CU{<>>L$j*~(Wp6ghSDUp+ z1a^^@HPu0(+AcQPKH#yTahn9)6ySv(cb?BwM zA!lG)b88`) zKYadp0(JikpsOM80w=f^pkb|jw(s=;9@3$-0Z?uGZwIJ9L*Y>(c`>~!q&rb7&ts^? zM+;E={*t5Qjo0%A4GGaIgS{GwFdaYQ_om7Et-Z*+C~fjiZlWdoFR5;Tr7K;c=Z@BV z2Ph7PK(4_@Eu9pB)O{0nFF?-#rXOmT0%ZJf2d63v2qGjca6JwLGeZAC6M!8~0n^Rd z8)dv1Cv&47U6`EBOKH@6Z*Ja}ATv4wmc$!1_kJiDnrY(&9O58r5Ja&$@6#!FB>E`G zGf)FbX8qf*%V)j3z;Fr$uw|?b9Gc)ce%*jX&BX0a;86YKHpZ-D=n5ZW4IF{nT&l56 z!u}_I?uS_m7}fV3Im8T877r3(6S21V-QHu4=-lhEPjurLOD9xLfR9P$Y#muSnbEM@ za_E-=_($ArV9ZNqEe>ogkF@8>bGf2+3QNMt*Pyt|66 zur1Heo0yI~=E`It&UQesN$W-Xo4he2%wqJQEcJ0|T9ZMRghpUyW_h3?k?Ukmwst;$ zRyj5dM?F6~8->~VgWR#F=WHym{e^H9ykcUF0CI_MErjpwW16r8;Ike}K_YLv;=uZI zsHGF6ed#rrfL_Tq0k^e&8~N0!*eEcAZ;SnLzx5Agb2kOut&6QV^YNE=zwS;=4w=Iq zO2EmV%rHg?oN_4cSE=h)04=kIZp%6$3#l41HIS)`qLZ^wkzeLKML0~9aVJ71ADh3n zomP(~9QD)B<*sjC8%zO;vPtG9uKINM7FkA0^)026t6sH1X;Sba_$`wqUiEqYc^<;G zgy#f4|CVb8ycqSygdHv4C^cbycb!IG8-N+CYUmIQHz$Sf9JUn8**SC$a8w{oyHZ)0 zw*X=uwbl#&-tg|1-&Oc0#NLcQfITg!RR!75&t?bTCc>nzfxf{+WcX|c&&UI7l9!kg zZj$y=vE6f@E`;%FbJFTH{ZqFfIVKOXBV3{NEIC!UT_5pO(JP9eOr4kavS9NfPM;1N$VwMxoYMmDn$)2thD?$RW8X3wJl4Bi4@am)6;6|5GqWw$A}x zibfo+cdU?YpNhy8cE6l*(x$I5jGFf-Wk@~Kj+Rq$?oqPlVD=M)64Z(1Deg;}UrIDp z;WP$jyj=c!Qqjlofr5X^Cy7C zF-Rwf;9$9Z+12an(b%H6^gCtt^Ptb#s^Q(cC9#&4-eAHVJ=F5jd&Sm}MHHKk9Uh;O z^;NQ?_qA1r#HvmH1)qLWLJ6&%vWpO@UD@U`5uVhKUzsXZF`X^K?3sG*bEm+K$@UWt zAI(QuDpYSZpVI^upnQcb^0EVw=ByI&xq)3KZQ+*Ud`J94U_yX)rU+5>-D zA&Zbd@>X^4gJ8lPPW2X!>$wAC;vg=1$8OtKLQXXYN3rqd40)}N6|rJIneCP^z3nK6 z*RC&^8PnYrm=YLwA&4{dLq%NCo9sr8guAK%C;=o1-5FLThj;FCM6QO$2kCr>w%do& zpL15RITSC-MVOp0GcDY*q!`?&f@IK$?7Rc0#kJqe@$9iOxm)^*I(gBqge3Uh;Z zcqY6x;^9PbyvPJCrzqwkzWU-oBo}Ua2M;M{hn!KLBee`Q(fPPBBdzaiLHHW9EM8QG zPUeh^_ssSc&oyy@WBjO!AzckF2kj1%O**@A+kCB@)}RjYvov+d=?{XJI~qfigJ>kL z))36Rs^lk$H@JLcC((NAG_?|~-t%+$4@L$c5=jlOy^30}9kpvQm0B`;4dJp&yalSN z(OHEYf}9k(UP-z7I@f||f;8J%WkgQME!&2#hr-xs4L!eii_tG$ovW&-U#)ejKz0xu3J3M#6fKtS#pX|xi`gwB;e1s7 z`l8@{@>Lti$j`9bHH6`KVZ@Jt2D+RO* zn)NB?(>c~d=T=IFn=Z|QQ4P9E5q7iL6wCp~S94_;AhYNLLHsuDLjC^HJ)IOyYQFQ5 zoj-hU%UCFIpnZ>T9&#aGB(idV9kao=HOzN_-#^Q|7tlsTQ7W5N6u)=q7^V6ozKJAY zOst5Ex0~-Pk);n(9_F)FF&qODOWw~|&1=mAEW1^e3fl-14zIOMFcw zO9qJ_5sBHtZBg_<3g~ujD4kLVk%~y=l_9`i2Oh@okHNIP4g#}NmkRR~jXg6?>FsE! zs^>hO5sxz=@tkx2W{iCBiTa4dQUG7RYqdu+W9M$i5V^1dwcGbCvmPgh&+`FJpK0x~ zf8Z??ULZf_HfJ-#$t-!((xtrc{kP5BT>896?(Ap>e`bMQsynT~1aY3#BzymdfMuyf z6|b=p41RBk`qM}nluXElD#+g~r@m5R*c!kv7d}NZjqCS64pxe9B~l5+op8O&*afxIi%YxRUlf zCcw~^Fa40NWRue_kB@6fwGB7-!e+QYq@{4uDuMLXzblYSGTAWWeZpQEgr3`RbtF=9 zo*J+Lisze{wYhMjPI_*jFnhi&5qAb&!DgGF z8mT>=ja2v5NN^Kn&3C1YET|I+Z%6GfN!B18JurJbC69R_FTuknC5vp0B2;8^8_&F( z`BKtdBhboSL!Zaqv=bVamY2VhVyn%%FXqEh?5WX3bko~i8naf%>@9)Q$^3Mbf;nkE z!+a;&dd6!nc7p)t6Y@&uDQ3f&=>!M1T*ubd>g6Kg zyRO~vF(B!e6!JNOEDxU2vLwdik@!~mlR{v4DU5%2YEp&}kWV{3aICCMlbp`!9^u0z zJ`rZ6{#?x;nLGL|vKvAeQ1yJoQiuqRQ+QiH5%VbvUnbqs{Nc%!h>W=iBWg*d3d4}F z@N)*f;-)X?h>c@gZYS7t!B%c0n5Mh~w2_@7XOfA}5g(6FVS*r_53O=7ezWCDKk0JW z-TFDnloI(k>$flh0E^henzX^C>~*^&hn(lLWNhPOI+1qFHkhVtE%1dAR`@3%Dij*? z_u57G7(-^5*5T5jbW@KZRnJdR-Cg$&^y|3zLESwWhYi{E2d8sb8bBk;19|%9jE?5$c^)j92Sp!yVpM#PY3AQ#c@E zOO5m*W#U}bbtp?VM5BJ{pAy)l6MdKhVIQ+V6vruq6UIFc07_3qoPXo3S-LDa1bVvq z(y56aQl!oylLsaA& z`%a00yo@n^HB_zS%jC1Y+b7ri__`-nvqWfNqt+dHgI~hgoV>ZNKspd9*kYkzDy{6c z`^&gX^wQZ&*DN_=@`{1M>uSq_7alEUim0ehOPA;3Ejw3LDb8=d<6|oAd*=Bwu41>= zc3H+jS5tZl!6jrBq03TBc-=PXlgEd;+jx@*c>sAbp_;U_Cv6(5oqwCvVH=gI{n7kdzvZyhBu9*o)N zM@l;&N}6G@G&cwDAU;CtHbC4!G{LBZ-!c|y=%hv^{DCr@NL)(UQh_|ynwr@$vZQs* zO!egg{k}k&navIawdPvjIA0>n(zmLd!@ibCDL`A@PJdM3AEow*(i>fXiy{2h(j(K5o;)OZ7ERf9|IPCRQX1Q6M|8@UNyZR)w zznG2i@66QI$CQ?nW1lPO$Xmu=n59t$zLianRT+1y zwQ+m>SbXpDcgxNChfE90pTEAa@q4Nqj5N{iufk#J#lopQu?=CuQWJy2Ly=N5CEw_6 zU=i!3K%$@^3pIt43zf&D{kk)JHGZ{om~&W|QwW*8`Bm6`nzZ2fk$+!%&3*E1<&^JH z(wk1Nw}L{QM<-K(_BsWNvUl4XL_vMc9Y2YdxP=`%64b6O>Gr$NxRsgq2dlnw)VU<~ zk??DyGDM{`np-46_{%X*Cx%*}n9`Hn2fs&;dj)>Iv-bbh*+A!Zq(2x=aF_UWkf3PO zhWQ*T`WE#DU9yJ5(fLlS;MJ;F%IAWl{O6Yhb9($3u@nK06({`DcN^1>uYT3T@sC;H|1P~&}PpzRSEB!Lqs{DM!AbH(?u z?6ER^|MSc9I_Mt<$a~&h&&_8L_6PkKU2?;(%8%VK>*?N(RO-(f%y8R>P(|@OMw*#+ z^cCw*X-s<3^hKv!vQ_bnv^@GoQaRjlv$QV7_@DnKMTz-)Rj|hHDE;^MV+7w=(bvpw z!!5qL&dfa5!-?!>ORL@HG?&q>tG=RnGT8i650@dS!@eZbdnqrF_8SjVzU3nb%?#2< z+~)s@dTY#vyOlA?S_aEissl%D8umY0r!~YKk&jLYSf8frylIfn*Zm<-!M^D^QvbU< zeorrc2FdIIhqzA&e{kt^#ItuPNB!N7Z}+ll%;}NO6LsUEU@V&(+?L7je77$1x9-E_ zsz_PvIXn(qji{AkL!x`D=bz6gn7w@*#`1R!XYh@6C;QA-f+jx*{@hRFr|ZXw04EfM zT>+cFA?#HSzYck#F9rR8Qkv}dIeM2d@_7g)w*>SP6fv?#2)N(GH`-7^6E6^V-Yso% z8<7h`+V-FqV@i(5)T7#jInfCIED9SZW7_m9g`pp>VT$=aqG%eizqnVJXY`wE@0Ks7&6fRL}P*_t*UqJhdmoD3%0^2DS{VKhBIc-K*;EF=Qy5 z+yjfL+w8hMxHpi_h{dT$){8ZKc6G&N_H~wXR)bT+;9(0pcz>1kYt?FVv+DdYtJp}H z_C=yCIxjWDH%!eeIogfP%`BPrzu=7)B}i1Ks339Ddh~bSHeMK^t+(HNpk~egXmcgV z8~t}jGbT9EoxK;M7z=g?K|29%s3po8E44n4(p+G5Ppq7r^^lmFAkVs4lSAA1X$|F#(jTYkem-ulVO zbx$b#^UYS8Voc|RakzhehyIg;qG0i^pvb-do&pS~d2sN{l$?K^14Q-Us7BvRO%wgA zF;vsJ;9zbJod409AXwz{Uj(`TL;I}{4t_0d1qQL{ke+=;-X1Dcx=*8KiNO$Hq8cC-1owM{Xd^TZ;d!;Abk9Xe}9Hg0~2w} zc9{G6-w)*trfRe|jCt$7KZ}$TB^OqFv&a0OM+fdJ3pk5W5B~r7%nVyK8Y8XV_utJa zMgdRs|9?%qAKEOH0+LhX;}#B)NRoW81cil#G~yMSR2LUr^6I+d7n^aY;Apxpq{yQB-h8cz(3B*$(a@a)m~8Q@;WYifQ;W0zy?JT zd+8QYU=T;n(+2eiWHz6LcplHI;fxyLlXpLO*$qesV3kGCm&_st)(MoPRAc?ZV_GkZ>|?if)PU7rz2I@RZjrbASOT{|NL~**zD9p8djJj6^)^{|vDCirydA zeBgHJywu0dus#X|F$;qdjnK8l_0h)_)v~&p`?)8cFNED!FAaXSx>v8p6@c_jDpT`d zzcUw*VX%)F%krtwtS{O-{v((TpTs}-Eh-*y+B&NY5#3(Whg^#XT{1-ro#v2D`)+sv z4+xX)OD5U-fihUwla1*5L{UcPyzmo9SzKybIkVbzI~u^=>Js@rOqrypqT*d=MvdK~ z3vV3b94M>T!DKzDi66D%tUN}7@bXB}b;95i3F256#|Cvt!Z{jat1@VZ&KR+hm6+O z6HbyOpgZ0!G+Ag$n@21GdQmpm2jNt{ca%Jg_gC9l(*S*!{Wt;oxvYEpKUo0azd#&u z%`HeTk)^-WMO&WKwZ6|D;K3bpiUK*cI1`drQ83EF>2M7q7cm*~*dCmxG8bn;3Dt{$ z`$d*U9>~6>dFiBOy#d{S5<}<;v$|AR^kB_3!)`m<;w4tf6-rJ|CdAxN;-@!Yh(TIn z#k#J~)}f?+xn#wu%^sEv5tQ%LiQRA^xqhLxshCiq_K2Zx-*3ZYZ%V5_BO-Dv*X4)+pw{ zVR?FCEpfb`Yi@_=<=4_m6%WlxAjy(6F2a3^5hZE*Dd493F1y=J2jNFH{Rh4DVQw$5 zC1?MDJ4oim>?aa6j%zlf)F}wLQe}*K&4Vu{^GjJ}q zft}|P6PPGn*;*zf4%nu*)-1+aU3Fc(GA*&B2`Z6?;v!Lq7FZcAC~zXZq|X&Y*~@(5 zZ^r4)UTmqMI1};6h8u>Y$uWe0omU~6e2y0M=mX{5RF?{@Z@I$ZtLY;8FqbqV)qM;f zh+<0@w&$z0y0^X2DoA-z-J8NOmw*7vhWrwUpOlV$@EM4PcsR1Y@25x3XTTOPQdRPD z@f1p_`lX}bJi(q`WO_z&-7i^9p;zl({JM_*v%V6B8B#@T$ZC5s*2J(FoIv$VUsUhL z+Wd^C_xS+cBXy1!lWz^yY9_0D59#gj1+qs~9qqEm%txgs2&B)?Xq+BcY>9OOQ72BY z;Yn%+>f4MYCDTw1PCuY)gQ^GzUcIDMXG%Mpqo5U``&@^CljkD^Q{`fIBGcBoVI5n}$(yKVp8u7bzNSjO?O`4ni8!S@o zGUA2VGR55(F!B)2aLr93b@@pc4*ps)ibt6mSq%BZKe`m>XvYOk3_cPB^eG1WV@{MK z(;{s4S`?dOy`@aKK>Xw;kUql`vK?4an1W%U9)S~f$GP4^skkOiF;!NmDixQ8l$Ni# zsYXE@Rg}S&mf-ScpIR-)hita#MavtdRtkw5N~z#u+P$>Z0NJlqPg8ce`PFgTIPUv9 zn>yU!d&iJCa9tCQY#s>w1SM25T0N&HRZI%o_6@IUBo`%=vdG3!_w?BCHEZE;V zRTP`y$s1-!^EFYt-UC<<3dZR6q^;4-Hp^zh*K&1O`|6vU>XxMgLcM+&hIqqdTVBbPAHl5CGF+-SIaiym&5|L}7I`5j+ccsA_@qBTjV^eIB6Ok3d* zFqBz!`NlL3_9>kaVdXaOX#GA`|&_DEd&ThwzMi z%vvD|CH^ZULr2T6?A`ZN3Gqo7LT=4qNTdq@OWf3zM~mx^Lj5GBsrz7@b1Gg&x1)mb zxQ{vCDAMl6XobrB&x&gg#Kw`h!Wp*X5-7Pddk$rCD(>YKtXb&gux0V4r(7aldcyNO zp<=Vr(2D#rJ-d@cB$Ly`N6yW@en5hL=rqHbC*g#fqEpS5Q#lVOyX#Qfr|qe{?WFuO z(ydJkjU7Rj>M%2CCYli3^2b;?-iF%gxQPQ>L>!C~UtH17;DQ%mI;fh_6`$T-p*19U z<<(m_At1<-om^!jU;LuxA4+(Fhq2aQ+ML~}^^11e^&2xK(f$=ZV`6(jKr?&KIy)n@ zUNl#Pb{>qYzLJP&v*9`jAB!OCJG0P52BW!hB?9G-#?q655XQ#WEbwNnRvm1GE%4QIZ#e~QaZi_I)=hf%+Np9u*(NvDjgx6Y! ztYfUX+2bffe9Pmrt_$wIE~d*bQ!|5SXNc2>br3OkcR`nlOZvk0lIYDiFf)rrxUX;s zGLZW1_~X8vq<_OY>BgSb;Edj|d=FOv$V(u^6?5xK?0YbQj2^r(UKSsd@7C0V81^tm z6Orh`G580ltsT>x%ID9tjaUb(7iF9*6@|uKlxYq`=5%)*SaPDZs1<3SslhqjeixXl zLe;zW*-6kzqG;TtrDEi^MhKnC7*58MO_)pkQK~w?7bsL%Cy=Ri|N5pE6SUKvA*(P> zm?{#dh#HSR=PWu7>KK)kE8SKtLY-07q#%9Jd+ipiInR;k1$r)Qy zW;(GKUY2E-OrC?I;#QNs&1Kv=43j+v^U%#I4Bzp+$yk)OTMDXu93>8y3Ei;ald0VN z!EH?nvAwi^A*xvL^J5@A2 zO1aI^;1kb-_R7Mu&Bvwu#EZ5gTovMxG>;a^Boyd#jjjA7v{RBh42^LekL8kFcHwwU zX9O-DF|CPx8kmbv;IbgXPDl`M>W^&2#|$%`D%bECO7m|U1L)$$@`0{Abs@{7$!x!6 z;co?ZQARWl2`qs%5zUCZBR>7F!8M97sruz@%T9>o7g4GCAb_p zg6+{fagT6zhcYWEdZIH#mZR!+V7%M=u%fH6IOVG2IXNEQYNQ-)3d-QkzgzF|%l5cd zV@ZPVtteyH1-vEnj^)gdKWTw?3eJy}#37R#vUEqlFHXG@WRw_A8{9)KXM*L}L{G~0 zox?F4e^@q#`LpDXGl-;jhqqVlhoOjO*r0a(>&7AOS{cZP1gi(QBnu3@4wf^~XS=jb z*&`b6g_j#9&h^h^TAJqnz$uY3rvBl4@yQxCu92dW#zr&b)L6%;X}GynTvv?Mz3v3o z=Q7XY?NG@{ln?j@UL&5HL=L@4)f$rEkb$iL*a@aJqOgS?zw$Ph)K3k9aD9Dj(VX+S zy!nijYc_XO;r91uDidQZHXat<9bFkMiVs{$TN0^>e%L0Reg#X=y5Tqy=qjs5j8S*A z2!oCe54K7LBpPnlNhgWQ)#d38AdsUkMkY{CwOzm9{iaOqjv9za^W$8PeYE`a*&0@g zZlV9hh@v>=aYk)=ZKAT+ilzR;G9YEElNs1!eh5bPxxa6p{@*YJ273^S{Gm}w;|reEt?mB33M@g?rqe zV3Thr7A+<5KeE1-%IOL|AVN{@pLbV9X!~EO3}nC( zymj^cKd}nMn54+soX`yYf52{0XK>AbS)hNg>5vkDEn~pf=G+@e|9MgK0-VlSxbWWx z_oyP$^+$z%RQ#Luaev3A^8h~2OFH%MgH7oG2rcp6uHyb1f6sjYMFaDatp9y5+hfq2 z5>sxCe?O}$Kq36Y1pd=-d}Q$aR2y@v|9dGQ?FC8~>}~7)`(V_FU~zvs-HAUU@xO<^ z1uVy38t*UA2b|dec^7_MX9eAs?mu^89kly@C1?J>bQ?oxK*G;1X}QS&`cS%S|0rfD0+JHDM-X~8Q`Z`%mzIA8F@O6R#sL#4vV5Q zAW_2;Y9iJlK}nBhOpkMiqN5;Y5Ej>#{htRPk00WSvvrUMVpY(^GV_6k%k}Yog;Jgq zP=Nqt{4vl9X(T?%2PYM>IPM)569`QxrFW8jLu&iH4~;Mb;$;`$dwO`;~~ z>$95#7sw+S97I;q>kVZ4B*o_r3D*vDq(4Fas7}ki5DuIG#_Cq}B+ndI?Vo;Jrf8}N z**czoi82aBxS>F53g0?Yz5&P>_6dZhES!B%7Wyoihu>hlA5CW=n70@`DmlbKPPr@&6r>m21e2$fYs zBV)KnvQbSH6;f=lBnB{XK#sVo#HOr%E8;SHtc0#A^3^bH+;9(Y+eB8fCB!@;#+8T* zpOC^cl2lmQO4ifpQXjcX-wm(yEl3%=Wx|p^|4O?Ai;0vmhE!Ti@?0RI{(Z2tno7+T z&`bRv?7ekRRpHw%Oi4*M(k)$*Qqm<2f+F1llF}tD-6=>(ODQGYu>k==klu7ix5W2s z4IVtL9Jxrdk1th`TFa!Xb+L)CJ5TwJt}y(abIf+hNRh23iOM!y+NJPkf;mX$MBa>}b?k4w-p}K-qFm@NTJag-z&V?A?T^pz$@yPNDbg0R77j=aGy+T4fmGBy3|&$~kFz)%(?~XsA8{{i z@L_eF$03yj)+6Y-|6nre_f zomCi%|Sh*EA4|E300otP`JQg@F@{m_dtRy^h zv{YIzr+5oz3tSI?Lf2^o8@=CKUlH?`Mb?C~Q?hwzhJKyx3F?m05IV=RhwRwn1#dPv}g07TUoari_;}M*^fCNcoGPrQOBKJ zR0C1JG^!zEoEB#Ldg{awqPj2BT7IN!#QCX(Y^4|^qe5APaqpf_8eSgNB2F(Yfk)gHY|4qe0kZYKHc zFfR@GE=S0TeMS1Jj$S|DyN; zR|_3erJ*zvK&{Np^dPijj7jh}4b`>hS?abqoTCwQ^au#)ucwMUN*u75?-n~Q1ruwa zcxH-nTS3d;Yif#2suEIiJ4o=E$acrpnClp;?Ph#SiwL!)`@!8yuK7wpH!LS)DTnPS zn^(C*ZNyli)tTgi`h6b3yKcQiw-N`F`^21lZz-!Yc2P0kYGeqU4yZ7peM59kP2HEy z+G(%|qi-vF2e5H9a`cDy)iI|m+}4w*F4VL?Fl0g0SXGju&=JoN>Dw@bZoUOH_ey*E zGYoT<)>GCSPrpxAW`ADI*G7ovbx2SCEF_Pbwx)|GGfottJS2(o;-jc}gI)L#h?qTE zp5#x;oWaY;zRq=~M2Qg$l#UDw!*)?F%|bnq!qal~sce^{ys^dD=Sl8l@3~*%skf28 zZ)h>R@io@G?^A4L980kKH64a|?S^ECGTvCvU5yZ&hO4KxcLxXC)>6r(wzZhn{#lmx!S!98#5#N?3Q<-?$c)I^;3c3h#~ z=(D)!i$TIK_>*VtDR>_MR~r}~hKng+wWw4mYK%IhKoEoB(P1P#&aGk*ud)xhitJ*u1~fho3~0#=Dy=NgfbATlP)FZj3L2G|^-rSV|y~+#^J1 z`+hp>bnZvVQ21ClG7k;z6`u6SwNRfDiC<_sL&9O~*!E>Aq;o>C_e4cysH#0nWYo`f zR8>SXIF&tdb+WU33!;DDsYq!(%(7UvIc%PLY5&92N@Api6AQzWx>8ootPM>r*$#V$ z`-!w2jBix1Mq86ESB&f1I`5F3;W8YO2SyG0zrarGOFkJ1LiWUmquZl2l1g!O9u3@i zr09>CMQ+l`LrA;KGZ=2wt5oFXc-JGj@Rhn>x*9TSuQAe;T=~|oHZQ$byiuofAk%}N zyK@v<(?`4%H%g?b(KZ&KW0ihQ$MyJw5!PSlW`D!DtlPkiQgh?Rw3i|X^Ii>euxVV(cmCPn&CefIOn?+-F{)~tbNeO#k?Ylzi$$hkk zt5+2+<>EbtDrYNK!x&e-l^MJ?o2McDN{6APmu4%!cUq8;m%GE9II*}|EQ}wD|1t)` ztFV%wFkrjA^mUgh(*a?`Vc@DE{vx|W^5|$pTzp=loJrt4S%AAp_i`A>R{-Yri#;nz zk-Cgsn0?#d4?fZuAkbv*v|mw;u{$sSd4JBqMhyfj#HC_ZG?;G2VH)T=jn{5T&)hEAeJ}7`YNO?FSH;ZxE7$}Ivk2IeX96q zS}*Bb47;XkZs0L$PGpTbsbPf#LurueHY)$VC{FF`UXMp`brE$VS1o|jXq;M$QCf?oL7;!JiPvp{fZ@;=B6XLdCwAJ@ zN7C0GVJ;z^d4=LNb-1(Qyv>f zNBNdW;c@>R%9GcZ;R@!NZ=dzEfBO)W6+$o3_WCp!~72qw~2xUzI5jAg>FCF@sx2{fM7o!oek`-2zeDmK_oErybl*c#uhIxyh%{uEn#&6`uv_i<14VOyqNoJ_F3GVzD2}fu_ zaaXU(CC7;c+mz)s!vGx}ZyPa&!Z1-I?>Fleo{W$AE)Osfg3FhW8QH;p_ZhDEtGYAO z6~vD^KwmY6AW!B*#M)N+y}2id(FhM-Oj34|)Cdh{Xbbc%GhN#jARmh{=cK##*iO07 zUCI1rS3r&Z5YJznRrSlP{!>Wa`H%d1p714SDg7-M(V#TC)lOOn^tBm!S7LpZoyyPc z!pJ}P5)ush|41)dADLH%fSs)~-CN&$5s>6S#8ZEG5-6CVq=&>c{Z zV2-IHTc_+^E5qu@6QVy+Xi!(!8rk+FiV54>{3mFsT|*qOG!I1e=^u31npVgI-ODIdpoCzMXlvgbOfci~cNEJ_t7G%ITg;%g&UF;PtMSy|eUMR{C-= z)FLwSW{qyT_=%c0M%z+IS4H@@G$~qz*XbBbDkbMKodc_{@t|j#m4(ziawYF$$VaTK z-t(Z9Dt44o@LAU*(6oyCKTCu<LSy$roQ0}$q?6A2KAo3bV_eN8 zjzpZqiX1BhHhgzpWSYat)_=Q>=+Z&F-B|^E-m=Sd#<|~AX=)%6>jf%o@Cs_JEuXKOXe9NI$P_a;pC_Uq2sfmKhh?@H` z-sf=ezUU}F!p<#9$@1<<3Cce8WI`~b?D~?fRj>V4{b*VupGh&1z1kcb-6lR15kpPk zUIbaz6-|QXJGR%8+7>U^$W0i~qPj!U43tmDS}5U&d9xitzg0->9EFCO43pyHeVD0t z8N}FpPdQ#bHs+9xw5Z&Bun~DnW3Dq>i)s1YoaL7d5IvVQQqO>8;7$aU#ok7XBuX~$ zHYkYU7bZIixs*1ftsU59!WS8$-z?I1RH|_&Fn-QR!>T2wu+o z<7s*7682i5^S}h7dcbj9p=fzt)D@Rmj|;}TrE?py#~-w?@1fq^PqsZkH-)rB(mxy1 zP&4O$t<@c;Z;`R2odJM5|t} z3%C_F2zC^-tJc$VZjvI{-x#Tcc~|IbWkOBpZoT;>F=}u>tSE+e1tvZTScBrQoK*!rhh2@)!0sC5v{IZAh3_gL#fH&PeVQ76>b%+%b8{ zLwD$KIMx&M)0T`71&j8>mL+Aea}>ca$+O7R@oTI${*FSoPF?$5GR}wbOr7k=1YL)4 z_adZAm5LOx+=oq~P(1HmKNWhoPP421v@5Ij-3e}9^9hmCJu9sAn6(kTI?qjYa-}K- z6~|`yj)3aekl23h`zIpnk=ELleTYGL+l}7CuP?dZRei;$hU|K}JxY8nnj(r(wHL*` z(~zmF*3~0}deyi%JfMlmQy5bju0*R!n(Ba1h05|iTED_Jd zTb}t~IL(Y2Ir$+@q_YL>x)3gAm&7n)yPzc_UB|`kx&M0EF~6Dr{dT^o+KxyKyA$p^ zRea3%bq-9zdF-DluEez26*yX2;#0iGe{2)+xD=Z1yz$(#RG6-P+S5`nvc`^VW|DGl zywbYd0TVIU<1dsAsP>Pa$U#2C;WGy4t`Frz$R&jT7h23!h14@iCnhX9@H3BX*|8%APL%e=y}rT^JP3H1bVps+z>$vu(>Fk0$9~N;p6;Nq7MQ{)*y_ct*@_ zT*ye$1Lbgrm)wV!6oVcYO~`vHe9A?@gyt`N-$nu>xo_)dPG5}fhA>|=6V`_Gr2pf` zql^k~IGGt6U7Pk7fdw#Wo(zDxpI#gPcA5MQb~6S6oLo})yT#v&1z!;O^Zeiu`Ip56O@&ejT7Ei6XIPnYaJw*v^S zXx`yu|Mv<0zurN%>3T=gqxZuQNWSBLQ`7(R)cYen#n+t|?v`g<{0YaVQ1K*+FxETY z9E2py10(ru%P4E5#ZfsbSX7F_Czw$y$my|5c<~3$hb^{Pq#o%oL%-{j>3n;5u~c_p z7fdtsnG}O1%T0sIV+Geqsw6t?Y4acccKRt`0>jMj18FV9{sO6cPNt+ufejwp917n$T#m z%MA>Cqw_8yAHe+<1-`lD)x9}hz?3$T{}+fj>$zPHoHlvM&$SkoLaDM^1$#8#0e8pb z?CzznFeiD>6C1rhs~uU!Xf{ajMas4nmgw;pz<_-NpQIV)6zonB$Bmb2h5+U^entCl zUwz*MpJY+|ixp4uYjCi9cbx&>Aa=2}mjk5Lff&fgJghUnv#${8xtNtS$25mizaMP! z9$l2v~ywD|d@>LUH$?nijJeixk>8R3X1I&1R zFi6Vm9W)Ss5&3)bphk<#kP{)(LEHa9P>VAS2+dz$PASN!?ZCo+6$Bb_oCt8bK=pbZ z4O*uA7d>IFSKvt~ctx*DX?*mZ7M`aou1D}K&l&KR34a;6AHM-O|8KQ(1!b*}>~-zI zl;hWEK%BDv<0g@AojnC!z?-*#iwgtkpdS;>ADXR{zA48G?^##K(L4XgR&bci>h5+|HUShUY|CC^0 zsP6~q6W^V18ZloyK{hI{9qwIw+J=~|vpXpQGm?`F)xQxrG6pVY$;@4a)jIEs+XWlh zagduJxLk8L+?d>awi}bE^+%*h3kYFzzsqY!+a~A(vJ>(m9rKO*B`70Cm)VM;I74r_ z&-*)yll9aw-^$^p`Dy@dk@s5r1Q*Vis`xGZ#sKCu-1152t3l z$&KI;1_Ph7?=LFGD1E51Wtr)iQ!I}uS`id@$@t$$!e>Fnj(W@G!@oFBsPS-D#v-L6 zIZ1Wfk<@uZ8cjhbmbp+>f@)dK`Lom-nkN8!Hnzf`F$RP{E}pC5L_e(k0RnT~J_5I*k{& zCuE}7t|<1)$A4$PO1&9oaKzv>bv7jz{xP_nQV%E1C7JxLdMvwo7((a)ZFW|lPqe_r-jkfP=gnZA+>$(^I? z7zjVyhV+D2Lug~r9lD(=C}6bY;VvU00X}CXO7f(n74#U=hjrMb$%Pny#8fOuj2(2t zuZIazBI~(ZpiiPt-bSW?NQtLdZ5vN@9I4T1g1zMeWvSbit_Plcs_Y65sb3+uB(#q1 zVTk+ybI?M2=XoF?Cwc1b-3h|rqmLfmx$kkv!F-QEFS2QPbtkRz*}F1~1rLed-Wpey z?D6m-N}-^yeZlu1WP8Z7y)bULigu_X zp9Gt~BtG`7XX4{jSrLc-f?R|(+>#?-2)W57nI%&y zot!fp*FsXV0V$T6Tgdo>Mu~&Us01T663eqv*;OetGoQ2(FbqSGsy*ILvBXp|LPEAYf;e zQ?J9Qavz{Gb|0+{qYQ8mPhc5fXUBb4re!QGR|4f5;cod@9K_0!S(9crFstt@X}3N2 zd34g7Scu$RS0mt97hlEDJvl;DGg1Eo{z3V@;JSw;F2J6M$ z-XgTObBLbbqNAyzf18TiHIfgie|v?vbSwMy4Ox&Mqy7kFOo2?*$^8oft9I(~;yWSMd*52AS!bD2p)#$P-;rFxxI_o;(( z_{(6A-U?j@!DuG5MOg!lw^7dNuvfuQ=o#f{mQud?hleAnRw|TueG{c~lU_Jfk|JzU z+6k4@FkYn*fp>w@;fGr-OOo{a zM(Jqs_CVrGyXW>{AQJ{8hjH|*jJ@AY;^Er3nn6OXf)7`zs?gS|g${&t%QC#5kVn0_ zjDEN^`joId^OPcLu&)U1G7EcjA1x)5hKv2tb z`KxC}QG&D&zlHGzm_@mJKSmYsKtFJI=yG|W^D0hiMarLW<@(#eA6D}Yc){SjoHs7x zGOow;ntfg*!Qb^D%OZ>O$i#$!)z&=djk=pLSkF|(9*l{exrCK~7pys4H9;Zn66@=-B&`hPe`~{eCl5)~v#Bm-=PDX{|8Lfy8 zN6U~g2Q1-+t={JziL8?|ktA`$sS}YDu_!lA<9ih0%zz(`iR>tayKzuKJ$~;`gP{BY zXC!!WV-L6&W4(R*L`pcGml07U-=dk1jJrsCvUFc7or{j!P;K+TQ%B^mC-U{cOE$CN zPE@W6RAS$jYCUF#Whpa=b&NfSbE;dOe3%3FkLZV)RM?WevAo9l$^y4OdLNC!>l&`c zP=D@9we1ImiST^=Vm6`x8}T}zUoS66u3lprOh|-j;@@De@N&;BA@1C(feB!_=8Od^~iI0Ad46`XFd#p zPelzzj6X-e8XK!b3X%3_&BpdW^n)WDE^r4LpkTL55)PHwnIZ?qCCEq9)6@xx;@*P6 z)wgb%-HcI~f7m@>pMDZn_yr>IZb{(9zO$U53O5|@B%Uno6a9|V)x((MPzIC47a?es zV-K+OUSw@^(}t*%S3DzlGExZ1*G^TC6rp9Z)UlAeNkg1KbyeARCrdhiex)oBtyeXJev@VZO)wJ2`;t(8r-%h82JMje1@DU53CyN*ENAfNY+ZI z#wqH(qCe%R8H(R*Z}F$}0R(BMl=q7V=nK4tN=Du?H?-~Qotu*gM92rmJO(2st7zOL z%ey9-SWKin2S(|d7bk7{LPPhg#KJ-})(JQSG!AyiQpgbbRT~dJ92}Z_NRDnXR+BrE zbEo#Yy1uGr)Ej-!QaP;gB#}cE(Orm#vfUZI)t5*yRaFW{Vf8}D^DEk^!(yCIZ}x=k z{QZ1>vwgO)l_E5)@W_EKXTK8eHjh3Dxb%K$+&;3Lu`T<9R7bk?UJb!Azb7C6k~OFU z=(!ps$)9j=7)UI>U7cZ82!e}XzbmjZkW}lEd z`IBvFwmsS?O2y7uP#5;3!kp2WAv(-%i-Wz6PUBX6Y7JRuH8vImK`u=)QFE60{0h)zF?pM5djUJjCMGMwgRsaUF|5-Xse5bC`PHxcnAa+P$HAu!TWK7 zvaacXs*28Jb}V|hA4tSb5V~1&T2f3LTpvSCO#9aPHJ?&1ym*Sb%bE?8&vz(Ki%^1I zUMDPF*`URIJ6KIQDKt^$FY>!dv>^K4q^^7TQ_iT26KS1b`231X-@+cW=&Jr1p2w`# zn7?Kr;1y@{`+^Z zRdO^3<-Z4Kr$(*e#2jUm3$=~mr77W_FFO__^k?EVDL>qEc#xc~xsk~*rYjubnLFs* z{CF=Hlz08u7-=MGNJ4oX&c!}^SE4}q{(v4QX1U&@VLUCf<}aVqPE^8Mc3AHL2eK^* z@!Ci7vVc{rWXrnQmDwnW(ekQ@Zq}R=8SZexHttCDqxkAoV`Q~ZTMuAJk{^9QSi=}v zWL(RbjQDYLaw=Np%T!})Q=Yjjzb%1MXWD6{x#TZ4DHmY&;e=W$<5^0X!NT z96%-zBx+IrJ*Wj(eWlVOe_+wSbTG9rPB^oOe-M^fi6p>KRO~YS_iz7ie&8Q>3CMqU z`TqkW_J7O4;s1yL{r_V&7=u4YI)2qO5QRk`s%(RK7XZz8+IW3NJFp|ByvT0{gh*J3 ztH^^tAR-Wag8>3erVW882mgeL=jT1v0sFHmJD?SE1ki$(+nb-^(}1uvioaT{es=+M zMF3jaql*za1dKg(ZR#DM=td!TXahE@-&D2bg5YdymwTcfn03D)d{gNw?9x6qW(Xo` z*}Ew~!;C`dzSuN)JsRUnaAvgD#^3`4wktV*`J4RGM#@3wKn}lvD6HrL+OOvz>=2kJ zFLc@tYt}9JK72#P+eL-vpz^a39zCAji6lO;Fl75%#RBX)Rl@H(xd@%WLIfB6@wrE8 z(T`Q%X*n+QmN|gWz*wY((ZXBzA2`Zo_|mE` zt!4%#=Qb1;7HT5TIgf}I&4ByflC43+_M_5~T~~_DN)0I*csoP3KvlH-eYg`Ic^B9O zIGJ9S;80O(|0<@a1AtTr9|~ia^2dM5uU9d>%kV}jrPEF(#6s#GvaUcpi#K4}wrfMg z#>9@lNY%#*yqt&7sGh$3{DVKa_yOb<(vW*9PE9e5s5VJEd_8BEmkwH zaPD8xa%n>^SQwiFln_sW9mSmOvzYc7ipNGW=iu4V6~579_3J=m(s!NE79nk015)YP z_rd4DPNCLfDBZ5PwG0+t`|F3pSAlgXy}K)}9{_cAbAYuX@>`QG+`!E9xk-g1k#vT| zf;T@>E$jNgB_%=|&F9&*MSUqJP~#r!G%0HQ#)b)M6BYvxJAKKUD}(MACS>@NAvUL=wZDCZL~E(~yH#=V?u1t6Qg@NDogw zc)I)xh3Spw>w77^FdXzyx7+I<7Gd9UR8V7>CtWq)t=7@!+d>we1IStOI%cin{A>VWIJ2q26CHPnuni07MSbm zPzxEe;#nYBa)ki*;i4rd${0^9ov|q<#>-&vtM1-MI}FF*Y}}w7Fpua2))3v>A@v`W zs-{%=9IoWniuzHtPCwr9@t=I8VWb(?dQI^7ku%K?2am~J0npoc5JOfGFsh+wLUn=vj5! zsp@q~un_4^vgpz*@E#zrAf$o1663y_&U^-^VNRTAlr-2@UZz>=SbN(s$>W~q+;rJjq&M`($B-9-$HG+$OPt=yA z;}@ATdx9F}DRH@k#us6%yfPLP}D%svlX*J$!DW)O5IEPU&B_sxP76GQY*inV|R+h!hv z6{D$tVGD6s)GL|bp<&7V%xMCq_#a&`<6O8Y+(6Tw4IsY)pHi+guCwT$(I}k9j0O%L zM4i@Jq~0Bwv4IE@9({~BaYE`gNcq{FHhqo?3AVJugBS*$r9qhoOt<|XOKN+5)0u>j z80Evpi$(ZUXtI1DMpVL)Iq3oT&BIprvbd0Kk*0xj_fug_USlLh!swY8T6q~KX*hKM zoXbQ_6&lsd>aKV4xojbRwEfBL4-YqM?wLuh;#MQ!kiv}N(?dvV6Yr_3`7BtDVxmbm z6lEtqtY0st|73m-znTO-$TW*{1`=L6k3Qe^m@s-CY6qW)!xC$bM-uqxdm_x+g#Ga)9rN2LJZpk@ z;>Taq6Tu_njGz^NnUB8C?8hN(Jb_b^qTM}JiQ@ZGzn>jZzD{dNITS7nAq-FbmYcIf zRqu9J(DQ9e#3p(~TxE4vQTjf}7}QEG9AP>_%Su8)>Nm~hNzY&WOk zz=jO@*F?R(^O7QKohxl8F(WI@56PeUwHq6z(sb6u& zYV%21{oBK9pChi>`;ZUgdr+iq?Edui&v=Z!45J7?7;{BoF8V~61d}2hncW<|(2DIf;+zBEN78aT;uVSph(*}TP=6W(+f4?G2umP8 zy8c)GLIrbCuh29VB(vEBAlxS`xu0^HdsV&7+X*xK@V4^zp`=|KgS2HLIVEdv>f^oA zr=w(*5F8{N4C3d{Vyt>3_90a5)S2kHfRPSPobtzgjg&O}VK4fjEurJEC6fqCLCAQK zO`1@WJzG*w+T|yqI%eT=0>R5aiOliE8@TS~k z+4!x*=}RGI%oQ8)`M=+4)TqkII3oe#U$rko(7H;#*r(jN_u&IQPW_UBIoIynraIG4BQ`E&yq{!t zLgouxh>$DIkMz3X;dM%m^;}pH)ZQ?|YbIWP4Ar5oUCXJAw^@$hQ{odMlLyuR(G`BvqiErndUFzj`=xp&#AP2V?%cD{Iq9I*-qyYCJ32DKU# z{ZY{bP;-q+iiBi|VIdm>?$oM5)A3&EsW>VaO=eRE%7TS~snAo}8m+xV&VTP!$uH^8Yv=i4sf za&4#{sg5YHG4ZuaIIA6j=;2cC@gCQn!K@F#EB_r`02&@N3^CaTDOhb{)6ZPjTu4q% z0$0zlS;BjBW&1jTVbv?`MB}nd)~s)fSPcGAYPFMsO97iSVlKUoYp7DiNbWOS`5$BI z3ECy!PjMWnrg_%;Ws}lY^oz=zt&Qsa5QJumN4{)v6NLI4-?dz4@7m54zZZY^Q_c3z zmqu%`w>T!;+C5}{`nr#_;1!gKoxGY#QJqn+1Rufu`Gz}6NQm~vVKe7;YJ|>XZNZX{ zmg=kJM-D^(!K*>I(gLY4O}m|+Y@WVI`v#gWKA+%JE>oGSa^KIOCsebWr>=$4o2)9x z(PX_q(~wN+$HWejKzGb))@l)&I;dW0S>`i&V{tDn#pEcxYbRomdAD7$W5K1r zWbz<3B0=fnjls)%>wG<~6II6_{}$UoOQg|~-ZALy-N+%apDEl_Q%olKjLL=7kQ0Wb zkBAER61C8Ek;GK(6!D(FrsEztMa29`zmebM>b3haN@rAf@f%m@5b>C1~}1b5q0%C zLt}+>t+V;v2Wpmico03_Qhx&C-X=7EjrG{vhx1wbPWraihRD!c#5%mXSEPm^7hgp~ zH%_j4HL@6Ht8{>?&2Lw$k|E!OugR#b;hipC5mE znib{HgsG6az)%d3N)n6n7!l2@09knB3_NXg=C)NJS=Il*I# zYUq`=ouLGx-MfEBM?imc-YI)RqE1^(AbcK5{I!!r&PxS(U?l`a5^gEO5T$V?Udf#> zRcw`Gu`p41Z3MmQTvFTu3}wwdi3D&v-nvCxd|d-t9D>%Sac&Rax$KM-S3U2I5cW@e z#3LYFhmOV#sm|HT^UdP?O1#nQp_jWZ5FvXH8ySiswDBk3H5v+?;7KN;vx_^jA}~z{ zgkfRPmOjF3FaNVsu#K3HKMvy>(}Mir&kH7?)?@=~)b!~~=a^@Kyu3^AOICP`q>x*+ zUJ~>kei_T_Db9-)W--Lg`_hRmp4pxO=M&rfA55>$uwo5M8H1@?wY?NWu&~To@d{RIx*wr56#Yg_*7_oG8T8KFoilx1lv z3;g-kkIz<9>3#ArG9BX$XH`s;jU{!*G!B$^mOl3OI-LajOOF!@YK@$Z(=NQFf8mOV zM5<`@QhZEey?NpNNU8(L zWAE5{ynz$0*B8s88txP`gXeiGd~Aq0kZWz_$bxS}hGHP6*O@x-GOTC2B!8=QV-4wk zy5Wd%b9S-Bq6qw}&Lm`WIJ7Xvh`VstLpoUnR{E)%FE~|6DXHN(B*lO|J!h** zU|`yX_Wkc~@}(#auiZZ&QO0+^a)wGBt0#_gx`|IY{(j%_9byBIJs$6Uo+MHMU7`nZku$APZwx;uy)OY zSIzQ(4y`Ms=hD#uP>s9Z(T9}wjHiF_&i5fm!*#Nta}rlh9zA+}RMdF5 zK^iS3Y_Rut~$lFF+=y4JbgM4lyrJe4v1S)m7u2P%1+9ZzR7Y=QAtrj{C@>qxf*E_cX z4=ipMoXcXZ=O5F79zf!vnSlPGDt4u1xXMkCO=4fxyxAj!0MHyV6ECdmAwEX^a!14* zD!i^z4DtQUVEJl5`_VTCpB}rJAt%a%S>pP6kEx_vd=`N5C%au$!v;~bAzgp)tE^)-cw^Vht#VfPPQ&&5yBI5RT zno+3h+_+G?O}Cofs9vif>NhT|uFnL1DvBr~ERf&@t%QZI%U`{%tG^u%;pn%k$vcfxRV7H=AMN`6H-qqpiaRHyGbJ--m$VK zF!mxw{*=k|<_P-PjtJSUQ{(chT~U7k>Hzp;Q`}iLvHlY#%#0X4*y3!{4=(qZOMeEv zG98j|_G16TdL#=HLC#F=d!nsoCl0@_$fRV6u#UAhQb>@yECeb|tCG;8_2T+p~Xs zNh|vR2`XY1p6DN<4p$mDmN30n`}DU^^}XjPhj0f z8!aHmaXO*sHvMz#zi%Mm=Kb&8KwUA)p_awhcY*FZ{p0HKxT;~TtsefL%r4#!ftXO43U7Qzj@4k(a1$_Co|m#W0w-$Fu>TPL!Dto;o1IuPr}%uWS$A0jUxQ0N%VkE)Zdgly zp}Tcz>33=g5ZN=BVn6GwY5ky=N_?v*&%SZ!%zSj_oc)u$Zy&EJv7lOa z^(g!H3h1;U(;x%wAjR(@1fzn>{M_J$0 z;}!rgeS55W>vDbat+y-!s=Mwpz+8CM!FeUwpq4c||8xEraVgQ`b?n7{%VAkz- zoJaKopb^h%ZD}aghr$24S`)yRKxx{L;iO$;gh#^&Y6JgUUJORi8#&OaFnD|pNQdSz za+%ivQ|zZK9?D9B?wEQ(!JwimlP~S^-+{C+ zUaB;WFTh)Y`vUnHp|sd>4tDc0#hyIrkR=YObhz9D@t@NpSY`sV4xMu~WP6G{T1;}s z+@`!U3@Q4DGp*M%fW4=bMccLyi67@d_R6%e1>DEoy{qB9vZtR|h7T!8rtw?Aq4~3x zv!yo??)r{ZH%wRxlooZfPD)+%V0T8A^zUbPw)I|GxZZVJZiNqBIPVdl^gTB|l(X~O zR5FC-5G`4XER*U%vu`i5m$BAk{fI3y9g%r0cOS1^%$8(del2uI=xI%V@W=cZuLytY zR187wrPK^b)98j#j|v`bgx(pcQ04a(&f7yyx~ueWO^m@#Yg8=cq0_KmH>Ksc02tre zWOl4y7y4ZmTFWk?h@U?yanMPJ^4t~}UNU5PhGKF!eDxEY8PGEP`Td(l@KMiH019Q_ z#&9yfqx;oCuVo~xL-j*|h5mh>KjGNj0B#L|dcvfKw*h_xxNA5mLT7W&<`0P|U&}b6 z?y4<`XqY~_czLt-vgN#+(|4I{A?z`U_hCY|be!B2*jxTIynyu7`g3ki0T0jTtjtuyt8PQ@HQsE}!mN`r z%jW=0ckd>#2eBtQ$z`$Fg?&iGPp4uD@2W^1<99wGCaLl89SYp5@I5I7tLSbg+i4Z_ z4(B~rDV!B%Eaund?6B3%I|gH1NFZFHxObAxON5k3Eq_C<7ibu-sE4B4@29k0ti6sh03>_x^*?1*_!A!t0IZo@TE@ky89d`C{ zKGV{;Fu?S}E@1aQu~39zEO*rK;LVeKJ<`P|LYl#HB6(~d4qAVHZe8A|x;>zh3cFqR zyIBVVAxVr6Jjrj8@dFXD1Q$P5_^?0CtT}HzNIc>djnm#aNWcAFas8AOR@;*BAt=`_ zXZHiE;5IVqs-Vb|Rdv;-HB`rrD<$=?n5PSRRIqx6?iZ~!Ce>?S#w^8By}!PUM9xtL z-2GeLLWk$KyL&hGVlf<1Xn7f|+~n031lhufyef{eCT0SZpSYG2It$QaQhGHO(WTU5g~0cYOgzvv(RqGqMd0w1E*6?%>b`jv{kc)tOf?!~IS?T@nOH zj|X8!12?ZnOH3EdEx9gb&)xJguJ#{8JujxOyq3llni!k_%~`fPwGl?DnPZtgkQ4q) zO7^>A!k33lVP-g_rFds~CS^MgXUvkyNWwoWT+O|jVUGlUC>%k8oIX9q-SF9@hpxdN zN8}Oac9h6~dZw)n4VE8T=pQypAoI+cYXcwX|l5AJ;AEj zd0HLd7D@^e3Z#-FB%DtqR=eFU4c^AGWS+-(uh1Pl15Ok{%QUW{S*h;HkEsbfJo_5W zlN6hww;YBSTe`7v;QP2xyPkcArGVo0>{d;fU2e-^<=}kn?UjNz-WpQjl;_h!pY#84*H#o=b{K^gbU&{ zEwrCH*`7qNIZz&~R$NI(dV!J$9(8}I!|y!QZ}G|Q?Xvg{U2^C;W_HbX2+OETBJC{XAeATp?HrV1ZU1~qFH3oupg`pQ=igRrw;1mMT_J@4MEW6kv z(Jj*iTSo|E3eU6KK4xwTNM?HQ>}jmKU0>|rC9OQtyTN&+XSue1)n)N(slY%kH70Nh zj=rCnkRD_|3abb?YS@kO*)ipU2BKZLqb%?j??ZceNJ9n<2WOf*cnv;sdF?u9lLcho zY-Zoo`TZ1VG8b9CxjAb!^bJVG6Tdl#2_+R@ONr2>eSg++gDBDb;}_EdXdR6No>b2YN%glYJyv3KMOGD1D> z(e1oFkX+#>O7zs7U?x0Ky*&^@qal%r~CxxX*(J(5ZM*1YRlW+oMp{qS=^ z)!+S23U;0c5@+^8;ksuGttZb~wQpX5@KegQn(%|4)UXOia4;s2rE5De zGOi5)f04KaR$Lq+HaASMvHDZJ#V+~Js9Qn~Rw6HV7e}*~NmoZ^1mH6f9Yp3eKzSiJ ztE~rQtp`ikeyLIqM6-7CV#I`1a`)Y+r&*|R8u9Yc(sw(%9++Q3Gz4Uc1lV4(LJ`Zh^ zllNGgw3(Rhcrt_c8_<(RW$Vv2x7WxS6ZD-(BP1qOpyoe@#su;PEps83mA)^#T$fAN zGE%p7e->d*W3{0>KeVj}>Qg_k!FsfpDzE}JIhw4Mj{A}0197$b`=VAkunP9z2V+a| z05&z3lo2Z;_$Y)OMD!&%BS^v|o zPzUfM>8Fo{B-j4aQR9)|fbZ~^NQcE=|K}vX1535KoPzRyk0DEg-kUns;-5=_eIfzW z&dU0C4gC9K1K@#d3L7nDhf0`__30n@4GgUNCICEBGttiOlJT)GHk z{I=AcU=Lyg?p~Wa{GZHhHf^~~rcB$!YBA*a!XhQQEdRL2V#ER)J@}3Xzua;EUh@Nn zU)ed2?F!#_%ut*zDKdG;;)St`ERaTbmf!5_#himKDnOoo;taVAvW;wu;c;I3KQS?0c_ZSnVfzd z-gZ>uQV1E07XkYJ8!+qMOlE=7H2Ge8cECz?;Nvnr1Y@4mZ{$V@D#?KsmSzre84jpD zgP64;?zKx<2l=er?A_1!$>xv+)C z%deK8-SR&zR`n?{jgP$A_47H~UO4w8zgeQXSuzf#NG=?Jn>e596_bUuqflLaaBD6V z;Vaq)TlGB|7~=a9l;+4u9rP;uOOM;13*s@J2B2Li?ohb7-t+ULO#7&K7#HJh=IPm}jTgnhWrLL3 zo-ClE*9@dEUs#)vOnyVg$dWM6x7)qj>(*W>KXO_y<@{E0vsD4})CXL!rF~$kcG)A) z5+Ikk4;q7`Cw?bSD!k^lkrC_@c`wfVZqKSc$zi<%hbb~;$dA^m!B$u;OyWLlaX%oK zX1{sQXOB1g0>j1&FoC>IN)3J1(kx{g09R`+RQzfQAU)Zg>mutyoF4s3MDN9tX@Bj4 z2@*!+BG59_7~^={V~Ra@6s26L;Bmi)ppFIau8wa_YkZ?A#QF~HQ=dOM%q`s%HiL3` z0d&-ljmF_jfMC0okr^!IO&Hd6_#P>=iRcjE zA)$Vpauluvs#7gDClyEN1N$Y$z&sDuH35zIqp%u0y2c+t_$rHYU7o~Zn+mFuNwVHe zhPrh$$Y(+OwYc>T5Ee`qI)c4SS5Zd@skdZxX;ShI=#Y)-o;< zTAK{Gql#1I* z4NgiHZm$>orUwmV&jD6qHqv_6+QW4xjG z+2=5P1z67p1%RR!hP43$PvD(!XPO{tw#mb}Zt`#`QMWR#$2wf3MC@-OR?ZDa;xjA1}6~8_%6j^0Hq3;{w^t%`Yd_6l7 zQ-9Gn7Y!pmyMXl7L-TqO2F3*J&%*E&Sm`UIUt=*8VQ@@~;A{_DTb&{3`yZJ0^y4RB z&XEI8TB`LQh+B#cVH#YHrHtJdgy|WJVl73t^)Sb2UpaCcxGx36z=txn?*PYg-#oe) z@$>C{B2EREOq@QON>2=ZP5==RS7skKV_kI5%^^m-O#IwFo^9{^-a z>n{L&0J>wwc@>V4zuG2Pjsnie%k?U_K$KpqNzaFVzOPDqk7$<{u7(zZwxVhTrNhm< zuD1>S0B!MWLLzq)_UnQ+a)u(%ST($N4q@+nbuevd8om6kG`k2!;eg zz@rd1x_GoI$HXyAYlvPH;VX*VLu4*A^N~>LB8-cxk=v^g)$6aSC-M*XG|O&1e!MVL z8t;S8!FX_dklE0l_Of=rp?@si8gJWX$^f*hHIxD=B*uYV;W7vXq7J(1`<+xbm5~sx zw^RAfqXvR}oGs^2WfFLsJ|sl!`ybQ{fNcK6Arp09HL%6v-o@OWufPXkth7|_v%LSO zz3+~v`VZqZPB^8E6ortTy`79GBQx2uj&N*_LsmwJY|7>sWu@$6X3HK&$j&;(At8H( z`#p4DzwZ6-zV7|=`p+>wpU*Qs&-1*W=kq+t0PupXDVZ1bYZpqVxZ2-gV6@gt>$%ik z-z$X<3cG6+NoUIYtQ2!*xA8~Tp{83J`LYgeZqgzHih2w`QtGCBe*wb}Sl%UYdz20e|ml>#i zA+XELZjt-ROIU`;MuCA{-$|ZaSLL~YH(@~P%@kIkD-Sr)J6y$e0j50%Vp-YTKj^SQ z(YpX-Bq=Ihpvv2!((YjCO2;bU|NF}!aBED(fxk`yw-K2ZZK&yy1XHVL8=?+lX=Yw)P^YY#O?&bH1SKFc+b7DH)S zQy5U_bT8D+-ctS^dw(b<<8&tv0D$+~g*tv6&z$+zr$N`rsozrH&g_wM`N3FZQz+2Z z)-2u6uW@M?z(rbgAs+omvuQrZqqaqgoI3clIzMgFPiGsN2^Pcj^#l1$?QeV!`?|=X zeBSA=JuBKIIA8(;yuQ9wj#C&zkPY*rrC$O4wJ8%1+(zZ;yu|s>Q4lo!E6$#(Jw8Q5{b!C zawfnN=w3227W-C;4x;Et?cOM;lJz^GME{pA_rBI$p{rIkJR2_dy+QbCCvC&~jS@2m z*ReJD2YlaIU{l1IxTZn~74)&#XU)z6O>Ik)^rWTG$|P6l(j_79km`+2VFu}i=)fL* z`l!BFE8P5CG-cA1I5!`E9pR_nJ=kpYvAJReo`B&>t1*2 z4~`f4QXceIO?#~Ztr3@oZw|FeIA9g^KQ~Qs>|G0EKCNmzDIDhdo3Aor#htfN?S)f zM*w-@086}Bx)qUuuZQ9(4bzB`A^H7sX_01Kg6snwH}%{=;>WkFTp#|<8+_fw84;4l zN@^_yJPJ&^AXkOI|ikv^iJ~%s^XE^g% z)FKF6f1BK^(M_qpnk?|*olxs^LVT*L!Ch|vOz6>DPA(h#iS4dXMQO3#?(Ee=WC=<# z#YIbtofYok^x+mV_(9P&0D5#YcR(qNRD!Dvu67AMnhADZSDo*oPDvt3Cyr2UUz>aY zJawlU{b@K`D2>2kG#&1Yg*@-$O}yb%S22rE)CZWjLSKXkyV9%0%XRqk_!8SKhXN(c zRtff7RlRi1zP=G|egF9s;m{{*=FNeNN}p~{)W2!rP#VX4D{4l zKK@mI)fv$hNt^Eh>Y{jaAOEN{odn7BHE^5+5D3}+ZhZen0{wbJ_JIVdi8HQwsrkcd z*Bdb~4OP9p$-tAr(f%*h-0aZXs=;3mE8rTStgUJzIfqXcjkj%!`0(#sGkD#!kx=ub zmzBBGj_Xc@r%gLqAXuADGP_hTb^Y$-r{CE2qM+fY-}WXXM2aVDKYZV9d){{uqB{`e zErnFoCGGk%w>3Ldh%Q4<-RU*~RM=F&(J9AatFz8^oonFF_L^UUFUy;VkEPVg5u=mx z{%$%xqHA{p@Kr?hUbJo+HvnKKzf)=%P0o7nq0-eFItm(!=x-1qKIEJ0AKM{C4i@lt z_Y+dLl3zFWu=glp&YEQW^w9 zBBF@A69~a@H$j;MQR$cvl%U=NdX=JjFG@U}cRRR- zxeN>@)DFysALuiL-YOm2ewch!M+5dTJ||&K)H97vpLcwKMSnYZQ)Il^{2P(w9y<&v zP2{;+-{oN?)z@#aQwZT7Cv2>2bbVphvO?7R`n{m)H8Z?B(RH~!de`L5hp@4aX{gyB zMJyIOeHeB3S~4+Ox+0Jt`e{pQyVtI}faYiK^x&^N|2&iVv|;j~Sf-d7&2cNvaPIBI zR3W|*_<1&Sa9V#WB#Bz14q^s*G3oz%Xzh&g2~phDb3DP{5^VHR5vkYy77m(@Vpv=fuC3Sw`oo8&dYQJyVU-^KnS;l66Ezyfi`hC6wQl zQ)h)$2_nP5-uh5KnL>}R($;5z41XBWi?qd!n{9aw;?}6RE!vp_qL=UC#!28IbiUdt zkUxXJPEi@+tXe9vxB(mZ%D%48Ha5E9=6oyI%1xYjyzC7<-z~sln_+bE*XM7lJ8K@) zNyo!?wmRuS1HvmMvl3l`l#0*5-9KRGL}E!y}evr9rsRE${_pgWs8GJXv$X##0w7r~93H-XS?GEGrbJrE8-ohQQL&q7ERXYC{zY zW{c-)H5-F-Gx~J{HO!e{qJ!(?3+tVU(^(eXPHtqa0)1#2Tsh;#7en6ylJUxwT%dudP*0ua>+IgE_!vrR1VHfo4zG{i+Sy9zgH2hw>APoi{~M3)nl54=_^YwB`d{1i z@!he7(1T^4OU$Z`)!IaPm)S@y+@7Jm*yut+^TCsp*4l`keJkqxWMdi-XvJqyu-B8`nLU~vV z@0F>egtb>(hyd2snu5@llLZ%%g0z+!J$X}`bK+y~qcsnRM8xD3Qk=tkbSyc|+VhfI z1MnFvv!53X9s}`aq9Lc@S=rDGpYLY4#~Y6|`^dzj*7XvJ7(FOhx_BZ(c z{k0hsrfC0+uM;+TSk6Mm!W?S>Z3#gnL~1@4rQQ$=Te9-GH#4H7CYpA3G-8_kRE@iz z%qN5PcE&*~`@}w2_3Eeo!0m?_mRaPP{$p^S-<4*aKsU6cY&nJf!-uvOCSJeO9u`sg zlBJ8azj_{*Qke%9FmE=xBM&n+V(Y_ty%o~IybaA`E}|e@+s)C;jpjQDS96dul_l6E z9?Lg0?T_(1{cuR%u-Q)g%lxrei|Nhcz=QW&z%w5GjNr8ewYx{+61)7Y1+Gb7^Hls|5rlNk093LLX3F zI9`$EC*X1XLwx|Yj^BRB+s|-@js7#k*32|ne%-A%NwwDz>`d3ia##+L1bQY`w2*aB zyF<41d2`>_9m4Fo9Ma1s=o`jc_+r(BQZKRB(&v0G_Y8Xsw=hvAwLPs0=`MaVQLpWEv3SGIFMwCNhikw5#s zlz|=c$?&XbJS7ux!+eehF{GJN@Va?DlLAbaUl>hCDyeO9-G$$o;K7T!|2n4Z*g5=oMcpV}8FhXF zEY&FWw?u?pykK|9S7~iSmpfu5Kb)mi+PXbC-_0L(dLsKZOZ8rFhem`{=>@&FjIw_? z`m@6lhQLbD&%;um*L0P-3H6AHjmO>L%V>E;5 ze8^|AYv%|(RFA-3snx?xq1BKnaAuhqt`7FW_W`sX6J}3bS&^=CYiv5_aTG9u{y$Q> zic8#;j_T3>uaXZRT<}mv(fh{#Hkia2IP$!A#Q!plr0qq=@4sbQa7&Wq5YuE8Gx>jV73k1cfN?Z-nEYoH%KynP(zQ@z zoa$6^1-6CZcp)xA;MSJsChrgpVu7E4fuA;>#@YWb6XtIMTy5ajK>fqim?kQM&k~ph z(lo@aa-4Ss$0Km-Bz|R2funlvb^SJqs^Sb^{BoRlz^Mq_zAH96zQ_IS8Np|9;MEiT zcu^x^YbJp5`|pyBEf#f*AaREN*Au9*Vx0@wGHAcJ9&#Zo;{NI#li;X4k&AITx~wG$ z%+o8YU^n5Qy4rW6@Ztksl`}0FQ(IhP&aKC^Q^{~Pq1Kkz0dWw;m$f^Oobm&_u; z<@8RChSFAo=cQsz17PoEev!=mMi=Zg%VC~501cH^9XLl*XYIk-iH0rXA4=wM|GJ3L zKS;54W!0rzO+FS1b|@%2&utVaX5OEs)V$eDw|c|jFZ!ttoemMNcO$AK7IH9GiE65O zjmy{rK24o%8 z1TjZZ*zcp8VLF!}PdX+KxvU4K!for|!jR50&AJ1zHu+`Hu?T0NZ`|#3t(n*%Y{XX_IMi*=%N3w&6+ zR!F_+J?|$CuFX$B%*_TUgyc$P6+^igUtUOwGq?Z~GPF@;z5q@rtLK7mH=RWhC2K=? zGx^yU+uJ1LTTMWGP7n&-7GlW>TZWRc@LKM+sX%yB0M9;u$s#va4fMrtH*ucw)_cZE zzZf0Qg;2GnP7;Woa~gEz#<~8@LP2|oR^nslIPswyL8|8WyR8R1eYM9;7hK#m{@jiI z0$z+lYzrcdMjqtJi*dVKen*(x`d$BP(c4&j-;`9-g?FZ&i^hgytkt{e3ZKu`ic3>w zvGzrN(T;qqhoboMfOFL~_Z14t_@W0#SN4zA!@u{+cgc9Px0y%C+Kb1sAadI794}_eVkFkkqds@8Xe)v)nWlBv$v^KWQ%9XO?S!RYtjhL_AGkgF*Bh1s<0$ z45x5jn6%rV1n1Uc5xe7^gQ0=AH){MT*^8C3+Rq0P>8^^BAUD{)RI?8|uyyrh283V9 zcQC}Zh12wYLlBp87sYL39uo`2Z8P4CGdxZZ@ssKj`84WY$z{u8qZi7k24aq%DI4S8moDlRdiFdd8(>hjEuBThA3RQcAV}EG`k!K`DE|q|=S0k<>4BTE@B)QNIkCvAn_Y66GJWd>5 zg4Xav?pi>KJ1YAO&^(d!3zb=R5?|jL;@4d34x@V?reJ{JvLCDx?Z#cWI3J0ge$zL9 zXJRv5rHKu3f9GJ_p{0xuHCCQbh%E2o!HQN5FV?zq^~7(tfc_Zskh(r{8;#k{oz5Lr zbnLfzkg_L}41N9lb=fbKe4I&JZ?^5vag2{TJ4qWaTF8KF3V`P?mboefWjD-oMJkvbJc)eB?ylU(SjVUd-T&YHUAtp zB%@_jRD7gs{Y@{hCsRkaAOL3&T^PTu;7vjobgwfOHpGYW%u}NM-#eUJ(i{@+CUCkA z3g`hrd<>-W8e=~pmnfgdKshI&isKXRg6s!CJ`1s)I9X++8@neBBsF?qwIX6YQ(@K= zgb=;}XFE2;beQ!A*;T1TI)9#}P$Y*;CLbyZ$$ zXy%@TR@>m@~w&iq?jjL4q#6ijy*jtT8^V>+qvm^!(`osf^cQ67B=gj=Ryf}Ah;rq z*;z`HwYz8B4alWz<33-scU^@;ON3D5WHE(Zjh)6CmJ=%-D+i!jWenX=lmwT$PVr#CVLRw^=zf9(0{um1CxN1P3xe$#xg%Kn@L zxciaOAH@m;YL$u8!GDu2=N2y(2rL9v3@gv=--YOmQ+}g(Fsb77^<3F5!jdQ@V2z|5 z9>a4fzp(lC;N_5vwVbMRhxhy=8G;TLU=7uygn!-;!7D+ot^Iq0lkk7`Vvbvb&8O#| zH%a1#KmcX+a>b$gpS=hGYcRw%SpKtRBoA;jRQ-hvl2Ktmaum;s` zq6ypRWf-fZ3py&K|ER+H!XLOel|L?m?_L6r$7*2L4!(u223y?#mFMfK~cia!UW>)+LFaQtuQj%AfD|!eG_#ee#{`CL= literal 0 HcmV?d00001 diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index 219aaec7b..4b6f5f66d 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -1,15 +1,27 @@ export default { - version: "3.0.0-beta.1", + version: "3.0.0-beta.3", steps: [ { titleIcon: "fa fa-map-o", title: { - "en-US": "Welcome to Node-RED 3.0 Beta 1!", - "ja": "Node-RED 3.0 ベータ1へようこそ!" + "en-US": "Welcome to Node-RED 3.0 Beta 3!", + "ja": "Node-RED 3.0 ベータ3へようこそ!" }, description: { - "en-US": "

      This is the first Beta release of Node-RED 3.0. It contains just about everything we have planned for the final release.

      Let's take a moment to discover the new features in this release.

      ", - "ja": "

      これはNode-RED 3.0の最初のベータリリースです。これには、最終リリースで計画しているほぼ全ての機能が含まれています。

      本リリースの新機能を見つけてみましょう。

      " + "en-US": "

      This is the final beta release of Node-RED 3.0.

      Let's take a moment to discover the new features in this release.

      ", + // "ja": "

      これはNode-RED 3.0の最初のベータリリースです。これには、最終リリースで計画しているほぼ全ての機能が含まれています。

      本リリースの新機能を見つけてみましょう。

      " + } + }, + { + title: { + "en-US": "Context Menu" + }, + image: 'images/context-menu.png', + description: { + "en-US": `

      The editor now has its own context menu when you + right-click in the workspace.

      +

      This makes many of the built-in actions much easier + to access.

      ` } }, { @@ -19,12 +31,13 @@ export default { }, image: 'images/junction-slice.gif', description: { - "en-US": `

      To make it easier to route wires around your flows, it is now possible to - add junction nodes that give you more control.

      -

      Junctions can be added to wires by holding the Shift key, then click and drag with - the right-hand mouse button across the wires.

      `, - "ja": `

      フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。

      -

      シフトキーを押しながら、マウスの右ボタンをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。

      ` + "en-US": `

      To make it easier to route wires around your flows, + it is now possible to add junction nodes that give + you more control.

      +

      Junctions can be added to wires by holding the Alt key + then click and drag the mouse across the wires.

      `, + // "ja": `

      フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。

      + //

      シフトキーを押しながら、マウスの右ボタンをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。

      ` }, }, { From ce529a9b9f3bbd9c8c71a2450191525813ea94ca Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 15:39:42 +0100 Subject: [PATCH 38/53] Update module dependencies --- packages/node_modules/@node-red/editor-api/package.json | 2 +- packages/node_modules/@node-red/nodes/package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 2 +- packages/node_modules/@node-red/util/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 793c5e516..769eb8f5a 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -26,7 +26,7 @@ "express": "4.18.1", "memorystore": "1.6.7", "mime": "3.0.0", - "multer": "1.4.4", + "multer": "1.4.5-lts.1", "mustache": "4.2.0", "oauth2orize": "1.11.1", "passport-http-bearer": "1.0.1", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 2a0d7e7c1..8c9d5adf7 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -36,7 +36,7 @@ "js-yaml": "4.1.0", "media-typer": "1.1.0", "mqtt": "4.3.7", - "multer": "1.4.4", + "multer": "1.4.5-lts.1", "mustache": "4.2.0", "node-watch": "0.7.3", "on-headers": "1.0.2", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 29e1b8dd6..b13fefc9f 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -21,6 +21,6 @@ "fs-extra": "10.1.0", "semver": "7.3.7", "tar": "6.1.11", - "uglify-js": "3.15.5" + "uglify-js": "3.16.0" } } diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 302e6f84e..a75ecd7ff 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -16,7 +16,7 @@ ], "dependencies": { "fs-extra": "10.1.0", - "i18next": "21.8.2", + "i18next": "21.8.10", "json-stringify-safe": "5.0.1", "jsonata": "1.8.6", "lodash.clonedeep": "^4.5.0", From 4ed559af95ae845792549a5f4061b873b2971be5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 15:44:51 +0100 Subject: [PATCH 39/53] Update changelog and version for 3-beta.3 --- CHANGELOG.md | 38 +++++++++++++++++++ package.json | 2 +- .../@node-red/editor-api/package.json | 6 +-- .../@node-red/editor-client/package.json | 2 +- .../node_modules/@node-red/nodes/package.json | 2 +- .../@node-red/registry/package.json | 4 +- .../@node-red/runtime/package.json | 6 +-- .../node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 ++--- 9 files changed, 55 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd3793487..254e6a977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +#### 3.0.0-beta.3: Beta Release + +Editor + + - Fix disable junction (#3671) @HiroyasuNishiyama + - Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi + - Reset mouse state when switching tabs (#3643) @knolleary + - Fix uncorrect fix of junction to subflow conversion (#3666) @HiroyasuNishiyama + - Fix undoing junction to subflow (#3653) @HiroyasuNishiyama + - Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama + - Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama + - Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi + - Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama + - Fix to sanitize tab name (#3646) @HiroyasuNishiyama + - Fix selector placement (#3644) @bonanitech + - Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi + - Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama + - Fix layer of palette node (#3638) @HiroyasuNishiyama + - Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama + - Fix typo in CSS (#3628) @bonanitech + - Use the correct variable for the gutter text color (#3615) @bonanitech + + +Runtime + + - Support loading node modules from `nodesdir` (#3676) @Steve-Mcl + - fix buffer parse error message of evaluateNodeProperty (#3624) @HiroyasuNishiyama + +Nodes + + - File: Further simplify file node filename entry UX (v3) (#3677) @Steve-Mcl + - Function: Fix initial cursor position of init/finalize tab of function node (#3674) @HiroyasuNishiyama + - Function: Fix ESM module loading in Function node (#3645) @knolleary + - Inject: Fix JSONata evaluation of inject button (#3632) @HiroyasuNishiyama + - TCP: Dont delete TCP socket twice (#3630) @Steve-Mcl + - MQTT Node: define noproxy variable (#3626) @Steve-Mcl + - Debug: i18n debug sidebar node label (#3623) @HiroyasuNishiyama + #### 3.0.0-beta.2: Beta Release **Migration from 2.x** diff --git a/package.json b/package.json index d22cf89dd..854626fb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 793c5e516..7c403ac4b 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "3.0.0-beta.2", - "@node-red/editor-client": "3.0.0-beta.2", + "@node-red/util": "3.0.0-beta.3", + "@node-red/editor-client": "3.0.0-beta.3", "bcryptjs": "2.4.3", "body-parser": "1.20.0", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 4380f5170..2a32cbded 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 2a0d7e7c1..f0a3b0b1b 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 29e1b8dd6..43ab2f00c 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "3.0.0-beta.2", + "@node-red/util": "3.0.0-beta.3", "clone": "2.1.2", "fs-extra": "10.1.0", "semver": "7.3.7", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 7ee872153..dcf7bf47c 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "3.0.0-beta.2", - "@node-red/util": "3.0.0-beta.2", + "@node-red/registry": "3.0.0-beta.3", + "@node-red/util": "3.0.0-beta.3", "async-mutex": "0.3.2", "clone": "2.1.2", "express": "4.18.1", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 302e6f84e..3b4f90418 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index e089fea71..36207c17e 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "3.0.0-beta.2", + "version": "3.0.0-beta.3", "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "3.0.0-beta.2", - "@node-red/runtime": "3.0.0-beta.2", - "@node-red/util": "3.0.0-beta.2", - "@node-red/nodes": "3.0.0-beta.2", + "@node-red/editor-api": "3.0.0-beta.3", + "@node-red/runtime": "3.0.0-beta.3", + "@node-red/util": "3.0.0-beta.3", + "@node-red/nodes": "3.0.0-beta.3", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "express": "4.18.1", From 83655a749c4eb3b410ec261ae1d908b3671f96ca Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 17:01:11 +0100 Subject: [PATCH 40/53] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 254e6a977..87fcc30bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Editor + - Add Right-Click content menu (#3678) @knolleary - Fix disable junction (#3671) @HiroyasuNishiyama - Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi - Reset mouse state when switching tabs (#3643) @knolleary From 1844633ff1f22fed00b4b7cd0fcda3cb29cc1fd0 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 22:56:54 +0100 Subject: [PATCH 41/53] Include scroll offset when positioning quick-add dialog Fixes #3684 --- .../node_modules/@node-red/editor-client/src/js/ui/view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 9f28d7191..522b86cbc 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -1071,8 +1071,8 @@ RED.view = (function() { var oy = point[1]; const offset = $("#red-ui-workspace-chart").offset() - var clientX = ox + offset.left - var clientY = oy + offset.top + var clientX = ox + offset.left - $("#red-ui-workspace-chart").scrollLeft() + var clientY = oy + offset.top - $("#red-ui-workspace-chart").scrollTop() if (RED.settings.get("editor").view['view-snap-grid']) { // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') From 53184715bc2568440efba79109fc203b2569f93c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 23:18:02 +0100 Subject: [PATCH 42/53] Fix menu padding to handle both icons and submenus Fixes #3683 --- .../editor-client/src/js/ui/common/menu.js | 23 ++++++++++++++++++- .../editor-client/src/sass/dropdownMenu.scss | 14 ++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js index 6327f5268..c5b8d1d06 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js @@ -133,6 +133,8 @@ RED.menu = (function() { if (opt.options) { item.addClass("red-ui-menu-dropdown-submenu"+(opt.direction!=='right'?" pull-left":"")); var submenu = $('
        ').appendTo(item); + var hasIcons = false + var hasSubmenus = false for (var i=0;i li > a, & > li > a:focus { display: block; - padding: 4px 20px 4px 12px; + padding: 4px 12px 4px 32px; clear: both; font-weight: normal; line-height: 20px; @@ -58,6 +58,18 @@ & > li.pull-left > a:focus { padding: 4px 12px 4px 32px; } + &.red-ui-menu-dropdown-noicons > li > a, + &.red-ui-menu-dropdown-noicons > li > a:focus { + padding: 4px 12px 4px 12px; + } + + &.red-ui-menu-dropdown-submenus > li > a, + &.red-ui-menu-dropdown-submenus > li > a:focus { + padding-right: 20px; + } + + + & > .active > a, & > .active > a:hover, & > .active > a:focus { From e3d6d242ac5396a2d7ec3f0bcf235e3eedbaf345 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 16 Jun 2022 23:29:46 +0100 Subject: [PATCH 43/53] Fix handling of spacebar inside JSON visual editor Fixes #3682 The treeList keyboard handling was consuming the event - used to select the item in the list. The fix here adds a 'selectable' flag on the treeList that can be used to disabled (=false) the keyboard selection of items. --- .../@node-red/editor-client/src/js/ui/common/treeList.js | 2 ++ .../@node-red/editor-client/src/js/ui/editors/json.js | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index 3b6e2cde3..85c6f0992 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -21,6 +21,7 @@ * - multi : boolean - if true, .selected will return an array of results * otherwise, returns the first selected item * - sortable: boolean/string - TODO: see editableList + * - selectable: boolean - default true - whether individual items can be selected * - rootSortable: boolean - if 'sortable' is set, then setting this to * false, prevents items being sorted to the * top level of the tree @@ -118,6 +119,7 @@ switch(evt.keyCode) { case 32: // SPACE case 13: // ENTER + if (!that.options.selectable) { return } if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) { return } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/json.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/json.js index 8531ba2c5..394350f26 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/json.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/json.js @@ -534,6 +534,7 @@ var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"}); var filterDepth = Infinity; var list = $('
        ').appendTo(container).treeList({ + selectable: false, rootSortable: false, sortable: ".red-ui-editor-type-json-editor-item-handle", }).on("treelistchangeparent", function(event, evt) { From 0000f2a34d274d0785d6d7c6a8021ff38d8000fa Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sat, 18 Jun 2022 00:51:05 +0900 Subject: [PATCH 44/53] Support i18n in context menu --- .../@node-red/editor-client/locales/en-US/editor.json | 6 ++++++ .../@node-red/editor-client/locales/ja/editor.json | 6 ++++++ .../@node-red/editor-client/src/js/ui/contextMenu.js | 8 ++++---- 3 files changed, 16 insertions(+), 4 deletions(-) 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 01107d49d..e3028272b 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 @@ -1187,5 +1187,11 @@ "missing-config": "__prop__: missing configuration node", "validation-error": "__prop__: validation error: __node__, __id__: __error__" } + }, + "contextMenu": { + "insert": "Insert", + "node": "Node", + "junction": "Junction", + "linkNodes": "Link Nodes" } } 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 4c918b615..a78fb7d24 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 @@ -1188,6 +1188,12 @@ "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" } }, + "contextMenu": { + "insert": "挿入", + "node": "ノード", + "junction": "分岐点", + "linkNodes": "Linkノード" + }, "action-list": { "toggle-show-tips": "ヒント表示切替", "show-about": "Node-REDの説明を表示", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 379ed5433..9920789f5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -48,10 +48,10 @@ RED.contextMenu = (function() { const menuItems = [ { onselect: 'core:show-action-list', onpostselect: function() {} }, { - label: 'Insert', + label: RED._("contextMenu.insert"), options: [ { - label: 'Node', + label: RED._("contextMenu.node"), onselect: function() { RED.view.showQuickAddDialog({ position: [ options.x - offset.left, options.y - offset.top ], @@ -62,12 +62,12 @@ RED.contextMenu = (function() { } }, { - label: 'Junction', + label: RED._("contextMenu.junction"), onselect: 'core:split-wires-with-junctions', disabled: hasSelection || !hasLinks }, { - label: 'Link Nodes', + label: RED._("contextMenu.linkNodes"), onselect: 'core:split-wire-with-link-nodes', disabled: hasSelection || !hasLinks } From 418b3ee7d68f280e30340032b28df97106a8d87e Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sat, 18 Jun 2022 00:56:34 +0900 Subject: [PATCH 45/53] Fix Japanese translations to be same as others --- .../@node-red/editor-client/locales/ja/editor.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a78fb7d24..70cb0aa22 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 @@ -1308,7 +1308,7 @@ "search": "検索", "search-previous": "前を検索", "search-next": "次を検索", - "show-action-list": "アクション一覧を表示", + "show-action-list": "動作一覧を表示", "confirm-edit-tray": "編集を完了", "cancel-edit-tray": "編集をキャンセル", "show-remote-diff": "リモートとの変更差分を表示", @@ -1330,6 +1330,6 @@ "zoom-out": "ズームアウト", "zoom-reset": "ズームリセット", "toggle-navigator": "ナビゲータ表示切替", - "show-system-info": "システムインフォメーション" + "show-system-info": "システム情報" } } From 3ad95bec3ca553ddd35e48e95aaf171e504d7a97 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sat, 18 Jun 2022 00:57:30 +0900 Subject: [PATCH 46/53] Add Japanese translation for file nodes --- packages/node_modules/@node-red/nodes/locales/ja/messages.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index c508a3e76..6e16daa6f 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -928,6 +928,7 @@ "write": "write file", "read": "read file", "filename": "ファイル名", + "path": "パス", "action": "動作", "addnewline": "メッセージの入力のたびに改行を追加", "createdir": "ディレクトリが存在しない場合は作成", From 75c9353cbddbbeb7203ddc5ebb24dc3fb8b0b0ea Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 19 Jun 2022 01:23:28 +0900 Subject: [PATCH 47/53] Fix keys to insert junction --- .../node_modules/@node-red/editor-client/src/tours/welcome.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index 4b6f5f66d..1289e6bee 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -34,7 +34,7 @@ export default { "en-US": `

        To make it easier to route wires around your flows, it is now possible to add junction nodes that give you more control.

        -

        Junctions can be added to wires by holding the Alt key +

        Junctions can be added to wires by holding both the Alt key and the Shift key then click and drag the mouse across the wires.

        `, // "ja": `

        フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。

        //

        シフトキーを押しながら、マウスの右ボタンをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。

        ` From 13885493acbc09d0578894717fff65daaf6e3a61 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 19 Jun 2022 01:26:48 +0900 Subject: [PATCH 48/53] Update Japanese translations in welcome tour --- .../@node-red/editor-client/src/tours/welcome.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index 1289e6bee..98d83c7e5 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -9,19 +9,22 @@ export default { }, description: { "en-US": "

        This is the final beta release of Node-RED 3.0.

        Let's take a moment to discover the new features in this release.

        ", - // "ja": "

        これはNode-RED 3.0の最初のベータリリースです。これには、最終リリースで計画しているほぼ全ての機能が含まれています。

        本リリースの新機能を見つけてみましょう。

        " + "ja": "

        これはNode-RED 3.0の最後のベータリリースです。

        本リリースの新機能を見つけてみましょう。

        " } }, { title: { - "en-US": "Context Menu" + "en-US": "Context Menu", + "ja": "コンテキストメニュー" }, image: 'images/context-menu.png', description: { "en-US": `

        The editor now has its own context menu when you right-click in the workspace.

        This makes many of the built-in actions much easier - to access.

        ` + to access.

        `, + "ja": `

        ワークスペースで右クリックすると、エディタに独自のコンテキストメニューが表示されるようになりました。

        +

        これによって多くの組み込み動作を、より簡単に利用できます。

        ` } }, { @@ -36,8 +39,8 @@ export default { you more control.

        Junctions can be added to wires by holding both the Alt key and the Shift key then click and drag the mouse across the wires.

        `, - // "ja": `

        フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。

        - //

        シフトキーを押しながら、マウスの右ボタンをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。

        ` + "ja": `

        フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。

        +

        Altキーとシフトキーを押しながらマウスをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。

        ` }, }, { From 643541eebdc4394937c0038e5582ff21586a9589 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 19 Jun 2022 01:37:55 +0900 Subject: [PATCH 49/53] Remove unnecessary spaces --- .../@node-red/nodes/locales/ja/function/10-function.html | 2 +- .../@node-red/nodes/locales/ja/network/10-mqtt.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html b/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html index a18e5e8a8..960b755f6 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html @@ -28,7 +28,7 @@

        返却/sendの対象は次のとおりです:

        • 単一メッセージオブジェクト - 最初の出力に接続されたノードに渡されます
        • -
        • メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます
        • +
        • メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます

        注: 初期化処理の実行はノードの初期化中に行われます。そのため、初期化処理タブにsendを記述した場合に後続ノードでメッセージを受け取れないことがあります。

        配列要素が配列の場合には、複数のメッセージを対応する出力に送出します。

        diff --git a/packages/node_modules/@node-red/nodes/locales/ja/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/locales/ja/network/10-mqtt.html index 1b43ea097..435829e1e 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/network/10-mqtt.html +++ b/packages/node_modules/@node-red/nodes/locales/ja/network/10-mqtt.html @@ -89,7 +89,7 @@
        userProperties オブジェクト
        MQTTv5: メッセージのユーザプロパティ
        messageExpiryInterval 数値
        -
        MQTTv5: 秒単位のメッセージの有効期限
        +
        MQTTv5: 秒単位のメッセージの有効期限
        topicAlias 数値
        MQTTv5: 使用するMQTTトピックエイリアス
        From 5e592427e940bd095440bb2ea0bf65636fe30194 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sun, 19 Jun 2022 01:56:59 +0900 Subject: [PATCH 50/53] Add Japanese translation in action list for junction --- .../@node-red/editor-client/locales/ja/editor.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 70cb0aa22..ac09a1e87 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 @@ -1330,6 +1330,7 @@ "zoom-out": "ズームアウト", "zoom-reset": "ズームリセット", "toggle-navigator": "ナビゲータ表示切替", - "show-system-info": "システム情報" + "show-system-info": "システム情報", + "split-wires-with-junctions": "分岐点によりワイヤーを分割" } } From ad0a08ea0e7dde0684e97545a0fd52c33c7a6316 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 20 Jun 2022 00:44:20 +0900 Subject: [PATCH 51/53] Add Japanese translations in action list for project feature --- .../@node-red/editor-client/locales/ja/editor.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 ac09a1e87..2849070bf 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 @@ -1331,6 +1331,10 @@ "zoom-reset": "ズームリセット", "toggle-navigator": "ナビゲータ表示切替", "show-system-info": "システム情報", - "split-wires-with-junctions": "分岐点によりワイヤーを分割" + "split-wires-with-junctions": "分岐点によりワイヤーを分割", + "new-project": "新しいプロジェクト", + "open-project": "プロジェクトを開く", + "show-project-settings": "プロジェクト設定を表示", + "show-version-control-tab": "バージョンコントロールタブを表示" } } From 202102ebf7374e6697d9c807457f9bc0c31099cd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 20 Jun 2022 20:51:31 +0100 Subject: [PATCH 52/53] Fix clicking on node in workspace to hide context menu --- .../editor-client/src/js/ui/contextMenu.js | 3 ++- .../@node-red/editor-client/src/js/ui/view.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 9920789f5..0b388aff3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -170,6 +170,7 @@ RED.contextMenu = (function() { } return { - show: show + show: show, + hide: disposeMenu } })() diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 522b86cbc..b55244d0f 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -988,6 +988,7 @@ RED.view = (function() { if (RED.view.DEBUG) { console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); } + RED.contextMenu.hide(); if (mouse_mode === RED.state.SELECTING_NODE) { d3.event.stopPropagation(); return; @@ -1779,6 +1780,9 @@ RED.view = (function() { } var i; var historyEvent; + if (d3.event.button === 2) { + return + } if (mouse_mode === RED.state.PANNING) { resetMouseVars(); return @@ -2903,6 +2907,7 @@ RED.view = (function() { function portMouseDown(d,portType,portIndex, evt) { if (RED.view.DEBUG) { console.warn("portMouseDown", mouse_mode,d,portType,portIndex); } + RED.contextMenu.hide(); evt = evt || d3.event; if (evt === 1) { return; @@ -3411,6 +3416,7 @@ RED.view = (function() { function nodeMouseDown(d) { if (RED.view.DEBUG) { console.warn("nodeMouseDown", mouse_mode,d); } focusView(); + RED.contextMenu.hide(); if (d3.event.button === 1) { return; } @@ -3793,6 +3799,7 @@ RED.view = (function() { if (RED.view.DEBUG) { console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); } + RED.contextMenu.hide(); if (mouse_mode === RED.state.SELECTING_NODE) { d3.event.stopPropagation(); return; @@ -3852,6 +3859,9 @@ RED.view = (function() { } function groupMouseUp(g) { + if (RED.view.DEBUG) { + console.warn("groupMouseUp", { mouse_mode, event: d3.event }); + } if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) { mouse_mode = RED.state.DEFAULT; RED.editor.editGroup(g); @@ -3867,6 +3877,10 @@ RED.view = (function() { // return // } + if (RED.view.DEBUG) { + console.warn("groupMouseDown", { mouse_mode, point: mouse, event: d3.event }); + } + RED.contextMenu.hide(); focusView(); if (d3.event.button === 1) { return; From d2e84925f7738dcceda6465627e4945d20b23ffc Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 22 Jun 2022 21:43:25 +0100 Subject: [PATCH 53/53] Set default editor to monaco in absence of user preference --- .../src/js/ui/editors/code-editor.js | 16 ++++++++-------- .../@node-red/runtime/lib/api/settings.js | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js index 13ed25611..7cee2026b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js @@ -21,7 +21,7 @@ const MONACO = "monaco"; const ACE = "ace"; - const defaultEditor = ACE; + const defaultEditor = MONACO; const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} }; var selectedCodeEditor = null; var initialised = false; @@ -48,12 +48,12 @@ } function create(options) { - //TODO: (quandry - for consideration) + //TODO: (quandry - for consideration) // Below, I had to create a hidden element if options.id || options.element is not in the DOM - // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an + // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an // invalid (non existing html element selector) (e.g. node-red-contrib-components does this) - // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre' - // code is thus skipped. + // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre' + // code is thus skipped. // In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI) // Because one (or more) contrib nodes have left this bad code in place, how would we handle this? // For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur. @@ -79,7 +79,7 @@ return this.editor.create(options);//fallback to ACE } } - + return { init: init, /** @@ -91,7 +91,7 @@ }, /** * Get user selected code editor - * @return {string} Returns + * @return {string} Returns * @memberof RED.editor.codeEditor */ get editor() { @@ -104,4 +104,4 @@ */ create: create } -})(); \ No newline at end of file +})(); diff --git a/packages/node_modules/@node-red/runtime/lib/api/settings.js b/packages/node_modules/@node-red/runtime/lib/api/settings.js index f56b8ab61..9bf640227 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/settings.js +++ b/packages/node_modules/@node-red/runtime/lib/api/settings.js @@ -91,7 +91,7 @@ var api = module.exports = { safeSettings.context = runtime.nodes.listContextStores(); if (runtime.settings.editorTheme && runtime.settings.editorTheme.codeEditor) { safeSettings.codeEditor = runtime.settings.editorTheme.codeEditor || {}; - safeSettings.codeEditor.lib = safeSettings.codeEditor.lib || "ace"; + safeSettings.codeEditor.lib = safeSettings.codeEditor.lib || "monaco"; safeSettings.codeEditor.options = safeSettings.codeEditor.options || {}; } safeSettings.libraries = runtime.library.getLibraries();