1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge remote-tracking branch 'upstream/dev' into mqtt5

This commit is contained in:
Steve-Mcl 2020-11-19 13:45:56 +00:00
commit 3033d2086e
87 changed files with 5161 additions and 184 deletions

View File

@ -27,6 +27,9 @@ jobs:
with: with:
node-version: '12' node-version: '12'
- run: node ./node-red/.github/scripts/update-node-red-docker.js - run: node ./node-red/.github/scripts/update-node-red-docker.js
with:
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Create Docker Pull Request - name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2 uses: peter-evans/create-pull-request@v2
with: with:

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ packages/node_modules/@node-red/editor-client/public
docs docs
!packages/node_modules/**/docs !packages/node_modules/**/docs
.vscode .vscode
.nyc_output

View File

@ -5,11 +5,11 @@ language: node_js
matrix: matrix:
include: include:
- node_js: "14" - node_js: "14"
script:
- ./node_modules/.bin/grunt && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
# - scripts/install-ui-test-dependencies.sh && grunt test-ui
before_script:
- npm install -g coveralls
- node_js: "12" - node_js: "12"
- node_js: "10" - node_js: "10"
# script:
# - ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
# - scripts/install-ui-test-dependencies.sh && grunt test-ui
# before_script:
# - npm install -g istanbul coveralls
- node_js: "8" - node_js: "8"

View File

@ -1,3 +1,70 @@
### 1.2.5: Maintenance Release
Editor
- Fix import of config nodes with unknown z property
Runtime
- Set ACTIONS_ALLOW_UNSECURE_COMMANDS in GH Action
### 1.2.4: Maintenance Release
Editor
- Support bigint types in Debug sidebar
- Clear retained status of deleted nodes
- Prevent needless retention of node status messages
- Update projects dialogs to use TypedInput-cred input
- Restore cursor position in TypedInput cred-mode
- Ensure config nodes with invalid z are imported somewhere
- Ensure user keyboard shortcuts override defaults Fixes #2753
Runtime
- Disable projects when flowFile passed into grunt dev
- Add Russian Locale (#2761) (#2531) (@alexk111)
- Add Japanese translation for http-in node (#2758) (@kazuhitoyokoi)
Nodes
- CSV: Fix CSV node repeating array output
### 1.2.3: Maintenance Release
Editor
- Disable 'use strict' checking in Function node Fixes #2743
- Add gray/grey alternate options for status
- Handle import errors on initial load and report to user
- Only apply recovery tab on initial load Fixes #2731
- Reinstate coveralls reporting to travis build
- Update Japanese message catalogue for 1.2.3 release #2747 (@HiroyasuNishiyama)
Runtime
- Modify default settings comment (#2739)
- Add mutex lock to saveSettings storage call Fixes #2736 (#2737)
- Migrate to nyc instead of istanbul for code coverage
- Move mosca to ui-test-dependencies
- Remove " from npm install prefix option
### 1.2.2: Maintenance Release
Editor
- Prevent node z property getting set to 0 or ""
- Only apply z-recovery logic to flow nodes
- Fix api call to reload flows Fixes #2726
- Remove bad z property from import config nodes
### 1.2.1: Maintenance Release
Runtime
- Fix race condition in .config file migration Fixes #2724
### 1.2.0: Milestone Release ### 1.2.0: Milestone Release
Editor Editor

View File

@ -24,6 +24,7 @@ module.exports = function(grunt) {
var flowFile = grunt.option('flowFile'); var flowFile = grunt.option('flowFile');
if (flowFile) { if (flowFile) {
nodemonArgs.push(flowFile); nodemonArgs.push(flowFile);
process.env.NODE_RED_ENABLE_PROJECTS=false;
} }
var userDir = grunt.option('userDir'); var userDir = grunt.option('userDir');
if (userDir) { if (userDir) {
@ -52,8 +53,8 @@ module.exports = function(grunt) {
ui: 'bdd', ui: 'bdd',
reporter: 'spec' reporter: 'spec'
}, },
all: { src: ['test/**/*_spec.js'] }, all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/_spec.js","test/unit/**/*_spec.js"]}, core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]} nodes: { src: ["test/nodes/**/*_spec.js"]}
}, },
webdriver: { webdriver: {
@ -61,19 +62,19 @@ module.exports = function(grunt) {
configFile: 'test/editor/wdio.conf.js' configFile: 'test/editor/wdio.conf.js'
} }
}, },
mocha_istanbul: { nyc: {
options: { options: {
globals: ['expect'], cwd: '.',
timeout: 3000, include: ['packages/node_modules/**'],
ignoreLeaks: false, excludeNodeModules: false,
ui: 'bdd', exclude: ['packages/node_modules/@node-red/editor-client/**'],
reportFormats: ['lcov','html'], reporter: ['lcov', 'html','text-summary'],
print: 'both', reportDir: 'coverage',
istanbulOptions: ['--no-default-excludes', '-i','**/packages/node_modules/**'] all: true
}, },
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] }, all: { cmd: false, args: ['grunt', 'simplemocha:all'] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]}, core: { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] },
nodes: { src: ["test/nodes/**/*_spec.js"]} nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] }
}, },
jshint: { jshint: {
options: { options: {
@ -512,7 +513,6 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-chmod'); grunt.loadNpmTasks('grunt-chmod');
grunt.loadNpmTasks('grunt-jsonlint'); grunt.loadNpmTasks('grunt-jsonlint');
grunt.loadNpmTasks('grunt-mocha-istanbul');
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
grunt.loadNpmTasks('grunt-webdriver'); grunt.loadNpmTasks('grunt-webdriver');
} }
@ -520,6 +520,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-jsdoc-to-markdown'); grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
grunt.loadNpmTasks('grunt-npm-command'); grunt.loadNpmTasks('grunt-npm-command');
grunt.loadNpmTasks('grunt-mkdir'); grunt.loadNpmTasks('grunt-mkdir');
grunt.loadNpmTasks('grunt-simple-nyc');
grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () { grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () {
const nodemon = require('nodemon'); const nodemon = require('nodemon');
@ -620,11 +621,11 @@ module.exports = function(grunt) {
grunt.registerTask('default', grunt.registerTask('default',
'Builds editor content then runs code style checks and unit tests on all components', 'Builds editor content then runs code style checks and unit tests on all components',
['build','verifyPackageDependencies','jshint:editor','mocha_istanbul:all']); ['build','verifyPackageDependencies','jshint:editor','nyc:all']);
grunt.registerTask('test-core', grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code', 'Runs code style check and unit tests on core runtime code',
['build','mocha_istanbul:core']); ['build','nyc:core']);
grunt.registerTask('test-editor', grunt.registerTask('test-editor',
'Runs code style check on editor code', 'Runs code style check on editor code',
@ -642,7 +643,7 @@ module.exports = function(grunt) {
grunt.registerTask('test-nodes', grunt.registerTask('test-nodes',
'Runs unit tests on core nodes', 'Runs unit tests on core nodes',
['build','mocha_istanbul:nodes']); ['build','nyc:nodes']);
grunt.registerTask('build', grunt.registerTask('build',
'Builds editor content', 'Builds editor content',
@ -667,7 +668,7 @@ module.exports = function(grunt) {
grunt.registerTask('coverage', grunt.registerTask('coverage',
'Run Istanbul code test coverage task', 'Run Istanbul code test coverage task',
['build','mocha_istanbul:all']); ['build','nyc:all']);
grunt.registerTask('docs', grunt.registerTask('docs',
'Generates API documentation', 'Generates API documentation',

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red", "name": "node-red",
"version": "1.2.0", "version": "1.3.0-beta.1",
"description": "Low-code programming for event-driven applications", "description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org", "homepage": "http://nodered.org",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -50,13 +50,13 @@
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "3.14.0", "js-yaml": "3.14.0",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.8.3", "jsonata": "1.8.4",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0", "media-typer": "1.1.0",
"memorystore": "1.6.2", "memorystore": "1.6.4",
"mime": "2.4.6", "mime": "2.4.6",
"moment-timezone": "^0.5.31", "moment-timezone": "0.5.32",
"mqtt": "4.2.1", "mqtt": "4.2.5",
"multer": "1.4.2", "multer": "1.4.2",
"mustache": "4.0.1", "mustache": "4.0.1",
"node-red-admin": "^0.2.6", "node-red-admin": "^0.2.6",
@ -73,7 +73,7 @@
"request": "2.88.0", "request": "2.88.0",
"semver": "6.3.0", "semver": "6.3.0",
"tar": "6.0.5", "tar": "6.0.5",
"uglify-js": "3.11.2", "uglify-js": "3.11.6",
"when": "3.7.8", "when": "3.7.8",
"ws": "6.2.1", "ws": "6.2.1",
"xml2js": "0.4.23" "xml2js": "0.4.23"
@ -82,7 +82,7 @@
"bcrypt": "3.0.8" "bcrypt": "3.0.8"
}, },
"devDependencies": { "devDependencies": {
"dompurify": "2.1.1", "dompurify": "2.2.2",
"grunt": "1.3.0", "grunt": "1.3.0",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.2", "grunt-cli": "~1.3.2",
@ -98,20 +98,18 @@
"grunt-jsdoc-to-markdown": "5.0.0", "grunt-jsdoc-to-markdown": "5.0.0",
"grunt-jsonlint": "2.1.3", "grunt-jsonlint": "2.1.3",
"grunt-mkdir": "~1.0.0", "grunt-mkdir": "~1.0.0",
"grunt-mocha-istanbul": "5.0.2",
"grunt-npm-command": "~0.1.2", "grunt-npm-command": "~0.1.2",
"grunt-sass": "~3.1.0", "grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1", "grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1",
"http-proxy": "1.18.1", "http-proxy": "1.18.1",
"istanbul": "0.4.5",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "1.2.0", "marked": "1.2.4",
"minami": "1.2.3", "minami": "1.2.3",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"mosca": "^2.8.3",
"node-red-node-test-helper": "^0.2.5", "node-red-node-test-helper": "^0.2.5",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"nodemon": "2.0.5", "nodemon": "2.0.6",
"should": "13.2.3", "should": "13.2.3",
"sinon": "1.17.7", "sinon": "1.17.7",
"stoppable": "^1.1.0", "stoppable": "^1.1.0",

View File

@ -33,8 +33,6 @@ var activeConnections = [];
var anonymousUser; var anonymousUser;
var retained = {};
var heartbeatTimer; var heartbeatTimer;
var lastSentTime; var lastSentTime;

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/editor-api", "name": "@node-red/editor-api",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,15 +16,15 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/util": "1.2.0", "@node-red/util": "1.3.0-beta.1",
"@node-red/editor-client": "1.2.0", "@node-red/editor-client": "1.3.0-beta.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.19.0", "body-parser": "1.19.0",
"clone": "2.1.2", "clone": "2.1.2",
"cors": "2.8.5", "cors": "2.8.5",
"express-session": "1.17.1", "express-session": "1.17.1",
"express": "4.17.1", "express": "4.17.1",
"memorystore": "1.6.2", "memorystore": "1.6.4",
"mime": "2.4.6", "mime": "2.4.6",
"multer": "1.4.2", "multer": "1.4.2",
"mustache": "4.0.1", "mustache": "4.0.1",

View File

@ -42,7 +42,8 @@
"loadNodeCatalogs": "Loading Node catalogs", "loadNodeCatalogs": "Loading Node catalogs",
"loadNodes": "Loading Nodes __count__", "loadNodes": "Loading Nodes __count__",
"loadFlows": "Loading Flows", "loadFlows": "Loading Flows",
"importFlows": "Adding Flows to workspace" "importFlows": "Adding Flows to workspace",
"importError": "<p>Error adding flows</p><p>__message__</p>"
}, },
"workspace": { "workspace": {
"defaultName": "Flow __number__", "defaultName": "Flow __number__",
@ -207,6 +208,8 @@
"download": "Download", "download": "Download",
"importUnrecognised": "Imported unrecognised type:", "importUnrecognised": "Imported unrecognised type:",
"importUnrecognised_plural": "Imported unrecognised types:", "importUnrecognised_plural": "Imported unrecognised types:",
"importDuplicate": "Imported duplicate node:",
"importDuplicate_plural": "Imported duplicate nodes:",
"nodesExported": "Nodes exported to clipboard", "nodesExported": "Nodes exported to clipboard",
"nodesImported": "Imported:", "nodesImported": "Imported:",
"nodeCopied": "__count__ node copied", "nodeCopied": "__count__ node copied",
@ -1086,6 +1089,7 @@
"en-US": "English", "en-US": "English",
"ja": "Japanese", "ja": "Japanese",
"ko": "Korean", "ko": "Korean",
"ru": "Russian",
"zh-CN": "Chinese(Simplified)", "zh-CN": "Chinese(Simplified)",
"zh-TW": "Chinese(Traditional)" "zh-TW": "Chinese(Traditional)"
} }

View File

@ -42,7 +42,8 @@
"loadNodeCatalogs": "ノードカタログを読み込み中", "loadNodeCatalogs": "ノードカタログを読み込み中",
"loadNodes": "ノードを読み込み中 __count__", "loadNodes": "ノードを読み込み中 __count__",
"loadFlows": "フローを読み込み中", "loadFlows": "フローを読み込み中",
"importFlows": "ワークスペースにフローを追加中" "importFlows": "ワークスペースにフローを追加中",
"importError": "<p>フロー追加エラー</p><p>__message__</p>"
}, },
"workspace": { "workspace": {
"defaultName": "フロー __number__", "defaultName": "フロー __number__",
@ -207,6 +208,8 @@
"download": "ダウンロード", "download": "ダウンロード",
"importUnrecognised": "認識できない型が読み込まれました:", "importUnrecognised": "認識できない型が読み込まれました:",
"importUnrecognised_plural": "認識できない型が読み込まれました:", "importUnrecognised_plural": "認識できない型が読み込まれました:",
"importDuplicate": "重複したノードを読み込みました:",
"importDuplicate_plural": "重複したノードを読み込みました:",
"nodesExported": "クリップボードへフローを書き出しました", "nodesExported": "クリップボードへフローを書き出しました",
"nodesImported": "読み込みました:", "nodesImported": "読み込みました:",
"nodeCopied": "__count__ 個のノードをコピーしました", "nodeCopied": "__count__ 個のノードをコピーしました",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
{
"info": {
"tip0" : "Вы можете удалить выбранные узлы или провода с {{core:delete-selection}}",
"tip1" : "Ищите узлы с {{core:search}}",
"tip2" : "{{core:toggle-sidebar}} показывает/скрывает эту боковою панель",
"tip3" : "Вы можете управлять палитрой узлов с помощью {{core:manage-palette}}",
"tip4" : "Узлы конфигурации потока перечисляются на боковой панели. Доступ к списку можно получить из меню или с помощью {{core:show-config-tab}}",
"tip5" : "Эти советы можно включить/выключить через настройки",
"tip6" : "Перемещайте выбранные узлы клавишами [влево] [вверх] [вниз] и [вправо]. Удерживайте [Shift], чтобы увеличить шаг",
"tip7" : "Перетаскивание узла на провод соединит его с обеих сторон",
"tip8" : "Экспортируйте выбранные узлы или текущую вкладку с {{core:show-export-dialog}}",
"tip9" : "Импортируйте поток, перетаскивая его JSON в редактор или с помощью {{core:show-import-dialog}}",
"tip10" : "Нажмите [Shift], [кликните] по порту узла и перетаскивайте подключенные провода на другой узел",
"tip11" : "Открывайте вкладку Информация с {{core:show-info-tab}} или вкладку Отладка с {{core:show-debug-tab}}",
"tip12" : "Нажмите [ctrl] и [кликните] в рабочей области, чтобы открыть диалог быстрого добавления",
"tip13" : "Нажмите [ctrl] и [кликните] по порту узла, чтобы начать быстрое подключение",
"tip14" : "Нажмите [Shift] и [кликните] по узлу, чтобы выбрать все соединенные узлы",
"tip15" : "Нажмите [ctrl] и [кликните] по узлу, чтобы добавить или убрать его из текущего выбора",
"tip16" : "Переключайте вкладки потока с помощью {{core:show-previous-tab}} и {{core:show-next-tab}}",
"tip17" : "Вы можете подтвердить изменения в редакторе узла с {{core:confirm-edit-tray}} или отменить их с {{core:cancel-edit-tray}}",
"tip18" : "Нажатие {{core:edit-selected-node}} откроет редактор первого узла в текущем выборе"
}
}

View File

@ -0,0 +1,274 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Преобразует параметр `arg` в строку, используя следующие правила приведения:\n\n - Строки возвращаются как есть\n - Функции преобразуются в пустую строку\n - Числовая бесконечность и NaN выдают ошибку, поскольку они не могут быть представлены числом в JSON\n - Все остальные значения преобразуются в строку JSON с помощью функции `JSON.stringify`. Если значение `prettify` равно true, тогда будет сгенерирован \"отформатированный\" JSON. То есть каждое поле будет в отдельной строке, а строки будут иметь отступ в зависимости от глубины поля."
},
"$length": {
"args": "str",
"desc": "Возвращает количество символов в строке `str`. Выдается ошибка, если `str` не является строкой."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Возвращает строку, содержащую символы из первого параметра `str`, начиная с позиции `start` (отсчет с нуля). Если указан `length`, то подстрока будет содержать максимум `length` символов. Если `start` отрицателен, то это означает количество символов с конца `str`."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Возвращает подстроку перед первым вхождением последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Возвращает подстроку после первого вхождения последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
},
"$uppercase": {
"args": "str",
"desc": "Возвращает строку со всеми символами `str`, преобразованными в верхний регистр."
},
"$lowercase": {
"args": "str",
"desc": "Возвращает строку со всеми символами `str`, преобразованными в нижний регистр."
},
"$trim": {
"args": "str",
"desc": "Нормализует и обрезает все пробельные символы в строке `str`, выполняя следующие шаги:\n\n - Все символы табуляции, возврата каретки и перевода строки заменяются пробелами.\n- Последовательности пробелов сокращаются до одного пробела.\n- Пробелы в начале и конце `str` удаляются.\n\n Если `str` не указан (то есть эта функция вызывается без аргументов), тогда значение контекста используется в качестве значения `str`. Выдается ошибка, если `str` не является строкой."
},
"$contains": {
"args": "str, pattern",
"desc": "Возвращает `true`, если строка `str` соответствует шаблону `pattern`, в противном случае возвращает `false`. Если `str` не указан (то есть эта функция вызывается с одним аргументом), то значение контекста используется как значение `str`. Параметр `pattern` может быть либо строкой, либо регулярным выражением."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Разбивает строку `str` на массив подстрок. Выдает ошибку, если `str` не является строкой. Необязательный параметр `separator` (строка или регулярное выражение) указывает символы внутри строки `str`, относительно которых она должна быть разделена. Если `separator` не указан, то предполагается пустая строка, и `str` будет разбит на массив из отдельных символов. Выдает ошибку, если `separator` не является строкой. Необязательный параметр `limit` - это число, указывающее максимальное количество подстрок для включения в результирующий массив. Любые дополнительные подстроки отбрасываются. Если `limit` не указан, то весь `str` разделяется без ограничения размера результирующего массива. Выдает ошибку, если `limit` не является положительным числом."
},
"$join": {
"args": "array[, separator]",
"desc": "Объединяет массив подстрок в одну объединенную строку, в которой каждая подстрока отделена необязательным параметром `separator`. Выдает ошибку, если входной массив содержит элемент, который не является строкой. Если `separator` не указан, то предполагается, что это пустая строка, то есть нет `separator` между подстроками. Выдает ошибку, если `separator` не является строкой."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "Применяет строку `str` к регулярному выражению `pattern` и возвращает массив объектов, каждый из которых содержит информацию о каждом совпадении внутри `str`."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "Находит вхождения шаблона `pattern` в строке `str` и заменяет их на строку `replacement`.\n\nНеобязательный параметр `limit` - это максимальное количество замен."
},
"$now": {
"args":"",
"desc":"Создает отметку времени в формате, совместимом с ISO 8601, и возвращает ее как строку."
},
"$base64encode": {
"args":"string",
"desc":"Преобразует ASCII-строку в base-64 кодировку. Каждый символ в строке обрабатывается как байт двоичных данных. Для этого необходимо, чтобы все символы в строке находились в диапазоне от 0x00 до 0xFF, который включает все символы строк в URI-кодировке. Символы Юникода за пределами этого диапазона не поддерживаются."
},
"$base64decode": {
"args":"string",
"desc":"Преобразует байты в кодировке base-64 в строку, используя кодовую страницу Юникод UTF-8."
},
"$number": {
"args": "arg",
"desc": "Преобразует параметр `arg` в число с использованием следующих правил приведения:\n\n - Числа возвращаются как есть\n - Строки, которые содержат последовательность символов, представляющих допустимое в JSON число, преобразуются в это число\n - Все остальные значения вызывают ошибку."
},
"$abs": {
"args":"number",
"desc":"Возвращает абсолютное значение числа `number`."
},
"$floor": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое меньше или равно `number`."
},
"$ceil": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое больше или равно `number`."
},
"$round": {
"args":"number [, precision]",
"desc":"Возвращает значение числа `number`, округленное до количества десятичных знаков, указанных необязательным параметром `precision`."
},
"$power": {
"args":"base, exponent",
"desc":"Возвращает значение числа `base`, возведенное в степень `exponent`."
},
"$sqrt": {
"args":"number",
"desc":"Возвращает квадратный корень из значения числа `number`."
},
"$random": {
"args":"",
"desc":"Возвращает псевдослучайное число, которе больше или равно нулю и меньше единицы."
},
"$millis": {
"args":"",
"desc":"Возвращает число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу) в виде числа. Все вызовы `$millis()` в пределах выполнения выражения будут возвращать одно и то же значение."
},
"$sum": {
"args": "array",
"desc": "Возвращает арифметическую сумму массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$max": {
"args": "array",
"desc": "Возвращает максимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$min": {
"args": "array",
"desc": "Возвращает минимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$average": {
"args": "array",
"desc": "Возвращает среднее значение массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$boolean": {
"args": "arg",
"desc": "Приводит аргумент к логическому значению, используя следующие правила: \n\n - Логические значения возвращаются как есть\n - пустая строка: `false`\n - непустая строка: `true`\n - число равное `0`: `false`\n - ненулевое число: `true`\n - `null` : `false`\n - пустой массив: `false`\n - массив, который содержит хотя бы один элемент, приводимый к `true`: `true`\n - массив, все элементы которого приводятся к `false`: `false`\n - пустой объект: `false`\n - непустой объект: `true`\n - функция: `false`"
},
"$not": {
"args": "arg",
"desc": "Возвращает логическое НЕ для аргумента. `arg` сначала приводится к логическому значению"
},
"$exists": {
"args": "arg",
"desc": "Возвращает логическое `true`, если выполнение выражения `arg` возвращает значение, или `false`, если выражение ничему не соответствует (например, путь к несуществующему полю)."
},
"$count": {
"args": "array",
"desc": "Возвращает количество элементов в массиве"
},
"$append": {
"args": "array, array",
"desc": "Присоединяет один массив к другому"
},
"$sort": {
"args":"array [, function]",
"desc":"Возвращает массив, содержащий все значения параметра `array`, но отсортированные по порядку.\n\nЕсли указан компаратор `function`, то это должна быть функция, которая принимает два параметра:\n\n`function(val1, val2)`\n\nЭту функцию вызывает алгоритм сортировки для сравнения двух значений: val1 и val2. Если значение val1 следует поместить после значения val2 в желаемом порядке сортировки, то функция должна возвращать логическое значение `true`, чтобы обозначить замену. В противном случае она должна вернуть `false`."
},
"$reverse": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но в обратном порядке."
},
"$shuffle": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но перемешанный в случайном порядке."
},
"$zip": {
"args":"array, ...",
"desc":"Возвращает свернутый (сжатый) массив, содержащий сгруппированные массивы значений из аргументов `array1` … `arrayN` по индексам 0, 1, 2...."
},
"$keys": {
"args": "object",
"desc": "Возвращает массив, содержащий ключи объекта. Если аргумент является массивом объектов, то возвращаемый массив содержит недублированный список всех ключей из всех объектов."
},
"$lookup": {
"args": "object, key",
"desc": "Возвращает значение, связанное с ключом в объекте. Если первый аргумент является массивом объектов, то просходит поиск по всем объектам в массиве, и возвращаются значения, связанные со всеми вхождениями ключа."
},
"$spread": {
"args": "object",
"desc": "Разбивает объект, содержащий пары ключ / значение, на массив объектов, каждый из которых имеет одну пару ключ / значение из входного объекта. Если параметр является массивом объектов, то результирующий массив содержит объект для каждой пары ключ / значение из каждого объекта предоставленного массива."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Объединяет массив объектов в один объект, содержащий все пары ключ / значение каждого из объектов входного массива. Если какой-либо из входных объектов содержит один и тот же ключ, возвращаемый объект будет содержать значение последнего в массиве. Вызывает ошибку, если входной массив содержит элемент, который не является объектом."
},
"$sift": {
"args":"object, function",
"desc":"Возвращает объект, который содержит только пары ключ / значение из параметра `object`, которые удовлетворяют предикату `function`, переданному в качестве второго параметра.\n\n`function`, которая передается в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, key [, object]])`"
},
"$each": {
"args":"object, function",
"desc":"Возвращает массив, который содержит значения, возвращаемые функцией `function` при применении к каждой паре ключ/значение из объекта `object`."
},
"$map": {
"args":"array, function",
"desc":"Возвращает массив, содержащий результаты применения функции `function` к каждому значению массива `array`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args":"array, function",
"desc":"Возвращает массив, содержащий только те значения из массива `array`, которые удовлетворяют предикату `function`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$reduce": {
"args":"array, function [, init]",
"desc":"Возвращает агрегированное значение, полученное в результате последовательного применения функции `function` к каждому значению в массиве в сочетании с результатом от предыдущего применения функции.\n\nФункция должна принимать два аргумента и вести себя как инфиксный оператор между каждым значением в массиве `array`. Сигнатура `function` должна иметь форму: `myfunc($accumulator, $value[, $index[, $array]])`\n\nНеобязательный параметр `init` используется в качестве начального значения в агрегации."
},
"$flowContext": {
"args": "string[, string]",
"desc": "Извлекает свойство контекста потока.\n\nЭто функция от Node-RED."
},
"$globalContext": {
"args": "string[, string]",
"desc": "Извлекает свойство глобального контекста.\n\nЭто функция от Node-RED."
},
"$pad": {
"args": "string, width [, char]",
"desc": "Возвращает копию строки `string` с дополнительным заполнением, если необходимо, чтобы общее количество символов как минимум соответствовало абсолютному значению параметра `width`.\n\nЕсли `width` является положительным числом, то строка дополняется справа; если отрицательным, то дополняется слева.\n\nНеобязательный аргумент `char` указывает символ(ы) для заполнения. Если не указано, по умолчанию используется пробел."
},
"$fromMillis": {
"args": "number",
"desc": "Преобразует число, представляющее миллисекунды с начала Unix-эпохи (1 января 1970 года по Гринвичу), в строку отметки времени в формате ISO 8601."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Преобразует число `number` в строку и форматирует ее в десятичное представление, как указано в строке `picture`.\n\nПоведение этой функции соответствует XPath/XQuery-функции fn:format-number, как определено в спецификация XPath F&O 3.1. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и fn:format-number.\n\nНеобязательный третий аргумент `options` используется для переопределения символов форматирования, специфичных для локали по умолчанию, таких как десятичный разделитель. Если аргумент указан, то это должен быть объект, содержащий пары имя/значение, указанные в разделе десятичного формата спецификации XPath F&O 3.1."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "Преобразует число `number` в строку и форматирует ее в целое число, представленное в системе счисления, указанной аргументом `radix`. Если `radix` не указан, то по умолчанию используется десятичная. Значение 'radix` может быть от 2 до 36, в противном случае выдается ошибка."
},
"$toMillis": {
"args": "timestamp",
"desc": "Преобразует строку `timestamp` в формате ISO 8601 в число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу). Вызывает ошибку, если строка в неправильном формате."
},
"$env": {
"args": "arg",
"desc": "Возвращает значение переменной среды.\n\nЭто функция от Node-RED."
},
"$eval": {
"args": "expr [, context]",
"desc": "Анализирует и исполняет строку `expr`, которая содержит JSON или выражение JSONata, используя текущий контекст в качестве контекста для исполнения."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Преобразует число `number` в строку и форматирует ее в целочисленное представление, как указано в строке `picture`. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и `fn:format-integer` из спецификации XPath F&O 3.1."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Разбирает содержимое строки `string` в целое число (как число JSON), используя формат, указанный в строке `picture`. Строковый параметр `picture` имеет тот же формат, что и `$formatInteger`."
},
"$error": {
"args": "[str]",
"desc": "Вызывает ошибку с сообщением. Необязательная строка `str` заменяет сообщение по умолчанию $error() function evaluated`"
},
"$assert": {
"args": "arg, str",
"desc": "Если значение `arg` равно true, функция возвращает значение undefined. Если значение `arg` равно false, генерируется исключение с `str` в качестве сообщения об исключении."
},
"$single": {
"args": "array, function",
"desc": "Возвращает одно-единственное значение из массива `array`, которое удовлетворяет предикату `function` (то есть когда `function` возвращает логическое `true` при передаче значения). Выдает исключение, если число подходящих значений не одно.\n\nФункция должна соответствовать следующей сигнатуре: `function(value [, index [, array]])` где value - элемент массива, index - позиция этого значения, а весь массив передается в качестве третьего аргумента"
},
"$encodeUrl": {
"args": "str",
"desc": "Кодирует компонент Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Кодирует Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrlComponent.\n\nПример: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "Возвращает массив содержащий все элементы из массива `array`, с удаленными дупликатами"
},
"$type": {
"args": "value",
"desc": "Возвращает тип значения `value` в виде строки. Если `value` не определено, то будет возвращено `undefined`"
},
"$moment": {
"args": "[str]",
"desc": "Получает date объект, используя библиотеку Moment."
}
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/editor-client", "name": "@node-red/editor-client",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",

File diff suppressed because one or more lines are too long

View File

@ -80,9 +80,8 @@ oop.inherits(NRJavaScriptWorker, Mirror);
// undef: true, // undef: true,
// unused: true, // unused: true,
esversion: 9, esversion: 9,
moz: true,
devel: true, devel: true,
browser: true, browser: false,
node: true, node: true,
laxcomma: true, laxcomma: true,
laxbreak: true, laxbreak: true,
@ -92,7 +91,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
maxerr: 100, maxerr: 100,
expr: true, expr: true,
multistr: true, multistr: true,
globalstrict: true, strict: false,
sub: true, sub: true,
asi: true asi: true
}; };

View File

@ -553,6 +553,9 @@ RED.nodes = (function() {
node.id = n.id; node.id = n.id;
node.type = n.type; node.type = n.type;
node.z = n.z; node.z = n.z;
if (node.z === 0 || node.z === "") {
delete node.z;
}
if (n.d === true) { if (n.d === true) {
node.d = true; node.d = true;
} }
@ -1047,6 +1050,7 @@ RED.nodes = (function() {
* Options: * Options:
* - generateIds - whether to replace all node ids * - generateIds - whether to replace all node ids
* - addFlow - whether to import nodes to a new tab * - addFlow - whether to import nodes to a new tab
* - importToCurrent
* - importMap - how to resolve any conflicts. * - importMap - how to resolve any conflicts.
* - id:import - import as-is * - id:import - import as-is
* - id:copy - import with new id * - id:copy - import with new id
@ -1103,8 +1107,9 @@ RED.nodes = (function() {
if (!options.generateIds) { if (!options.generateIds) {
if (!options.importMap[id]) { if (!options.importMap[id]) {
// No conflict resolution for this node // No conflict resolution for this node
if (nodes[id] || configNodes[id] || workspaces[id] || subflows[id] || groups[id]) { var existing = nodes[id] || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
existingNodes.push(id); if (existing) {
existingNodes.push({existing:existing, imported:n});
} }
} else if (options.importMap[id] === "replace") { } else if (options.importMap[id] === "replace") {
nodesToReplace.push(n); nodesToReplace.push(n);
@ -1116,7 +1121,21 @@ RED.nodes = (function() {
}) })
if (existingNodes.length > 0) { if (existingNodes.length > 0) {
var existingNodesError = new Error(); var errorMessage = RED._("clipboard.importDuplicate",{count:existingNodes.length});
var nodeList = $("<ul>");
var existingNodesCount = Math.min(5,existingNodes.length);
for (var i=0;i<existingNodesCount;i++) {
var conflict = existingNodes[i];
$("<li>").text(
conflict.existing.id+
" [ "+conflict.existing.type+ ((conflict.imported.type !== conflict.existing.type)?" | "+conflict.imported.type:"")+" ]").appendTo(nodeList)
}
if (existingNodesCount !== existingNodes.length) {
$("<li>").text(RED._("deploy.confirm.plusNMore",{count:existingNodes.length-existingNodesCount})).appendTo(nodeList)
}
var wrapper = $("<p>").append(nodeList);
var existingNodesError = new Error(errorMessage+wrapper.html());
existingNodesError.code = "import_conflict"; existingNodesError.code = "import_conflict";
existingNodesError.importConfig = identifyImportConflicts(newNodes); existingNodesError.importConfig = identifyImportConflicts(newNodes);
throw existingNodesError; throw existingNodesError;
@ -1150,7 +1169,7 @@ RED.nodes = (function() {
if (n.z) { if (n.z) {
nodeZmap[n.z] = nodeZmap[n.z] || []; nodeZmap[n.z] = nodeZmap[n.z] || [];
nodeZmap[n.z].push(n); nodeZmap[n.z].push(n);
} else if (n.z === 0) { } else if (isInitialLoad && n.hasOwnProperty('x') && n.hasOwnProperty('y') && !n.z) {
// Hit the rare issue where node z values get set to 0. // Hit the rare issue where node z values get set to 0.
// Repair the flow - but we really need to track that down. // Repair the flow - but we really need to track that down.
if (!recoveryWorkspace) { if (!recoveryWorkspace) {
@ -1235,6 +1254,9 @@ RED.nodes = (function() {
if (defaultWorkspace == null) { if (defaultWorkspace == null) {
defaultWorkspace = n; defaultWorkspace = n;
} }
if (activeWorkspace === 0) {
activeWorkspace = n.id;
}
if (createNewIds || options.importMap[n.id] === "copy") { if (createNewIds || options.importMap[n.id] === "copy") {
nid = getID(); nid = getID();
workspace_map[n.id] = nid; workspace_map[n.id] = nid;
@ -1337,6 +1359,10 @@ RED.nodes = (function() {
} }
} }
} }
} else {
if (n.z && !workspaces[n.z]) {
n.z = activeWorkspace;
}
} }
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) { if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
@ -1348,6 +1374,9 @@ RED.nodes = (function() {
users:[], users:[],
_config:{} _config:{}
}; };
if (!n.z) {
delete configNode.z;
}
if (n.hasOwnProperty('d')) { if (n.hasOwnProperty('d')) {
configNode.d = n.d; configNode.d = n.d;
} }
@ -1490,6 +1519,9 @@ RED.nodes = (function() {
delete node.wires; delete node.wires;
delete node.inputLabels; delete node.inputLabels;
delete node.outputLabels; delete node.outputLabels;
if (!n.z) {
delete node.z;
}
} }
var orig = {}; var orig = {};
for (var p in n) { for (var p in n) {

View File

@ -178,11 +178,21 @@ var RED = (function() {
var currentHash = window.location.hash; var currentHash = window.location.hash;
RED.nodes.version(nodes.rev); RED.nodes.version(nodes.rev);
loader.reportProgress(RED._("event.importFlows"),90 ) loader.reportProgress(RED._("event.importFlows"),90 )
RED.nodes.import(nodes.flows); try {
RED.nodes.dirty(false); RED.nodes.import(nodes.flows);
RED.view.redraw(true); RED.nodes.dirty(false);
if (/^#flow\/.+$/.test(currentHash)) { RED.view.redraw(true);
RED.workspaces.show(currentHash.substring(6)); if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6));
}
} catch(err) {
RED.notify(
RED._("event.importError", {message: err.message}),
{
fixed: true,
type: 'error'
}
);
} }
} }
done(); done();

View File

@ -21,7 +21,18 @@ RED.actions = (function() {
var result = []; var result = [];
Object.keys(actions).forEach(function(action) { Object.keys(actions).forEach(function(action) {
var shortcut = RED.keyboard.getShortcut(action); var shortcut = RED.keyboard.getShortcut(action);
result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined,user:shortcut?shortcut.user:undefined}) var isUser = false;
if (shortcut) {
isUser = shortcut.user;
} else {
isUser = !!RED.keyboard.getUserShortcut(action);
}
result.push({
id:action,
scope:shortcut?shortcut.scope:undefined,
key:shortcut?shortcut.key:undefined,
user:isUser
})
}) })
return result; return result;
} }

View File

@ -773,6 +773,9 @@ RED.clipboard = (function() {
// representation or null // representation or null
return null; return null;
} }
if (value.type === 'bigint') {
return value.data.toString();
}
if (value.type === 'undefined') { if (value.type === 'undefined') {
return undefined; return undefined;
} }

View File

@ -173,6 +173,7 @@
valueLabel: function(container,value) { valueLabel: function(container,value) {
var that = this; var that = this;
container.css("pointer-events","none"); container.css("pointer-events","none");
container.css("flex-grow",0);
this.elementDiv.hide(); this.elementDiv.hide();
var buttons = $('<div>').css({ var buttons = $('<div>').css({
position: "absolute", position: "absolute",
@ -184,22 +185,25 @@
width:"20px" width:"20px"
}).appendTo(buttons).on("click", function(evt) { }).appendTo(buttons).on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
var cursorPosition = that.input[0].selectionStart;
var currentType = that.input.attr("type"); var currentType = that.input.attr("type");
if (currentType === "text") { if (currentType === "text") {
that.input.attr("type","password"); that.input.attr("type","password");
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
setTimeout(function() { setTimeout(function() {
that.input.focus(); that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50); },50);
} else { } else {
that.input.attr("type","text"); that.input.attr("type","text");
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash"); eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
setTimeout(function() { setTimeout(function() {
that.input.focus(); that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50); },50);
} }
}).hide(); }).hide();
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-1px").appendTo(eyeButton); var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
if (value === "__PWRD__") { if (value === "__PWRD__") {
var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({ var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
@ -284,7 +288,7 @@
this.input.css('width','100%'); this.input.css('width','100%');
this.uiSelect.width(m[1]); this.uiSelect.width(m[1]);
this.uiWidth = null; this.uiWidth = null;
} else { } else if (this.uiWidth !== 0){
this.uiSelect.width(this.uiWidth); this.uiSelect.width(this.uiWidth);
} }
["Right","Left"].forEach(function(d) { ["Right","Left"].forEach(function(d) {
@ -304,7 +308,11 @@
this.element.attr('type','hidden'); this.element.attr('type','hidden');
this.options.types = this.options.types||Object.keys(allOptions); if (!this.options.types && this.options.type) {
this.options.types = [this.options.type]
} else {
this.options.types = this.options.types||Object.keys(allOptions);
}
this.selectTrigger = $('<button class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect); this.selectTrigger = $('<button class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect);
$('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger); $('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
@ -872,6 +880,9 @@
this.elementDiv.hide(); this.elementDiv.hide();
this.valueLabelContainer.hide(); this.valueLabelContainer.hide();
} else if (opt.valueLabel) { } else if (opt.valueLabel) {
// Reset any CSS the custom label may have set
this.valueLabelContainer.css("pointer-events","");
this.valueLabelContainer.css("flex-grow",1);
this.valueLabelContainer.show(); this.valueLabelContainer.show();
this.valueLabelContainer.empty(); this.valueLabelContainer.empty();
this.elementDiv.hide(); this.elementDiv.hide();

View File

@ -70,6 +70,11 @@ RED.keyboard = (function() {
} }
} }
} }
function getUserKey(action) {
var currentEditorSettings = RED.settings.get('editor') || {};
var userKeymap = currentEditorSettings.keymap || {};
return userKeymap[action];
}
function init() { function init() {
// Migrate from pre-0.18 // Migrate from pre-0.18
migrateOldKeymap(); migrateOldKeymap();
@ -255,6 +260,19 @@ RED.keyboard = (function() {
var i=0; var i=0;
if (typeof key === 'string') { if (typeof key === 'string') {
if (typeof cbdown === 'string') { if (typeof cbdown === 'string') {
if (!ondown && !defaultKeyMap.hasOwnProperty(cbdown)) {
defaultKeyMap[cbdown] = {
scope:scope,
key:key,
user:false
}
}
if (!ondown) {
var userAction = getUserKey(cbdown);
if (userAction) {
return;
}
}
actionToKeyMap[cbdown] = {scope:scope,key:key}; actionToKeyMap[cbdown] = {scope:scope,key:key};
if (typeof ondown === 'boolean') { if (typeof ondown === 'boolean') {
actionToKeyMap[cbdown].user = ondown; actionToKeyMap[cbdown].user = ondown;
@ -417,11 +435,9 @@ RED.keyboard = (function() {
}); });
revertButton.on("click", function(e) { revertButton.on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
RED.keyboard.revertToDefault(object.id);
container.empty(); container.empty();
container.removeClass('keyboard-shortcut-entry-expanded'); container.removeClass('keyboard-shortcut-entry-expanded');
var shortcut = RED.keyboard.getShortcut(object.id); // var userKeymap = RED.settings.get('keymap') || {};
var userKeymap = RED.settings.get('keymap') || {};
var currentEditorSettings = RED.settings.get('editor') || {}; var currentEditorSettings = RED.settings.get('editor') || {};
var userKeymap = currentEditorSettings.keymap || {}; var userKeymap = currentEditorSettings.keymap || {};
@ -429,6 +445,9 @@ RED.keyboard = (function() {
currentEditorSettings.keymap = userKeymap; currentEditorSettings.keymap = userKeymap;
RED.settings.set('editor',currentEditorSettings); RED.settings.set('editor',currentEditorSettings);
RED.keyboard.revertToDefault(object.id);
var shortcut = RED.keyboard.getShortcut(object.id);
var obj = { var obj = {
id:object.id, id:object.id,
scope:shortcut?shortcut.scope:undefined, scope:shortcut?shortcut.scope:undefined,
@ -589,6 +608,7 @@ RED.keyboard = (function() {
getShortcut: function(actionName) { getShortcut: function(actionName) {
return actionToKeyMap[actionName]; return actionToKeyMap[actionName];
}, },
getUserShortcut: getUserKey,
revertToDefault: revertToDefault, revertToDefault: revertToDefault,
formatKey: formatKey, formatKey: formatKey,
validateKey: validateKey, validateKey: validateKey,

View File

@ -1033,7 +1033,7 @@ RED.projects.settings = (function() {
var credentialSecretExistingRow = $('<div class="red-ui-settings-row red-ui-settings-row-credentials"></div>').appendTo(credentialFormRows); var credentialSecretExistingRow = $('<div class="red-ui-settings-row red-ui-settings-row-credentials"></div>').appendTo(credentialFormRows);
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.currentKey")).appendTo(credentialSecretExistingRow); $('<label for=""></label>').text(RED._("sidebar.project.projectSettings.currentKey")).appendTo(credentialSecretExistingRow);
var credentialSecretExistingInput = $('<input type="password">').appendTo(credentialSecretExistingRow) var credentialSecretExistingInput = $('<input type="text">').appendTo(credentialSecretExistingRow).typedInput({type:"cred"})
.on("change keyup paste",function() { .on("change keyup paste",function() {
if (popover) { if (popover) {
popover.close(); popover.close();
@ -1046,7 +1046,7 @@ RED.projects.settings = (function() {
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.newKey")).appendTo(credentialSecretNewRow); $('<label for=""></label>').text(RED._("sidebar.project.projectSettings.newKey")).appendTo(credentialSecretNewRow);
var credentialSecretNewInput = $('<input type="password">').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles); var credentialSecretNewInput = $('<input type="text">').appendTo(credentialSecretNewRow).typedInput({type:"cred"}).on("change keyup paste",checkFiles);
var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i>' + RED._("sidebar.project.projectSettings.credentialsAlert") + '</div>').hide().appendTo(credentialFormRows); var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i>' + RED._("sidebar.project.projectSettings.credentialsAlert") + '</div>').hide().appendTo(credentialFormRows);

View File

@ -81,8 +81,8 @@ RED.projects = (function() {
$('<p>').text(RED._("projects.welcome.desc2")).appendTo(body); $('<p>').text(RED._("projects.welcome.desc2")).appendTo(body);
var row = $('<div style="text-align: center"></div>').appendTo(body); var row = $('<div style="text-align: center"></div>').appendTo(body);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row); var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row); var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
createAsEmpty.on("click", function(e) { createAsEmpty.on("click", function(e) {
e.preventDefault(); e.preventDefault();
@ -511,7 +511,8 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row); subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.clone-project.passwd")+'</label>').appendTo(subrow); $('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.clone-project.passwd")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow); projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// ----------------------------------------------------- // -----------------------------------------------------
// Repo credentials - key/passphrase ------------------- // Repo credentials - key/passphrase -------------------
@ -539,12 +540,12 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row); subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.passphrase")+'</label>').appendTo(subrow); $('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow); projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows); subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow); var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow); $('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow); subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) { $('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault(); e.preventDefault();
dialog.dialog( "close" ); dialog.dialog( "close" );
RED.userSettings.show('gitconfig'); RED.userSettings.show('gitconfig');
@ -558,8 +559,8 @@ RED.projects = (function() {
// Secret - clone // Secret - clone
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body); row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label>'+RED._("projects.clone-project.credential-key")+'</label>').appendTo(row); $('<label>'+RED._("projects.clone-project.credential-key")+'</label>').appendTo(row);
projectSecretInput = $('<input type="password"></input>').appendTo(row); projectSecretInput = $('<input style="width: 100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
return container; return container;
@ -894,6 +895,7 @@ RED.projects = (function() {
$('<label class="red-ui-projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">'+RED._("projects.encryption-config.use-custom")+'</span></label>').appendTo(row); $('<label class="red-ui-projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">'+RED._("projects.encryption-config.use-custom")+'</span></label>').appendTo(row);
row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox); row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row); emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row);
emptyProjectCredentialInput.typedInput({type:"cred"});
emptyProjectCredentialInput.on("change keyup paste", validateForm); emptyProjectCredentialInput.on("change keyup paste", validateForm);
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox); row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
@ -1169,11 +1171,11 @@ RED.projects = (function() {
row = $('<div class="form-row button-group"></div>').appendTo(container); row = $('<div class="form-row button-group"></div>').appendTo(container);
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row); var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row); var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row); // var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row); var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row); // var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) { row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected'); container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
@ -1298,6 +1300,7 @@ RED.projects = (function() {
$('<label class="red-ui-projects-edit-form-inline-label">'+RED._("projects.create.encryption-key")+'</label>').appendTo(row); $('<label class="red-ui-projects-edit-form-inline-label">'+RED._("projects.create.encryption-key")+'</label>').appendTo(row);
// row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox); // row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row); emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row);
emptyProjectCredentialInput.typedInput({type:"cred"});
emptyProjectCredentialInput.on("change keyup paste", validateForm); emptyProjectCredentialInput.on("change keyup paste", validateForm);
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.desc0")+'</small></label>').appendTo(row); $('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.desc0")+'</small></label>').appendTo(row);
@ -1356,7 +1359,8 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row); subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.create.password")+'</label>').appendTo(subrow); $('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.create.password")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow); projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// ----------------------------------------------------- // -----------------------------------------------------
// Repo credentials - key/passphrase ------------------- // Repo credentials - key/passphrase -------------------
@ -1384,12 +1388,13 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row); subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.passphrase")+'</label>').appendTo(subrow); $('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow); projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows); subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow); var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow); $('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow); subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) { $('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault(); e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click"); $('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig'); RED.userSettings.show('gitconfig');
@ -1403,8 +1408,8 @@ RED.projects = (function() {
// Secret - clone // Secret - clone
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container); row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label>'+RED._("projects.create.credentials-encryption-key")+'</label>').appendTo(row); $('<label>'+RED._("projects.create.credentials-encryption-key")+'</label>').appendTo(row);
projectSecretInput = $('<input type="password"></input>').appendTo(row); projectSecretInput = $('<input style="width:100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
switch(options.screen||"empty") { switch(options.screen||"empty") {
case "empty": createAsEmpty.trigger("click"); break; case "empty": createAsEmpty.trigger("click"); break;
@ -1617,14 +1622,14 @@ RED.projects = (function() {
function deleteProject(row,name,done) { function deleteProject(row,name,done) {
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row); var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover); $('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button class="red-ui-button">'+RED._("common.label.cancel")+'</button>') $('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
.appendTo(cover) .appendTo(cover)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
cover.remove(); cover.remove();
done(true); done(true);
}); });
$('<button class="red-ui-button primary">'+RED._("common.label.delete")+'</button>') $('<button class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
.appendTo(cover) .appendTo(cover)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
@ -1808,7 +1813,7 @@ RED.projects = (function() {
header.addClass("selectable"); header.addClass("selectable");
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header); var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
$('<button class="red-ui-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>') $('<button class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
.appendTo(tools) .appendTo(tools)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
@ -1962,7 +1967,8 @@ RED.projects = (function() {
var isSSH = false; var isSSH = false;
if (/^https?:\/\//.test(url)) { if (/^https?:\/\//.test(url)) {
$('<div class="form-row"><label for="projects-user-auth-username">'+RED._("projects.send-req.username")+'</label><input id="projects-user-auth-username" type="text"></input></div>'+ $('<div class="form-row"><label for="projects-user-auth-username">'+RED._("projects.send-req.username")+'</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for=projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message); '<div class="form-row"><label for="projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
message.find("#projects-user-auth-password").typedInput({type:"cred"})
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) { } else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true; isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message); var row = $('<div class="form-row"></div>').appendTo(message);
@ -1980,7 +1986,7 @@ RED.projects = (function() {
}); });
row = $('<div class="form-row"></div>').appendTo(message); row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-passphrase">'+RED._("projects.send-req.passphrase")+'</label>').appendTo(row); $('<label for="projects-user-auth-passphrase">'+RED._("projects.send-req.passphrase")+'</label>').appendTo(row);
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row); $('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row).typedInput({type:"cred"});
} }
var notification = RED.notify(message,{ var notification = RED.notify(message,{

View File

@ -60,7 +60,7 @@ RED.utils = (function() {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']'); result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'function') { } else if (value.hasOwnProperty('type') && value.type === 'function') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function'); result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
} else if (value.hasOwnProperty('type') && value.type === 'number') { } else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data); result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
} else { } else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>'); result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
@ -352,7 +352,7 @@ RED.utils = (function() {
$('<span class="red-ui-debug-msg-type-null">'+obj+'</span>').appendTo(entryObj); $('<span class="red-ui-debug-msg-type-null">'+obj+'</span>').appendTo(entryObj);
} else if (obj.__enc__ && obj.type === 'undefined') { } else if (obj.__enc__ && obj.type === 'undefined') {
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj); $('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
} else if (obj.__enc__ && obj.type === 'number') { } else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj); e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) { } else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj); e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);

View File

@ -98,7 +98,8 @@ RED.view = (function() {
"green": "#5a8", "green": "#5a8",
"yellow": "#F9DF31", "yellow": "#F9DF31",
"blue": "#53A3F3", "blue": "#53A3F3",
"grey": "#d3d3d3" "grey": "#d3d3d3",
"gray": "#d3d3d3"
} }
var PORT_TYPE_INPUT = 1; var PORT_TYPE_INPUT = 1;

View File

@ -251,7 +251,7 @@ g.red-ui-flow-node-selected {
stroke-dasharray: none; stroke-dasharray: none;
} }
} }
@each $current-color in red green yellow blue grey { @each $current-color in red green yellow blue grey gray {
.red-ui-flow-node-status-dot-#{$current-color} { .red-ui-flow-node-status-dot-#{$current-color} {
fill: map-get($node-status-colors,$current-color); fill: map-get($node-status-colors,$current-color);
stroke: map-get($node-status-colors,$current-color); stroke: map-get($node-status-colors,$current-color);

View File

@ -90,7 +90,7 @@
font-size: 1.2em; font-size: 1.2em;
} }
} }
button.red-ui-button { button.red-ui-button.red-ui-projects-dialog-button {
width: calc(50% - 80px); width: calc(50% - 80px);
margin: 20px; margin: 20px;
height: auto; height: auto;

View File

@ -18,6 +18,7 @@
border: 1px solid $form-input-border-color; border: 1px solid $form-input-border-color;
border-radius: 4px; border-radius: 4px;
height: 34px; height: 34px;
line-height: 14px;
display: inline-flex; display: inline-flex;
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@ -21,8 +21,11 @@ module.exports = function(RED) {
this.tosidebar = n.tosidebar; this.tosidebar = n.tosidebar;
if (this.tosidebar === undefined) { this.tosidebar = true; } if (this.tosidebar === undefined) { this.tosidebar = true; }
this.active = (n.active === null || typeof n.active === "undefined") || n.active; this.active = (n.active === null || typeof n.active === "undefined") || n.active;
if (this.tostatus) { this.status({fill:"grey", shape:"ring"}); } if (this.tostatus) {
else { this.status({}); } this.status({fill:"grey", shape:"ring"});
this.oldState = "{}";
}
var hasStatExpression = (n.statusType === "jsonata"); var hasStatExpression = (n.statusType === "jsonata");
var statExpression = hasStatExpression ? n.statusVal : null; var statExpression = hasStatExpression ? n.statusVal : null;
@ -97,7 +100,11 @@ module.exports = function(RED) {
} }
} }
} }
this.on("close", function() {
if (this.oldState) {
this.status({});
}
})
this.on("input", function(msg, send, done) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) { if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) {
done(); done();
@ -202,13 +209,8 @@ module.exports = function(RED) {
function setNodeState(node,state) { function setNodeState(node,state) {
if (state) { if (state) {
node.active = true; node.active = true;
if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); }
} else { } else {
node.active = false; node.active = false;
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
node.oldStatus.shape = "dot";
node.status(node.oldStatus);
}
} }
} }

View File

@ -127,6 +127,8 @@ module.exports = function(RED) {
node.topic = n.topic; node.topic = n.topic;
node.outstandingTimers = []; node.outstandingTimers = [];
node.outstandingIntervals = []; node.outstandingIntervals = [];
node.clearStatus = false;
var sandbox = { var sandbox = {
console:console, console:console,
util:util, util:util,
@ -163,6 +165,7 @@ module.exports = function(RED) {
node.on.apply(node, arguments); node.on.apply(node, arguments);
}, },
status: function() { status: function() {
node.clearStatus = true;
node.status.apply(node, arguments); node.status.apply(node, arguments);
} }
}, },
@ -389,7 +392,9 @@ module.exports = function(RED) {
while (node.outstandingIntervals.length > 0) { while (node.outstandingIntervals.length > 0) {
clearInterval(node.outstandingIntervals.pop()); clearInterval(node.outstandingIntervals.pop());
} }
node.status({}); if (node.clearStatus) {
node.status({});
}
}); });
promise.then(function (v) { promise.then(function (v) {

View File

@ -63,14 +63,19 @@ module.exports = function(RED) {
if (typeof msg.payload == "object") { // convert object to CSV string if (typeof msg.payload == "object") { // convert object to CSV string
try { try {
var ou = ""; var ou = "";
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
if (node.hdrout !== "none" && node.hdrSent === false) { if (node.hdrout !== "none" && node.hdrSent === false) {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { if ((node.template.length === 1) && (node.template[0] === '')) {
node.template = clean((msg.columns || "").split(",")); if (msg.hasOwnProperty("columns")) {
node.template = clean((msg.columns || "").split(","));
}
else {
node.template = Object.keys(msg.payload[0]);
}
} }
ou += node.template.join(node.sep) + node.ret; ou += node.template.join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; } if (node.hdrout === "once") { node.hdrSent = true; }
} }
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
for (var s = 0; s < msg.payload.length; s++) { for (var s = 0; s < msg.payload.length; s++) {
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) { if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; } if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
@ -98,10 +103,10 @@ module.exports = function(RED) {
} }
for (var p in msg.payload[0]) { for (var p in msg.payload[0]) {
/* istanbul ignore else */ /* istanbul ignore else */
if (msg.payload[0].hasOwnProperty(p)) { if (msg.payload[s].hasOwnProperty(p)) {
/* istanbul ignore else */ /* istanbul ignore else */
if (typeof msg.payload[0][p] !== "object") { if (typeof msg.payload[s][p] !== "object") {
var q = "" + msg.payload[0][p]; var q = "" + msg.payload[s][p];
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""'); q = q.replace(/"/g, '""');
ou += node.quo + q + node.quo + node.sep; ou += node.quo + q + node.quo + node.sep;

View File

@ -39,7 +39,7 @@
will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.</p> will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.</p>
<p>When converting to CSV, the column template is used to identify which properties to extract from the object and in what order.</p> <p>When converting to CSV, the column template is used to identify which properties to extract from the object and in what order.</p>
<p>If the template is blank then the node can use a simple comma separated list of properties supplied in <code>msg.columns</code> to <p>If the template is blank then the node can use a simple comma separated list of properties supplied in <code>msg.columns</code> to
determine what to extract. If that is not present then all the object properties are ouput in the order in which they are found.</p> determine what to extract. If that is not present then all the object properties are output in the order in which the properties are found in the first row.</p>
<p>If the input is an array then the columns template is only used to optionally generate a row of column titles.</p> <p>If the input is an array then the columns template is only used to optionally generate a row of column titles.</p>
<p>If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,"1.5",2'.</p> <p>If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,"1.5",2'.</p>
<p>If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '"1","",3'.</p> <p>If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '"1","",3'.</p>

View File

@ -403,7 +403,7 @@
"label": { "label": {
"method": "メソッド", "method": "メソッド",
"url": "URL", "url": "URL",
"doc": "Docs", "doc": "ドキュメント",
"return": "出力形式", "return": "出力形式",
"upload": "ファイルのアップロード", "upload": "ファイルのアップロード",
"status": "ステータスコード", "status": "ステータスコード",

View File

@ -0,0 +1,54 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="inject">
<p>
Запускает сообщение в поток вручную или через равные промежутки времени. Данные сообщения могут быть различных типов, включая строку, объект JavaScript или текущее время.
</p>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">разные</span></dt>
<dd>Настроенные данные сообщения.</dd>
<dt class="optional">topic <span class="property-type">строка</span></dt>
<dd>Необязательное свойство темы сообщения, которое можно настроить в узле.</dd>
</dl>
<h3>Подробности</h3>
<p>
Узел Inject может инициировать поток с определенными данными (значение payload). Данные по умолчанию - это отметка текущего времени в миллисекундах с 1 января 1970 года.
</p>
<p>
Узел также поддерживает вывод строк, чисел, логических значений, объектов JavaScript или значений потоковых/глобальных контекстов.
</p>
<p>
По умолчанию узел запускается вручную при нажатии на его кнопку в редакторе. Его также можно настроить для автоматического запуска через равные промежутки времени или по расписанию.
</p>
<p>
Он также может быть настроен так, чтобы делать вывод один раз при каждом запуске потоков.
</p>
<p>
Максимальный <i>интервал</i>, который можно указать, составляет около 596 часов / 24 дней. Однако если Вам нужны интервалы, превышающие один день, Вам следует рассмотреть возможность использования узла планировщика, который может работать с перебоями электроэнергии и перезапусками.
</p>
<p>
<b>Примечание</b>: В параметрах <i>"с интервалом в промежутке"</i> и <i>"в определенное время"</i> используется стандартная система cron.
Это означает, что 20 минут будут в следующем часу, 20 минут спустя и 40 минут спустя - а не через 20 минут.
Если нужно каждые 20 минут - используйте параметр <i>"с интервалом"</i>.
</p>
<p>
<b>Примечание</b>: Чтобы включить многострочный текст в строковое значение, необходимо использовать узел Function для создания данных.
</p>
</script>

View File

@ -0,0 +1,39 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="debug">
<p>
Отображает выбранные свойства сообщения на боковой панели во вкладке отладки и, при необходимости, журнале выполнения. По умолчанию отображается содержимое <code>msg.payload</code>, но его можно настроить для отображения любого свойства, полного сообщения или результата выражения JSONata.
</p>
<h3>Подробности</h3>
<p>
Вкладка отладки на боковой панели обеспечивает структурированное представление отправляемых сообщений, что упрощает понимание их структуры.
</p>
<p>
Объекты и массивы JavaScript могут быть свернуты и развернуты по мере необходимости. Буферные объекты могут отображаться в виде сырых данных или как строка, когда это возможно.
</p>
<p>
Рядом с каждым сообщением отладочная панель содержит информацию о времени получения сообщения, узле, который его отправил, и типе сообщения.
Нажатие на идентификатор узла-источника, показывает этот узел в рабочей области.
</p>
<p>
Кнопка на узле может использоваться для включения или отключения его вывода. Рекомендуется отключать или удалять любые отладочные узлы, которые не используются.
</p>
<p>
Узел также может быть сконфигурирован для отправки всех сообщений в журнал выполнения или для отправки короткого (32 символа) текста в статус под узлом отладки.
</p>
</script>

View File

@ -0,0 +1,35 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="complete">
<p>
Запустить поток, когда другой узел завершит обработку сообщения.
</p>
<h3>Подробности</h3>
<p>
Если узел сообщает среде выполнения о завершении обработки сообщения, этот узел можно использовать для запуска второго потока.
</p>
<p>
Например, это можно использовать вместе с узлом без выходного порта, таким как узел Email, для продолжения потока.
</p>
<p>
Этот узел должен быть настроен для обработки события выбранных узлов в потоке. В отличие от узла Catch, он не предоставляет режим 'обрабатывать все' для автоматического применения ко всем узлам в потоке.
</p>
<p>
Не все узлы будут инициировать это событие - это будет зависеть от того, была ли в них реализована поддержка этой функции, добавленной в Node-RED 1.0.
</p>
</script>

View File

@ -0,0 +1,50 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="catch">
<p>
Ловит ошибки, выдаваемые узлами на той же вкладке.
</p>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>error.message <span class="property-type">строка</span></dt>
<dd>сообщение об ошибке.</dd>
<dt>error.source.id <span class="property-type">строка</span></dt>
<dd>идентификатор узла, выдавшего ошибку.</dd>
<dt>error.source.type <span class="property-type">строка</span></dt>
<dd>тип узла, выдавшего ошибку.</dd>
<dt>error.source.name <span class="property-type">строка</span></dt>
<dd>имя, если установлено, узла, выдавшего ошибку.</dd>
</dl>
<h3>Подробности</h3>
<p>
Если узел выдает ошибку во время обработки сообщения, поток обычно останавливается. Этот узел можно использовать для отлова этих ошибок и обработки их с помощью отдельного потока.
</p>
<p>
По умолчанию узел будет отлавливать ошибки, генерируемые любым узлом на той же вкладке. По желанию он может быть нацелен на определенные узлы или настроен на перехват только тех ошибок, которые не были еще перехвачены 'нацеленным' Catch узлом.
</p>
<p>
Когда выдается ошибка, все соответствующие Catch узлы получат сообщение.
</p>
<p>
Если ошибка выдается в подпотоке, она будет обработана любыми Catch узлами внутри подпотока. Если таковых нет, ошибка будет распространена до вкладки, на которой находится экземпляр подпотока.
</p>
<p>
Если сообщение уже имеет свойство <code>error</code>, оно копируется в <code>_error</code>.
</p>
</script>

View File

@ -0,0 +1,41 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="status">
<p>
Сообщает о статусах других узлов на той же вкладке.
</p>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>status.text <span class="property-type">строка</span></dt>
<dd>текст статуса.</dd>
<dt>status.source.type <span class="property-type">строка</span></dt>
<dd>тип узла, сообщившего о статусе.</dd>
<dt>status.source.id <span class="property-type">строка</span></dt>
<dd>идентификатор узла, сообщившего о статусе.</dd>
<dt>status.source.name <span class="property-type">строка</span></dt>
<dd>имя, если установлено, узла, сообщившего о статусе.</dd>
</dl>
<h3>Подробности</h3>
<p>
Этот узел не создает <code>payload</code> данные.
</p>
<p>
По умолчанию узел сообщает о статусах всех узлов на той же вкладке в рабочей области. Его можно настроить для выборочного отчета о статусе отдельных узлов.
</p>
</script>

View File

@ -0,0 +1,49 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="link in">
<p>
Создает виртуальный провод между потоками.
</p>
<h3>Подробности</h3>
<p>
Этот узел может быть подключен к любому узлу <code>link out</code> на любой вкладке. После подключения они ведут себя так, как если бы они были соединены вместе обычным проводом.
</p>
<p>
Связи между link-узлами отображаются, только когда выбран link-узел. Если есть какие-либо провода к другим вкладкам, отображается виртуальный узел, по которому можно кликнуть, чтобы перейти на соответствующую вкладку.
</p>
<p>
<b>Примечание:</b> Связи не могут идти внутрь подпотока или изнутри подпотока наружу.
</p>
</script>
<script type="text/html" data-help-name="link out">
<p>
Создает виртуальный провод между потоками.
</p>
<h3>Подробности</h3>
<p>
Узел может быть подключен к любому узлу <code>link in</code> на любой вкладке. После подключения они ведут себя так, как если бы они были соединены вместе обычным проводом.
</p>
<p>
Связи между link-узлами отображаются, только когда выбран link-узел. Если есть какие-либо провода к другим вкладкам, отображается виртуальный узел, по которому можно кликнуть, чтобы перейти на соответствующую вкладку.
</p>
<p>
<b>Примечание:</b> Связи не могут идти внутрь подпотока или изнутри подпотока наружу.
</p>
</script>

View File

@ -0,0 +1,26 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="comment">
<p>
Узел, который Вы можете использовать для добавления комментариев к Вашим потокам.
</p>
<h3>Подробности</h3>
<p>
Панель редактирования поддерживает синтаксис Markdown. Текст будет отображен как 'описание' на этой информационной вкладке.
</p>
</script>

View File

@ -0,0 +1,37 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="unknown">
<p>
Тип этого узла неизвестен Вашему Node-RED.
</p>
<h3>Подробности</h3>
<p>
<i>
Если Вы развернете узел в этом состоянии, его конфигурация будет сохранена, но поток не будет запущен, пока не будет установлен отсутствующий тип.
</i>
</p>
<p>
Используйте опцию <code>Меню - Управление палитрой</code> для поиска и установки узлов или <b>npm install &lt;модуль&gt;</b> для установки любых отсутствующих модулей и перезапустите Node-RED и повторно импортируйте узлы.
</p>
<p>
Возможно, этот тип узла уже установлен, но отсутствуют какие-то из его зависимостей. Проверьте журнал запуска Node-RED на наличие сообщений об ошибках, связанных с отсутствующим типом узла.
</p>
<p>
В противном случае Вам следует связаться с автором потока, чтобы получить копию отсутствующего типа узла.
</p>
</script>

View File

@ -0,0 +1,90 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="function">
<p>
Блок с JavaScript функцией, написанной во вкладке <b>Функция</b>, которая исполняется для сообщений, получаемых узлом.
</p>
<p>
Сообщения передаются в виде объекта JavaScript с именем <code>msg</code>.
</p>
<p>
Обычно он имеет свойство <code>msg.payload</code>, содержащее тело сообщения.
</p>
<p>
Ожидается, что функция вернет объект сообщения (или несколько объектов сообщения), но может также ничего не возвращать, чтобы остановить поток.
</p>
<p>
Код настройки, выполняемый один раз при запуске сервера или при развертывании новой конфигурации потока, можно указать во вкладке <b>Настройка</b>. Кроме того, код очистки, выполняемый при остановке или повторном развертывании узла, можно указать во вкладке <b>Закрытие</b>.
</p>
<p>
Если код настройки возвращает объект Promise, обработка входящих сообщений узлом начнется после его завершения.
</p>
<h3>Подробности</h3>
<p>
Смотрите <a target="_blank" href="http://nodered.org/docs/writing-functions.html">онлайн-документацию</a> для получения дополнительной информации по написанию функций.
</p>
<h4>Отправка сообщений</h4>
<p>
Функция может либо вернуть сообщения, которые она хочет передать следующим узлам в потоке, либо вызвать <code>node.send(сообщения)</code>.
</p>
<p>
Она может вернуть/отправить:
</p>
<ul>
<li>один объект сообщения - передается узлам, подключенным к первому выходу</li>
<li>массив объектов сообщений - передается на узлы, подключенные к соответствующим выходам</li>
</ul>
<p>
Примечание: код настройки выполняется во время инициализации узлов. Таким образом, если на вкладке настройки кода вызывается <code>node.send</code>, последующие узлы могут не получить это сообщение.
</p>
<p>
Если какой-либо элемент массива сам является массивом сообщений, тогда на соответствующий выход отправляется несколько сообщений.
</p>
<p>
Если возвращен null, либо сам по себе, либо как элемент массива, тогда сообщение не передается.
</p>
<h4>Ведение журнала и обработка ошибок</h4>
<p>
Для добавления информации в журнал или сообщения об ошибке доступны следующие функции:
</p>
<ul>
<li><code>node.log("Сообщение для журнала")</code></li>
<li><code>node.warn("Предупреждение")</code></li>
<li><code>node.error("Ошибка")</code></li>
</ul>
<p>
Узел Catch также может использоваться для обработки ошибок. Чтобы узел Catch мог ловить сообщение об ошибке, передайте <code>msg</code> в качестве второго аргумента в <code>node.error</code>:
</p>
<pre>node.error("Ошибка", msg);</pre>
<h4>Доступ к информации об узле</h4>
<p>
В функциональном блоке к идентификатору и имени узла можно обращаться, используя следующие свойства:
</p>
<ul>
<li><code>node.id</code> - идентификатор узла</li>
<li><code>node.name</code> - имя узла</li>
</ul>
<h4>Использование переменных среды</h4>
<p>
Доступ к переменным среды можно получить с помощью <code>env.get("MY_ENV_VAR")</code>.
</p>
</script>

View File

@ -0,0 +1,59 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="switch">
<p>
Направляет сообщения по разным ветвям потока на основе значений свойств сообщения или его позиции в последовательности.
</p>
<h3>Подробности</h3>
<p>
Когда приходит сообщение, узел выполняет проверку соответствия каждому из определенных правил и перенаправляет сообщение на их выходы.
</p>
<p>
При желании узел может быть настроен на прекращение проверки правил, как только он найдет первое подходящее.
</p>
<p>
Правила могут выполняться для отдельного свойства сообщения, свойства потокового или глобального контекста, переменной среды или результата выражения JSONata.
</p>
<h4>Правила</h4>
<p>
Существует четыре типа правил:
</p>
<ol>
<li>Правила <b>value rules</b> сравниваются с указанным свойством</li>
<li>Правила <b>sequence rules</b> могут применяться для последовательностей сообщений, таких как сгенерированные узлом Split</li>
<li><b>Выражение JSONata</b> исполняется для сообщения и считается подходящим, если оно возвращает истинное значение</li>
<li>Правило <b>иначе</b> используется для сообщений, не подходящих ни под одно из предыдущих правил.</li>
</ol>
<h4>Примечание</h4>
<p>
Правила <code>равно true/false</code> и <code>равно null</code> проводят строгое сравнение с этими типами. Они не конвертируются между типами.
</p>
<p>
Правила <code>пустое</code> и <code>не пустое</code> могут быть использованы для проверки длины строк, массивов и буферов, или количества свойств, которые содержит объект. Эти правила не подходят, если тестируемое свойство содержит значение <code>логического типа</code>, <code>null</code> или <code>undefined</code>.
</p>
<h4>Обработка последовательностей сообщений</h4>
<p>
По умолчанию узел не изменяет свойство <code>msg.parts</code> у сообщений, которые являются частью последовательности.
</p>
<p>
Можно включить параметр <b>пересоздавать последовательности сообщений</b> для создания новых последовательностей сообщений для каждого соответствующего правила. В этом режиме узел будет помещать в буфер всю входящую последовательность перед отправкой новых последовательностей. Настройка <code>nodeMessageBufferMaxLength</code> может быть использована для ограничения количества сообщений в буфере.
</p>
</script>

View File

@ -0,0 +1,42 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="change">
<p>
Устанавливает, изменяет, удаляет или перемещает свойства сообщения, контекста потока или глобального контекста.
</p>
<p>
В узле можно указать несколько правил, которые будут применяться в том порядке, в котором они определены.
</p>
<h3>Подробности</h3>
<p>
Доступные операции:
</p>
<dl class="message-properties">
<dt>Установить</dt>
<dd>установить свойство. Значение может быть разных типов или может быть взято из существующего свойства сообщения или контекста.</dd>
<dt>Изменить</dt>
<dd>найти &amp; заменить части свойства. Если используется регулярное выражение, настройка &quot;заменить на&quot; может включать группы захвата, например <code>$1</code>. При полном совпадении заменяет только тип.</dd>
<dt>Удалить</dt>
<dd>удалить свойство.</dd>
<dt>Переместить</dt>
<dd>переместить или переименовать свойство.</dd>
</dl>
<p>
Тип свойства &quot;выражение&quot; использует язык запросов и выражений <a href="http://jsonata.org/" target="_new">JSONata</a>.
</p>
</script>

View File

@ -0,0 +1,59 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="range">
<p>
Соотносит значение одного числового диапазона к другому числовому диапазону.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">число</span></dt>
<dd>Данные <i>должны</i> быть числом. Данные другого типа будут преобразованы в число и отклонены, если это не удастся.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">число</span></dt>
<dd>Значение, соотнесенное к новому диапазону.</dd>
</dl>
<h3>Подробности</h3>
<p>
Этот узел будет линейно масштабировать полученное значение. По умолчанию результат не ограничен диапазоном, определенным в узле.
</p>
<p>
<i>Масштабировать и ограничить целевым диапазоном</i> означает, что результат никогда не будет выходить за пределы диапазона, указанного в целевом диапазоне.
</p>
<p>
<i>Масштабировать и обернуть в целевой диапазон</i> означает, что результат будет обернут вокруг целевого диапазона:
</p>
<p>
Например, для входа 0 - 10 сопоставленного с 0 - 100.
</p>
<table style="outline-width:#888 solid thin">
<tr><th width="80px">режим</th><th width="80px">вход</th><th width="80px">выход</th></tr>
<tr><td><center>масштабировать</center></td><td><center>2</center></td><td><center>20</center></td></tr>
<tr><td><center>ограничить</center></td><td><center>2</center></td><td><center>20</center></td></tr>
<tr><td><center>обернуть</center></td><td><center>2</center></td><td><center>20</center></td></tr>
<tr><td><center>масштабировать</center></td><td><center>12</center></td><td><center>120</center></td></tr>
<tr><td><center>ограничить</center></td><td><center>12</center></td><td><center>100</center></td></tr>
<tr><td><center>обернуть</center></td><td><center>12</center></td><td><center>20</center></td></tr>
<tr><td><center>масштабировать</center></td><td><center>18</center></td><td><center>180</center></td></tr>
<tr><td><center>ограничить</center></td><td><center>18</center></td><td><center>100</center></td></tr>
<tr><td><center>обернуть</center></td><td><center>18</center></td><td><center>80</center></td></tr>
</table>
</script>

View File

@ -0,0 +1,63 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="template">
<p>
Устанавливает свойство на основе предоставленного шаблона.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">объект</span></dt>
<dd>Объект сообщения msg, содержащий информацию для заполнения шаблона.</dd>
<dt class="optional">шаблон <span class="property-type">строка</span></dt>
<dd>Шаблон для заполнения данными из сообщения msg. Если шаблон не настроен на панели редактирования, тогда он может быть установлен через свойство msg.template.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">объект</span></dt>
<dd>Сообщение msg, у которого выбранному свойству присвоено значение, сформированное заполнением шаблона свойствами из входящего сообщения.</dd>
</dl>
<h3>Подробности</h3>
<p>
По умолчанию используется формат <i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>, но это может быть выключено при необходимости.
</p>
<p>
Например, когда шаблон:
</p>
<pre>Привет, {{payload.name}}. Сегодня {{date}}</pre>
<p>
получает сообщение msg, содержащее:
</p>
<pre>{
date: "понедельник",
payload: {
name: "Иван"
}
}</pre>
<p>
Результирующее свойство будет:
</p>
<pre>Привет, Иван. Сегодня понедельник</pre>
<p>
Можно использовать свойство из контекста потока или глобального контекста. Просто используйте <code>{{flow.name}}</code> или <code>{{global.name}}</code>, или для постоянного хранилища <code>store</code> используйте <code>{{flow[store].name}}</code> или <code>{{global[store].name}}</code>.
</p>
<p>
<b>Примечание:</b> по умолчанию <i>mustache</i> кодирует любые не алфавитно-цифровых или HTML-сущности в значениях, которые он подставляет, для безопасного использования в HTML. Чтобы предотвратить это, используйте тройные фигурные скобки <code>{{{triple}}}</code>.
</p>
</script>

View File

@ -0,0 +1,40 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="delay">
<p>
Задерживает каждое сообщение, проходящее через узел, или ограничивает скорость, с которой они могут проходить.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">число</span></dt>
<dd>Устанавливает задержку в миллисекундах, которая будет применена к сообщению. Этот параметр применяется только в том случае, если узел настроен на то, чтобы разрешать сообщению переопределять установленный интервал задержки по умолчанию.</dd>
<dt class="optional">reset</dt>
<dd>Если в полученном сообщении этому свойству присвоено какое-либо значение, все ожидающие сообщения, удерживаемые узлом, очищаются без дальнейшей отправки.</dd>
<dt class="optional">flush</dt>
<dd>Если в полученном сообщении этому свойству присвоено какое-либо значение, все ожидающие сообщения, удерживаемые узлом, немедленно отправляются далее.</dd>
</dl>
<h3>Подробности</h3>
<p>
Когда настроено задерживать сообщения, интервал задержки может быть фиксированным значением, случайным значением в пределах диапазона или динамически установленным для каждого сообщения. Каждое сообщение задерживается независимо от любых других сообщений, основываясь на времени его прибытия.
</p>
<p>
Когда настроено ограничивать скорость сообщений, их доставка распределяется по настроенному периоду времени. Статус показывает количество сообщений, находящихся в данный момент в очереди. Если выбрано он может отбрасывать промежуточные сообщений по мере их поступления.
</p>
<p>
Ограничение скорости может применяться ко всем сообщениям или группировать их в соответствии со значением <code>msg.topic</code>. При группировании промежуточные сообщения автоматически отбрасываются. В каждом интервале времени узел может либо выпустить самое последнее сообщение для всех тем, либо выпустить самое последнее сообщение для следующей темы.
</p>
</script>

View File

@ -0,0 +1,55 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="trigger">
<p>
При срабатывании может отправить сообщение, а затем дополнительно второе сообщение, если оно не продлено или не сброшено.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">число</span></dt>
<dd>Устанавливает задержку в миллисекундах, которая будет применяться к сообщению. Этот параметр применяется только в том случае, если узел настроен так, чтобы сообщение могло отменять настроенный интервал задержки по умолчанию.</dd>
<dt class="optional">reset</dt>
<dd>Если сообщение получено с этим свойством, любой тайм-аут или повтор, находящийся в обработке в текущий момент, будет сброшен, и сообщение не сработает.</dd>
</dl>
<h3>Подробности</h3>
<p>
Этот узел можно использовать для создания тайм-аута в потоке. По умолчанию, когда узел получает сообщение, он отправляет сообщение с <code>payload</code> равным <code>1</code>. Затем он ждет 250 мс, прежде чем отправить второе сообщение с <code>payload</code> равным <code>0</code>. Это можно использовать, например, для мигания светодиода, подключенного к выводу Raspberry Pi GPIO.
</p>
<p>
Данные каждого отправленного сообщения могут быть настроены на различные значения, включая возможность ничего не отправлять. Например, если установить начальное сообщение на <i>ничего</i> и выбрать опцию продления таймера с каждым новым сообщением, узел будет действовать как сторожевой таймер, отправляя сообщение только в том случае, если ничего не получено в течение установленного интервала.
</p>
<p>
Если установлен тип <i>строка</i>, узел поддерживает синтаксис шаблона mustache.
</p>
<p>
Задержка между отправкой сообщений может быть изменена с помощью <code>msg.delay</code>, если эта опция включена в узле. Значение должно быть указано в миллисекундах.
</p>
<p>
Если узел получает сообщение со свойством <code>reset</code> или <code>payload</code>, который совпадает с настроенным в узле, любой тайм-аут или повтор, находящийся в обработке в текущий момент, будет сброшен, и сообщение не сработает.
</p>
<p>
Узел может быть настроен на повторную отправку сообщения с регулярным интервалом, пока оно не будет сброшено полученным сообщением.
</p>
<p>
При желании узел может быть настроен на обработку сообщений, как если бы они были отдельными потоками, используя свойство msg для идентификации каждого потока. По умолчанию <code>msg.topic</code>.
</p>
<p>
Статус показывает, что узел в данный момент активен. Если используется несколько потоков, статус показывает количество удерживаемых потоков.
</p>
</script>

View File

@ -0,0 +1,106 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="exec">
<p>
Запускает системную команду и возвращает ее вывод.
</p>
<p>
Узел может быть настроен либо на ожидание завершения команды, либо на отправку своих выходных данных, пока команда их генерирует.
</p>
<p>
Выполняемая команда может быть настроена в узле или предоставлена полученным сообщением.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">payload <span class="property-type">строка</span></dt>
<dd>будет добавлено к выполненной команде, если настроено так делать.</dd>
<dt class="optional">kill <span class="property-type">строка</span></dt>
<dd>тип сигнала уничтожения для отправки существующему процессу узла exec.</dd>
<dt class="optional">pid <span class="property-type">число|строка</span></dt>
<dd>идентификатор существующего процесса узла exec для уничтожения.</dd>
</dl>
<h3>Выводит</h3>
<ol class="node-ports">
<li>Стандартный вывод
<dl class="message-properties">
<dt>payload <span class="property-type">строка</span></dt>
<dd>стандартный вывод команды.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">объект</span></dt>
<dd>только в exec режиме, копия объекта кода возврата (также доступна на порту 3)</dd>
</dl>
</li>
<li>Стандартный вывод ошибок
<dl class="message-properties">
<dt>payload <span class="property-type">строка</span></dt>
<dd>стандартная ошибка команды.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">объект</span></dt>
<dd>только в exec режиме, копия объекта кода возврата (также доступна на порту 3)</dd>
</dl>
</li>
<li>Код возврата
<dl class="message-properties">
<dt>payload <span class="property-type">объект</span></dt>
<dd>объект, содержащий код возврата и, возможно, свойства <code>message</code>, <code>signal</code>.</dd>
</dl>
</li>
</ol>
<h3>Подробности</h3>
<p>
По умолчанию используется системный вызов <code>exec</code>, который вызывает команду, ожидает ее завершения и возвращает результат. Например, успешная команда должна иметь код возврата <code>{code: 0}</code>.
</p>
<p>
При желании вместо этого можно использовать <code>spawn</code>, который возвращает выходные данные из stdout и stderr по ходу выполнения команды, обычно по одной строке за раз. После завершения он возвращает объект на 3-й порт. Например, успешная команда должна вернуть <code>{code: 0}</code>.
</p>
<p>
Ошибки могут возвращать дополнительную информацию на 3-м порту в <code>msg.payload</code>, такую как строка <code>message</code>, строка <code>signal</code>.
</p>
<p>
Выполняемая команда настраивается в узле, с возможностью добавления к ней <code>msg.payload</code> и дополнительного набора параметров.
</p>
<p>
Команды или параметры с пробелами должны быть заключены в кавычки - <code>"Это один параметр"</code>
</p>
<p>
Возвращаемый <code>payload</code> обычно представляет собой <i>строку</i>, пока не обнаружены символы, отличные от UTF8, в этом случае это <i>буфер</i>.
</p>
<p>
Значок состояния узла и PID будут видны, пока узел активен. Изменения в статусе могут быть прочитаны узлом <code>Status</code>.
</p>
<h4>
Уничтожения процессов
</h4>
<p>
Отправка <code>msg.kill</code> уничтожит один активный процесс. <code>msg.kill</code> должен быть строкой, содержащей тип передаваемого сигнала, например, <code>SIGINT</code>, <code>SIGQUIT</code> или <code>SIGHUP</code>. По умолчанию <code>SIGTERM</code>, если задана пустая строка.
</p>
<p>
Если узлом запущено более одного процесса, тогда в <code>msg.pid</code> также должно быть установлено значение PID для уничтожения.
</p>
<p>
Если в поле <code>Тайм-аут</code> указано значение, то, если процесс не завершится по истечении указанного количества секунд, он будет автоматически уничтожен
</p>
<p>
Совет: если Вы запускаете Python приложение, Вам может потребоваться использовать параметр <code>-u</code>, чтобы остановить буферизацию вывода.
</p>
</script>

View File

@ -0,0 +1,969 @@
{
"common": {
"label": {
"payload": "Данные",
"topic": "Тема",
"name": "Имя",
"username": "Имя польз.",
"password": "Пароль",
"property": "Свойство",
"selectNodes": "Выберите узлы...",
"expand": "Развернуть"
},
"status": {
"connected": "подключен",
"not-connected": "не подключен",
"disconnected": "соединение разорвано",
"connecting": "идет подключение",
"error": "ошибка",
"ok": "OK"
},
"notification": {
"error": "<strong>Ошибка</strong>: __message__",
"errors": {
"not-deployed": "узел не развернут",
"no-response": "нет ответа от сервера",
"unexpected": "непредвиденная ошибка (__status__) __message__"
}
},
"errors": {
"nooverride": "Предупреждение: свойства msg больше не могут переопределять установленные свойства узла. Смотрите bit.ly/nr-override-msg-props"
}
},
"inject": {
"inject": "запустить",
"repeat": "повторы = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "остановлено",
"failed": "Не удалось запустить: __error__",
"label": {
"properties": "Свойства",
"repeat": "Повторять",
"flow": "контекст потока",
"global": "глобальный контекст",
"str": "строка",
"num": "число",
"bool": "логический тип",
"json": "объект",
"bin": "буфер",
"date": "отметка времени",
"env": "переменная среды",
"object": "объект",
"string": "строка",
"boolean": "логический тип",
"number": "число",
"Array": "массив",
"invalid": "Неверный объект JSON"
},
"timestamp": "отметка времени",
"none": "нет",
"interval": "с интервалом",
"interval-time": "с интервалом в промежутке",
"time": "в определенное время",
"seconds": "сек",
"minutes": "мин",
"hours": "час",
"between": "между",
"previous": "предыдущее значение",
"at": "в",
"and": "и",
"every": "каждые",
"days": [
"пн",
"вт",
"ср",
"чт",
"пт",
"сб",
"вс"
],
"on": "по",
"onstart": "Отправить через",
"onceDelay": "сек, затем",
"success": "Успешно отправлено: __label__",
"errors": {
"failed": "запуск не удался, смотрите подробности в журнале",
"toolong": "Интервал слишком большой",
"invalid-expr": "Неверное выражение JSONata: __error__"
}
},
"catch": {
"catch": "ловить: все",
"catchNodes": "ловить: __number__",
"catchUncaught": "ловить: непойманные",
"label": {
"source": "Ловить ошибки",
"selectAll": "выбрать все",
"uncaught": "Игнорировать ошибки, пойманные другими узлами Catch"
},
"scope": {
"all": "всех узлов",
"selected": "выбранных узлов"
}
},
"status": {
"status": "статус: все",
"statusNodes": "статус: __number__",
"label": {
"source": "Получать статус",
"sortByType": "сортировать по типу"
},
"scope": {
"all": "всех узлов",
"selected": "выбранных узлов"
}
},
"complete": {
"completeNodes": "завершение: __number__"
},
"debug": {
"output": "Выводить",
"status": "статус",
"none": "Нет",
"invalid-exp": "Неверное выражение JSONata: __error__",
"msgprop": "свойство сообщения",
"msgobj": "весь msg объект сообщения",
"autostatus": "то, что выводит этот узел",
"to": "В",
"debtab": "вкладка отладки",
"tabcon": "вкладка отладки и консоль",
"toSidebar": "окно отладки",
"toConsole": "системную консоль",
"toStatus": "статус узла (32 символа)",
"severity": "Уровень",
"notification": {
"activated": "Успешно активирован: __label__",
"deactivated": "Успешно деактивирован: __label__"
},
"sidebar": {
"label": "отладка",
"name": "Отладочные сообщения",
"filterAll": "все узлы",
"filterSelected": "выбранные узлы",
"filterCurrent": "текущий поток",
"debugNodes": "Отладочные узлы",
"clearLog": "Очистить журнал",
"filterLog": "Фильтровать журнал",
"openWindow": "Открыть в новом окне",
"copyPath": "Копировать путь",
"copyPayload": "Копировать значение",
"pinPath": "Закрепить видимость"
},
"messageMenu": {
"collapseAll": "Свернуть все пути",
"clearPinned": "Очистить закреп. пути",
"filterNode": "Фильтровать этот узел",
"clearFilter": "Очистить фильтр"
}
},
"link": {
"linkIn": "связь (вход)",
"linkOut": "связь (выход)"
},
"tls": {
"tls": "Конфигурация TLS",
"label": {
"use-local-files": "Использовать ключ и сертификаты из локальных файлов",
"upload": "Загрузить",
"cert": "Сертификат",
"key": "Закрытый ключ",
"passphrase": "Пароль",
"ca": "CA сертификат",
"verify-server-cert":"Проверить сертификат сервера",
"servername": "Имя сервера"
},
"placeholder": {
"cert":"путь к сертификату (формат PEM)",
"key":"путь к закрытому ключу (формат PEM)",
"ca":"путь к сертификату CA (формат PEM)",
"passphrase":"пароль закрытого ключа (необязательно)",
"servername":"для использования с SNI"
},
"error": {
"missing-file": "Файл сертификата/ключа не предоставлен"
}
},
"exec": {
"exec": "выполнить",
"spawn": "выполнить в новом процессе",
"label": {
"command": "Команда",
"append": "Добавить",
"timeout": "Тайм-аут",
"timeoutplace": "необяз",
"return": "Вывод",
"seconds": "сек",
"stdout": "стандартный вывод",
"stderr": "стандартный вывод ошибок",
"retcode": "код возврата"
},
"placeholder": {
"extraparams": "дополнительные параметры"
},
"opt": {
"exec": "когда команда завершена - exec режим",
"spawn": "пока команда работает - spawn режим"
},
"oldrc": "Использовать вывод в старом стиле (режим совместимости)"
},
"function": {
"function": "",
"label": {
"function": "Функция",
"initialize": "Настройка",
"finalize": "Закрытие",
"outputs": "Выходы"
},
"text": {
"initialize": "// Добавленный здесь код будет исполняться\n// однократно при развертывании узла.\n",
"finalize": "// Добавленный здесь код будет исполняться при\n// остановке узла или повторном развертывании.\n"
},
"error": {
"inputListener":"Невозможно добавить слушателя к событию 'input' в функции",
"non-message-returned":"Функция пыталась отправить сообщение типа __type__"
}
},
"template": {
"template": "шаблон",
"label": {
"template": "Шаблон",
"property": "Свойство",
"format": "Подсветка синтаксиса",
"syntax": "Формат",
"output": "Вывести как",
"mustache": "Mustache шаблон",
"plain": "Простой текст",
"json": "JSON",
"yaml": "YAML",
"none": "нет"
},
"templatevalue": "А вот и данные: {{payload}} !"
},
"delay": {
"action": "Действие",
"for": "На",
"delaymsg": "Задержка каждого сообщения",
"delayfixed": "Фиксированная задержка",
"delayvarmsg": "Переопределять задержку с msg.delay",
"randomdelay": "Случайная задержка",
"limitrate": "Ограничение скорости",
"limitall": "Все сообщения",
"limittopic": "Для каждого msg.topic",
"fairqueue": "Отправлять темы по очереди",
"timedqueue": "Отправлять все темы",
"milisecs": "мсек",
"secs": "сек",
"sec": "сек",
"mins": "мин",
"min": "мин",
"hours": "час",
"hour": "час",
"days": "сут",
"day": "сут",
"between": "Между",
"and": "и",
"rate": "Скорость",
"msgper": "сообщений в",
"dropmsg": "без промежуточных сообщений",
"label": {
"delay": "задержка",
"variable": "переменная",
"limit": "ограничение",
"limitTopic": "ограничение темы",
"random": "случайный",
"units" : {
"second": {
"plural" : "сек",
"singular": "сек"
},
"minute": {
"plural" : "мин",
"singular": "мин"
},
"hour": {
"plural" : "час",
"singular": "час"
},
"day": {
"plural" : "сут",
"singular": "сут"
}
}
},
"error": {
"buffer": "буфер превысил 1000 сообщений",
"buffer1": "буфер превысил 10000 сообщений"
}
},
"trigger": {
"send": "Отправить",
"then": "затем",
"then-send": "затем отправить",
"output": {
"string": "строку",
"number": "число",
"existing": "существующий объект сообщения",
"original": "оригинальный объект сообщения",
"latest": "последний объект сообщения",
"nothing": "ничего"
},
"wait-reset": "ждать сброс",
"wait-for": "ждать",
"wait-loop": "переотправлять его каждые",
"for": "Обрабатывать",
"bytopics": "каждый",
"alltopics": "все сообщения",
"duration": {
"ms": "мсек",
"s": "сек",
"m": "мин",
"h": "час"
},
"extend": " продлить при поступлении нового сообщения",
"override": "заменить задержку через msg.delay",
"second": " отправить второе сообщение на отдельный выход",
"label": {
"trigger": "триггер",
"trigger-block": "триггер & блок",
"trigger-loop": "переотправлять каждые",
"reset": "Сбрасывать триггер, если:",
"resetMessage":"установлен msg.reset",
"resetPayload":"msg.payload равен",
"resetprompt": "необязательно"
}
},
"comment": {
"comment": "комментарий"
},
"unknown": {
"label": {
"unknown": "неизвестный"
},
"tip": "<p>Тип этого узла неизвестен Вашей установке Node-RED.</p><p><i>Если Вы развернете узел в этом состоянии, его конфигурация будет сохранена, но поток не будет запущен, пока отсутствующий тип не будет установлен.</i></p><p>Дополнительную справку смотрите в информационной вкладке на боковой панели</p>"
},
"mqtt": {
"label": {
"broker": "Сервер",
"example": "например, localhost",
"output": "Выход",
"qos": "QoS",
"retain": "Хранить",
"clientid": "ID клиента",
"port": "Порт",
"keepalive": "Keep-alive время (сек)",
"cleansession": "Использовать чистую сессию",
"use-tls": "Включить безопасное (SSL/TLS) соединение",
"tls-config":"Конфигурация TLS",
"verify-server-cert":"Проверить сертификат сервера",
"compatmode": "Использовать устаревшую поддержку MQTT 3.1"
},
"sections-label":{
"birth-message": "Сообщение отправляемое при подключении (birth сообщение)",
"will-message":"Сообщение отправляемое при неожиданном отключении (will message)",
"close-message":"Сообщение отправляемое перед отключением (close сообщение)"
},
"tabs-label": {
"connection": "Соединение",
"security": "Безопасность",
"messages": "Сообщения"
},
"placeholder": {
"clientid": "Оставьте пустым для автоматически сгенерированного",
"clientid-nonclean":"Должен быть установлен для не чистых сессий",
"will-topic": "Оставьте пустым, чтобы отключить will сообщение",
"birth-topic": "Оставьте пустым, чтобы отключить birth сообщение",
"close-topic": "Оставьте пустым, чтобы отключить close сообщение"
},
"state": {
"connected": "Подключен к брокеру: __broker__",
"disconnected": "Отключен от брокера: __broker__",
"connect-failed": "Не удалось подключиться к брокеру: __broker__"
},
"retain": "Хранить",
"output": {
"buffer": "буфер",
"string": "строка",
"base64": "строка в кодировке Base64",
"auto": "автоопределение (строка или буфер)",
"json": "объект JSON"
},
"true": "да",
"false": "нет",
"tip": "Совет: Оставьте тему, qos или хранение пустыми, если Вы хотите устанавливать их через свойства msg.",
"errors": {
"not-defined": "тема не определена",
"missing-config": "отсутствует конфигурация брокера",
"invalid-topic": "Указана неверная тема",
"nonclean-missingclientid": "ID клиента не установлен, используется чистая сессия",
"invalid-json-string": "Неверная строка JSON",
"invalid-json-parse": "Не удалось проанализировать строку JSON"
}
},
"httpin": {
"label": {
"method": "Метод",
"url": "URL",
"doc": "Docs",
"return": "Возврат",
"upload": "Принимать загрузки файлов?",
"status": "Код состояния",
"headers": "Заголовки",
"other": "другое",
"paytoqs": {
"ignore": "Игнорировать",
"query": "Добавлять к параметрам строки запроса",
"body": "Отправлять как тело запроса"
},
"utf8String": "Строка UTF8",
"binaryBuffer": "двоичный буфер",
"jsonObject": "объект JSON",
"authType": "Тип",
"bearerToken": "Токен"
},
"setby": "- устанавливается через msg.method -",
"basicauth": "Использовать аутентификацию",
"use-tls": "Включить безопасное (SSL/TLS) соединение",
"tls-config":"Конфигурация TLS",
"basic": "basic аутентификация",
"digest": "digest аутентификация",
"bearer": "bearer authentication",
"use-proxy": "Использовать прокси",
"persist": "Включить keep-alive соединение",
"proxy-config": "Конфигурация прокси",
"use-proxyauth": "Использовать прокси-аутентификацию",
"noproxy-hosts": "Игнор. хосты",
"utf8": "строка UTF-8",
"binary": "двоичный буфер",
"json": "объект JSON",
"tip": {
"in": "URL будет относительно ",
"res": "Сообщения, отправляемые на этот узел <b>должны</b> отправляться с узла <i>http input</i>",
"req": "Совет: Если анализ JSON не удается, полученная строка возвращается как есть."
},
"httpreq": "http запрос",
"errors": {
"not-created": "Невозможно создать узел http-in, когда httpNodeRoot установлено в false",
"missing-path": "пропущен путь",
"no-response": "Нет объекта ответа",
"json-error": "Ошибка анализа JSON",
"no-url": "URL не указан",
"deprecated-call":"Устаревший вызов __method__",
"invalid-transport":"запрошен не-http транспорт",
"timeout-isnan": "Значение тайм-аута не является действительным числом, проигнорировано",
"timeout-isnegative": "Значение тайм-аута отрицательно, проигнорировано",
"invalid-payload": "Неверные данные"
},
"status": {
"requesting": "запрос"
}
},
"websocket": {
"label": {
"type": "Тип",
"path": "Путь",
"url": "URL"
},
"listenon": "Слушать на ...",
"connectto": "Присоединиться к ...",
"sendrec": "Отправить/Получить",
"payload": "msg.payload данные",
"message": "весь msg объект сообщение",
"tip": {
"path1": "По умолчанию <code>payload</code> будет содержать данные, которые будут отправлены или получены из websocket. Слушатель может быть настроен на отправку или получение всего объекта сообщения в виде строки в формате JSON.",
"path2": "Путь будет относительно <code>__path__</code>.",
"url1": "URL должен использовать схему ws:&#47;&#47; или wss:&#47;&#47; и указывать на существующего слушателя websocket.",
"url2": "По умолчанию <code>payload</code> будет содержать данные, которые будут отправлены или получены из websocket. Клиент может быть настроен на отправку или получение всего объекта сообщения в виде строки в формате JSON."
},
"status": {
"connected": "подключен __count__",
"connected_plural_2": "подключено __count__",
"connected_plural_5": "подключено __count__"
},
"errors": {
"connect-error": "Произошла ошибка в соединении ws: ",
"send-error": "Произошла ошибка при отправке: ",
"missing-conf": "Отсутствует конфигурация сервера",
"duplicate-path": "Не может быть двух слушателей WebSocket по одному пути: __path__"
}
},
"watch": {
"watch": "наблюдение",
"label": {
"files": "Файл(ы)",
"recursive": "Наблюдать за подкаталогами рекурсивно"
},
"placeholder": {
"files": "Разделенный запятыми список файлов и/или каталогов"
},
"tip": "В Windows Вы должны использовать двойную обратную косую черту \\\\ в любых именах каталогов."
},
"tcpin": {
"label": {
"type": "Тип",
"output": "Вывод",
"port": "порт",
"host": "к хосту",
"payload": "данные",
"delimited": "разделены с",
"close-connection": "Закрывать соединение после отправки каждого сообщения?",
"decode-base64": "Расшифровать сообщение Base64?",
"server": "Сервер",
"return": "Возврат",
"ms": "мсек",
"chars": "симв."
},
"type": {
"listen": "Слушать",
"connect": "Подключиться",
"reply": "Ответить на TCP"
},
"output": {
"stream": "поток",
"single": "одиночный",
"buffer": "буфер",
"string": "строка",
"base64": "строка Base64"
},
"return": {
"timeout": "по истечении времени ожидания",
"character": "когда получен символ",
"number": "по получении N символов",
"never": "никогда - держать соединение открытым",
"immed": "немедленно - не ждать ответа"
},
"status": {
"connecting": "подключение к __host__:__port__",
"connected": "подключен к __host__:__port__",
"listening-port": "прослушивание порта __port__",
"stopped-listening": "прослушивание порта остановлено",
"connection-from": "соединение от __host__:__port__",
"connection-closed": "закрыто соединение от __host__:__port__",
"connections": "__count__ соединение",
"connections_plural_2": "__count__ соединения",
"connections_plural_5": "__count__ соединений"
},
"errors": {
"connection-lost": "потеряно соединение с __host__:__port__",
"timeout": "сокет на порту __port__ закрыт из-за превышения времени ожидания",
"cannot-listen": "невозможно прослушивать порт __port__, ошибка: __error__",
"error": "ошибка: __error__",
"socket-error": "ошибка сокета от __host__:__port__",
"no-host": "Хост и/или порт не установлены",
"connect-timeout": "превышено время ожидания подключения",
"connect-fail": "подключение не удалось"
}
},
"udp": {
"label": {
"listen": "Слушать",
"onport": "на порте",
"using": "используя",
"output": "Выход",
"group": "Группа",
"interface": "Локал. IF",
"send": "Отправлять",
"toport": "на порт",
"address": "Адрес",
"decode-base64": "Декодировать данные кодированные в Base64?"
},
"placeholder": {
"interface": "(необяз) локальный интерфейс или адрес для привязки",
"interfaceprompt": "(необяз) локальный интерфейс или адрес для привязки",
"address": "IP-адрес назначения"
},
"udpmsgs": "UDP сообщения",
"mcmsgs": "многоадресные сообщения",
"udpmsg": "UDP сообщение",
"bcmsg": "широковещательное сообщение",
"mcmsg": "многоадресное сообщение",
"output": {
"buffer": "буфер",
"string": "строка",
"base64": "строка Base64"
},
"bind": {
"random": "привязать к случайному локальному порту",
"local": "привязать к локальному порту",
"target": "привязать к целевому порту"
},
"tip": {
"in": "Совет: убедитесь, что Ваш брандмауэр разрешит вхождение данных.",
"out": "Совет: оставьте адрес и порт пустыми, если вы хотите установить их, используя <code>msg.ip</code> и <code>msg.port</code>.",
"port": "Уже используемые порты: "
},
"status": {
"listener-at": "слушатель udp на __host__:__port__",
"mc-group": "группа многоадресной рассылки udp __group__",
"listener-stopped": "слушатель udp остановлен",
"output-stopped": "выход udp остановлен",
"mc-ready": "многоадресная рассылка udp готова: __iface__:__outport__ -> __host__:__port__",
"bc-ready": "широковещательная рассылка udp готова: __outport__ -> __host__:__port__",
"ready": "udp готов: __outport__ -> __host__:__port__",
"ready-nolocal": "udp готов: __host__:__port__",
"re-use": "сокет повторного использования udp: __outport__ -> __host__:__port__"
},
"errors": {
"access-error": "Ошибка доступа UDP, Вам может потребоваться доступ с правами root для портов ниже 1024",
"error": "ошибка: __error__",
"bad-mcaddress": "Неверный адрес многоадресной рассылки",
"interface": "Должен быть IP-адрес требуемого интерфейса",
"ip-notset": "UDP: IP-адрес не установлен",
"port-notset": "UDP: порт не установлен",
"port-invalid": "UDP: номер порта недействителен",
"alreadyused": "UDP: порт __port__ уже используется",
"ifnotfound": "UDP: интерфейс __iface__ не найден"
}
},
"switch": {
"switch": "направить",
"label": {
"property": "Свойство",
"rule": "правило",
"repair": "воссоздать последовательность сообщений"
},
"previous": "предыдущее значение",
"and": "и",
"checkall": "проверка всех правил",
"stopfirst": "остановка после первого совпадения",
"ignorecase": "игнорировать регистр",
"rules": {
"btwn": "между",
"cont": "содержит",
"regex": "подходит под регул. выраж.",
"true": "равно true",
"false": "равно false",
"null": "равно null",
"nnull": "не равно null",
"istype": "является типом",
"empty": "пустое",
"nempty": "не пустое",
"head": "первые N",
"tail": "последние N",
"index": "индекс между",
"exp": "выраж. JSONata",
"else": "иначе",
"hask": "имеет ключ"
},
"errors": {
"invalid-expr": "Неверное выражение JSONata: __error__",
"too-many": "слишком много ожидающих сообщений в узле switch"
}
},
"change": {
"label": {
"rules": "Правила",
"rule": "правило",
"set": "установить __property__",
"change": "изменить __property__",
"delete": "удалить __property__",
"move": "переместить __property__",
"changeCount": "изменить: __count__ правило",
"changeCount_plural_2": "изменить: __count__ правила",
"changeCount_plural_5": "изменить: __count__ правил",
"regex": "Использовать регул. выражение"
},
"action": {
"set": "Установить",
"change": "Изменить",
"delete": "Удалить",
"move": "Переместить",
"to": "в",
"search": "Искать",
"replace": "Заменить на"
},
"errors": {
"invalid-from": "Неверное свойство 'from': __error__",
"invalid-json": "Неверное свойство JSON 'to'",
"invalid-expr": "Неверное выражение JSONata: __error__",
"no-override": "Невозможно установить свойство необъектного типа: __property__"
}
},
"range": {
"range": "Диапазон",
"label": {
"action": "Действие",
"inputrange": "Сопоставить входной диапазон",
"resultrange": "с целевым диапазоном",
"from": "от",
"to": "до",
"roundresult": "Округлять результат до ближайшего целого числа?"
},
"placeholder": {
"min": "напр. 0",
"maxin": "напр. 99",
"maxout": "напр. 255"
},
"scale": {
"payload": "Масштабировать msg-свойство",
"limit": "Масштабировать и ограничить целевым диапазоном",
"wrap": "Масштабировать и обернуть в целевой диапазон"
},
"tip": "Совет: этот узел работает ТОЛЬКО с числами.",
"errors": {
"notnumber": "Не число"
}
},
"csv": {
"label": {
"columns": "Столбцы",
"separator": "Разделитель",
"c2o": "Опции CSV -> Объект",
"o2c": "Опции Объект -> CSV",
"input": "Вход",
"skip-s": "Пропускать первые",
"skip-e": "строк(и)",
"firstrow": "первый ряд содержит имена столбцов",
"output": "Выход",
"includerow": "включать ряд с именами столбцов",
"newline": "Новая строка",
"usestrings": "разбирать числовые значения",
"include_empty_strings": "включать пустые строковые значения",
"include_null_values": "включать null-значения"
},
"placeholder": {
"columns": "имена столбцов через запятую"
},
"separator": {
"comma": "запятая",
"tab": "табуляция",
"space": "пробел",
"semicolon": "точка с запятой",
"colon": "двоеточие",
"hashtag": "хэштег",
"other": "другой..."
},
"output": {
"row": "сообщение для каждой строки",
"array": "одно сообщение [массив]"
},
"newline": {
"linux": "Linux (\\n)",
"mac": "Mac (\\r)",
"windows": "Windows (\\r\\n)"
},
"hdrout": {
"none": "никогда не отправлять заголовки столбцов",
"all": "всегда отправлять заголовки столбцов",
"once": "отправлять заголовки один раз, до msg.reset"
},
"errors": {
"csv_js": "Этот узел обрабатывает только CSV-строки или объекты js.",
"obj_csv": "Не указан шаблон столбцов для Объект -> CSV.",
"bad_csv": "Неверно сформированный CSV-файл - возможно, выход поврежден."
}
},
"html": {
"label": {
"select": "Селектор",
"output": "Выход",
"in": "в"
},
"output": {
"html": "html-контент элементов",
"text": "только текстовый контент элементов",
"attr": "объект любых атрибутов элементов"
},
"format": {
"single": "одним сообщением [массив]",
"multi": "по сообщению для каждого элемента"
}
},
"json": {
"errors": {
"dropped-object": "Данные не-объектного типа проигнорированы",
"dropped": "Данные неподдерживаемого типа проигнорированы",
"dropped-error": "Не удалось преобразовать данные",
"schema-error": "Ошибка схемы JSON",
"schema-error-compile": "Ошибка схемы JSON: не удалось скомпилировать схему"
},
"label": {
"o2j": "Опции Объект -> JSON",
"pretty": "Форматировать строку JSON",
"action": "Действие",
"property": "Свойство",
"actions": {
"toggle": "Преобразовывать в любую сторону",
"str":"Всегда преобразовывать в строку JSON",
"obj":"Всегда преобразовывать в объект JavaScript"
}
}
},
"yaml": {
"errors": {
"dropped-object": "Данные не-объектного типа проигнорированы",
"dropped": "Данные неподдерживаемого типа проигнорированы",
"dropped-error": "Не удалось преобразовать данные"
}
},
"xml": {
"label": {
"represent": "Имя свойства для атрибутов XML-тега",
"prefix": "Имя свойства для текстового содержимого тега",
"advanced": "Расширенные опции",
"x2o": "Опции XML -> Объект"
},
"errors": {
"xml_js": "Этот узел обрабатывает только строки XML или объекты JS."
}
},
"file": {
"label": {
"filename": "Имя файла",
"action": "Действие",
"addnewline": "Добавлять новую строку (\\n) к данным?",
"createdir": "Создать каталог, если он не существует?",
"outputas": "Выход",
"breakchunks": "Разбить файл на части",
"breaklines": "Разбить на строки",
"filelabel": "файл",
"sendError": "Отправлять сообщение при ошибке (устаревший режим)",
"encoding": "Кодировка",
"deletelabel": "удалить __file__",
"utf8String": "строка UTF8",
"binaryBuffer": "двоичный буфер"
},
"action": {
"append": "добавить в файл",
"overwrite": "перезаписать файл",
"delete": "удалить файл"
},
"output": {
"utf8": "одна ut8-строка",
"buffer": "один объект буфера",
"lines": "сообщение для каждой строчки",
"stream": "поток буферов"
},
"status": {
"wrotefile": "записано в файл: __file__",
"deletedfile": "удален файл: __file__",
"appendedfile": "добавлено в файл: __file__"
},
"encoding": {
"none": "по умолчанию",
"native": "Нативная",
"unicode": "Юникод",
"japanese": "Японская",
"chinese": "Китайская",
"korean": "Корейская",
"taiwan": "Тайвань/Гонконг",
"windows": "Кодовые страницы Windows",
"iso": "Кодовые страницы ISO",
"ibm": "Кодовые страницы IBM",
"mac": "Кодовые страницы Mac",
"koi8": "Кодовые страницы KOI8",
"misc": "Разные"
},
"errors": {
"nofilename": "Не указано имя файла",
"invaliddelete": "Предупреждение: неверное удаление. Пожалуйста, используйте конкретную опцию удаления в диалоге конфигурации.",
"deletefail": "не удалось удалить файл: __error__",
"writefail": "не удалось записать в файл: __error__",
"appendfail": "не удалось добавить в файл: __error__",
"createfail": "не удалось создать файл: __error__"
},
"tip": "Подсказка: имя файла должно быть абсолютным путем, иначе он будет относительно рабочего каталога процесса Node-RED."
},
"split": {
"split": "разделить",
"intro":"Разделить <code>msg.payload</code> в зависимости от типа:",
"object":"<b>Объект</b>",
"objectSend":"Отправлять сообщение для каждой пары ключ/значение",
"strBuff":"<b>Строка</b> / <b>Буфер</b>",
"array":"<b>Массив</b>",
"splitUsing":"С помощью",
"splitLength":"Фикс. длина",
"stream":"Обрабатывать как поток сообщений",
"addname":" Копировать ключ в "
},
"join": {
"join": "соединить",
"mode": {
"mode": "Режим",
"auto": "автоматический",
"merge": "объединение последовательности",
"reduce": "агрегация последовательности",
"custom": "ручной"
},
"combine": "Объединить каждый",
"completeMessage": "полное сообщение",
"create": "чтобы создать",
"type": {
"string": "строку",
"array": "массив",
"buffer": "буфер",
"object": "объект ключей/значений",
"merged": "объединенный объект"
},
"using": "используя значение",
"key": "как ключ",
"joinedUsing": "соединяя с помощью",
"send": "Отправить сообщение:",
"afterCount": "после ряда частей сообщения",
"count": "кол-во",
"subsequent": "и каждое последующее сообщение.",
"afterTimeout": "по истечении времени с 1го сообщения",
"seconds": "сек",
"complete": "после сообщения с установленным свойством <code>msg.complete</code>",
"tip": "В этом режиме предполагается, что этот узел либо связан с узлом <i>split</i>, либо полученные сообщения будут иметь правильно настроенное свойство <code>msg.parts</code>.",
"too-many": "слишком много ожидающих сообщений в узле join",
"merge": {
"topics-label": "Объединенные темы",
"topics": "темы",
"topic": "тема",
"on-change": "Отправлять объединенное сообщение по прибытии новой темы"
},
"reduce": {
"exp": "Агрегирующее выражение",
"exp-value": "выражение",
"init": "Начальное значение",
"right": "Выполнять в обратном порядке (от последнего к первому)",
"fixup": "Исправляющее выражение"
},
"errors": {
"invalid-expr": "Неверное выражение JSONata: __error__",
"invalid-type": "Невозможно присоединить __error__ к буферу"
}
},
"sort" : {
"sort": "сортировать",
"target" : "Сортировать",
"seq" : "последовательность сообщений",
"key" : "Ключ",
"elem" : "значение элемента",
"order" : "Порядок",
"ascending" : "восходящий",
"descending" : "нисходящий",
"as-number" : "как число",
"invalid-exp" : "Неверное выражение JSONata в узле sort: __message__",
"too-many" : "Слишком много ожидающих сообщений в узле sort",
"clear" : "очистить ожидающее сообщение в узле sort"
},
"batch" : {
"batch": "группировать",
"mode": {
"label" : "Режим",
"num-msgs" : "Группировать по количеству сообщений",
"interval" : "Группировать по интервалу времени",
"concat" : "Объединять последовательности"
},
"count": {
"label" : "Количество сообщений",
"overlap" : "Совпадения",
"count" : "кол-во",
"invalid" : "Неверные количество и совпадения"
},
"interval": {
"label" : "Интервал",
"seconds" : "сек",
"empty" : "отправить пустое сообщение, когда сообщение не приходит"
},
"concat": {
"topics-label": "Темы",
"topic" : "тема"
},
"too-many" : "Слишком много ожидающих сообщений в узле batch",
"unexpected" : "неожиданный режим",
"no-parts" : "в сообщении нет свойства parts"
}
}

View File

@ -0,0 +1,19 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="tls-config">
<p>Параметры конфигурации для соединений TLS.</p>
</script>

View File

@ -0,0 +1,26 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="http proxy">
<p>
Параметры конфигурации для HTTP-прокси.
</p>
<h3>Подробности</h3>
<p>
При доступе к хосту, находящемуся в списке игнорируемых хостов, прокси не будет использоваться.
</p>
</script>

View File

@ -0,0 +1,113 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="mqtt in">
<p>
Подключается к брокеру MQTT и подписывается на сообщения из указанной темы.
</p>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка | буфер</span></dt>
<dd>строка, если не обнаружено как двоичный буфер.</dd>
<dt>topic <span class="property-type">строка</span></dt>
<dd>тема MQTT, использует / в качестве разделителя иерархии.</dd>
<dt>qos <span class="property-type">число</span> </dt>
<dd>0: приходит не более одного раза, 1: приходит не менее одного раза, 2: приходит только один раз.</dd>
<dt>retain <span class="property-type">логич.тип</span></dt>
<dd>значение true указывает, что сообщение было сохранено и может быть устаревшим.</dd>
</dl>
<h3>Подробности</h3>
<p>
Тема подписки может включать подстановочные знаки MQTT, + для одного уровня, # для нескольких уровней.
</p>
<p>
Для этого узла требуется соединение с брокером MQTT. Это настраивается нажатием на значок карандаша.
</p>
<p>
Несколько узлов MQTT (in или out) могут совместно использовать одно и то же соединение с брокером, если это необходимо.
</p>
</script>
<script type="text/html" data-help-name="mqtt out">
<p>
Подключается к брокеру MQTT и публикует сообщения.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка | буфер</span></dt>
<dd>данные для публикации. Если это свойство не установлено, сообщение не будет отправлено. Чтобы отправить пустое сообщение, установите для этого свойства пустую строку.</dd>
<dt class="optional">topic <span class="property-type">строка</span></dt>
<dd>тема MQTT для публикации.</dd>
<dt class="optional">qos <span class="property-type">number</span></dt>
<dd>0: приходит не более одного раза, 1: приходит не менее одного раза, 2: приходит только один раз. По умолчанию 0.</dd>
<dt class="optional">retain <span class="property-type">boolean</span></dt>
<dd>установите значение true, чтобы сохранить сообщение в брокере. По умолчанию false.</dd>
</dl>
<h3>Подробности</h3>
<p>
Свойство <code>msg.payload</code> используется в качестве данных опубликованного сообщения. Если оно содержит объект, то он будет преобразован в строку JSON перед отправкой. Если оно содержит двоичный буфер, сообщение будет опубликовано как есть.
</p>
<p>
Используемая тема может быть настроена в узле или, если оставить ее пустой, может быть установлена с помощью <code>msg.topic</code>.
</p>
<p>
Аналогично, значения QoS и сохранения могут быть сконфигурированы в узле или, если они оставлены пустыми, установлены с помощью <code>msg.qos</code> и <code>msg.retain</code> соответственно. Чтобы удалить ранее сохраненную тему из брокера, отправьте пустое сообщение в эту тему с установленным флагом сохранения.
</p>
<p>
Для этого узла требуется соединение с брокером MQTT. Это настраивается нажатием на значок карандаша.
</p>
<p>
Несколько узлов MQTT (in или out) могут совместно использовать одно и то же соединение с брокером, если это необходимо.
</p>
</script>
<script type="text/html" data-help-name="mqtt-broker">
<p>
Конфигурация для подключения к брокеру MQTT.
</p>
<p>
Эта конфигурация создаст одно соединение с посредником, которое затем может быть повторно использовано узлами <code>MQTT In</code> и <code>MQTT Out</code>.
</p>
<p>
Узел сгенерирует случайный идентификатор клиента, если он не задан, а узел настроен на использование соединения с чистым сеансом. Если идентификатор клиента установлен, он должен быть уникальным для брокера, к которому Вы подключаетесь.
</p>
<h4>Birth сообщение</h4>
<p>
Это сообщение будет опубликовано в настроенной теме при каждом установлении соединения.
</p>
<h4>Close сообщение</h4>
<p>
Это сообщение будет опубликовано в настроенной теме перед тем, как соединение будет закрыто нормально, либо путем повторного развертывания узла, либо остановкой.
</p>
<h4>Will сообщение</h4>
<p>
Это сообщение, которое будет опубликовано брокером в случае, если узел неожиданно потеряет соединение.
</p>
<h4>WebSockets</h4>
<p>
Узел может быть настроен на использование соединения WebSocket. Для этого в поле Сервер должен быть указан полный URI для соединения. Например:
</p>
<pre>ws://example.com:4000/mqtt</pre>
</script>

View File

@ -0,0 +1,107 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="http in">
<p>
Создает конечную точку HTTP для создания веб-сервисов.
</p>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload</dt>
<dd>Для GET-запроса содержит объект с любыми параметрами строки запроса. В противном случае содержит тело HTTP-запроса.</dd>
<dt>req<span class="property-type">объект</span></dt>
<dd>Объект HTTP-запроса. Этот объект содержит несколько свойств, которые предоставляют информацию о запросе.
<ul>
<li><code>body</code> - тело входящего запроса. Формат будет зависеть от запроса.</li>
<li><code>headers</code> - объект, содержащий заголовки HTTP-запроса.</li>
<li><code>query</code> - объект, содержащий любые параметры строки запроса.</li>
<li><code>params</code> - объект, содержащий любые параметры маршрута.</li>
<li><code>cookies</code> - объект, содержащий куки запроса.</li>
<li><code>files</code> - если включено в узле, объект, содержащий любые файлы, загруженные как часть POST-запроса.</li>
</ul>
</dd>
<dt>res<span class="property-type">объект</span></dt>
<dd>Объект ответа HTTP. Это свойство не должно использоваться напрямую; узел ответа <code>HTTP Response</code> описывает, как ответить на запрос. Это свойство должно оставаться в сообщении, передаваемому узлу ответа.</dd>
</dl>
<h3>Подробности</h3>
<p>
По настроенному пути узел будет слушать запросы определенного типа. Путь может быть указан полностью, например <code>/user</code>, или включать именованные параметры, которые принимают любое значение, например <code>/user/:name</code>. Когда используются именованные параметры, их фактическое значение в запросе будет доступно в <code>msg.req.params</code>.
</p>
<p>
Для запросов, которые содержат тело, такие как POST или PUT, содержимое запроса доступно в <code>msg.payload</code>.
</p>
<p>
Если тип содержимого запроса может быть определен, тело будет преобразовано в любой подходящий тип. Например, <code>application/json</code> будет преобразован в объект JavaScript.
</p>
<p>
<b>Примечание:</b> этот узел не отправляет никакого ответа на запрос. Поток должен включать узел ответа HTTP Response для завершения запроса.
</p>
</script>
<script type="text/html" data-help-name="http response">
<p>
Отправляет ответ на запрос, полученный от узла HTTP Input.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка</span></dt>
<dd>Тело ответа.</dd>
<dt class="optional">statusCode <span class="property-type">число</span></dt>
<dd>Если установлено, то используется как код состояния ответа. По умолчанию: 200.</dd>
<dt class="optional">headers <span class="property-type">объект</span></dt>
<dd>Если установлено, предоставляет заголовки HTTP для включения в ответ.</dd>
<dt class="optional">cookies <span class="property-type">объект</span></dt>
<dd>Если установлено, может использоваться для установки или удаления куков.</dd>
</dl>
<h3>Подробности</h3>
<p>
Свойства <code>statusCode</code> и <code>headers</code> также могут быть установлены внутри самого узла, настройками 'код состояния' и 'заголовки' соответственно. Если свойство установлено в узле, оно не может быть переопределено соответствующим свойством сообщения.
</p>
<h4>Обработка куков</h4>
<p>
Свойство <code>cookies</code> должно быть объектом пар имя/значение. Значением может быть либо строка для установки значения куки с параметрами по умолчанию, либо это может быть объект параметров.
</p>
<p>
В следующем примере устанавливаются два куки - один с именем <code>name</code> со значением <code>nick</code>, другой с именем <code>session</code> со значением <code>1234</code> и сроком действия в 15 минут.
</p>
<pre>
msg.cookies = {
name: 'nick',
session: {
value: '1234',
maxAge: 900000
}
}</pre>
<p>
Допустимые параметры включают в себя:
</p>
<ul>
<li><code>domain</code> - (Строка) доменное имя для куки</li>
<li><code>expires</code> - (Дата) срок годности по Гринвичу. Если не указан или установлен в 0, создает сессионный куки</li>
<li><code>maxAge</code> - (Строка) дата истечения срока действия относительно текущего времени в миллисекундах</li>
<li><code>path</code> - (Строка) путь для куки. По умолчанию /</li>
<li><code>value</code> - (Строка) значение, используемое для куки</li>
</ul>
<p>
Чтобы удалить куки, установите для его <code>value</code> значение <code>null</code>.
</p>
</script>

View File

@ -0,0 +1,106 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="http request">
<p>
Отправляет HTTP-запрос и возвращает ответ.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">url <span class="property-type">строка</span></dt>
<dd>Если 'URL' не установлен в узле, это необязательное свойство устанавливает URL запроса.</dd>
<dt class="optional">method <span class="property-type">строка</span></dt>
<dd>Если 'метод' не установлен в узле, это необязательное свойство устанавливает HTTP-метод запроса. Должно быть <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> или <code>DELETE</code>.</dd>
<dt class="optional">headers <span class="property-type">объект</span></dt>
<dd>Устанавливает заголовки HTTP запроса.</dd>
<dt class="optional">cookies <span class="property-type">объект</span></dt>
<dd>Если установлено, может использоваться для отправки куки с запросом.</dd>
<dt class="optional">payload</dt>
<dd>Отправляется как тело запроса.</dd>
<dt class="optional">rejectUnauthorized</dt>
<dd>Если установлено значение <code>false</code>, разрешает отправлять запросы на сайты https, которые используют самозаверяющие сертификаты.</dd>
<dt class="optional">followRedirects</dt>
<dd>Если установлено значение <code>false</code>, запрещает переадресацию (HTTP 301). По умолчанию <code>true</code>.</dd>
<dt class="optional">requestTimeout</dt>
<dd>Если задано положительное число миллисекунд, будет переопределен глобально заданный параметр <code>httpRequestTimeout</code>.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка | объект | буфер</span></dt>
<dd>Тело ответа. Узел можно настроить так, чтобы он возвращал тело в виде строки, пытался проанализировать его как строку JSON или оставлял его в виде двоичного буфера.</dd>
<dt>statusCode <span class="property-type">число</span></dt>
<dd>Код статуса ответа или код ошибки, если запрос не может быть выполнен.</dd>
<dt>headers <span class="property-type">объект</span></dt>
<dd>Объект, содержащий заголовки ответа.</dd>
<dt>responseUrl <span class="property-type">строка</span></dt>
<dd>Если при обработке запроса произошли перенаправления, это свойство является окончательным перенаправленным URL-адресом. В противном случае, URL исходного запроса.</dd>
<dt>responseCookies <span class="property-type">объект</span></dt>
<dd>Если ответ содержит куки, это свойство является объектом пар имя/значение для каждого из них.</dd>
<dt>redirectList <span class="property-type">массив</span></dt>
<dd>Если запрос был перенаправлен один или несколько раз, накопленная информация будет добавлена в это свойство. `location` - это следующий пункт назначения перенаправления. `cookies` - это куки, возвращаемые из источника перенаправления.</dd>
</dl>
<h3>Подробности</h3>
<p>
При настройке внутри узла свойство URL может содержать <a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a>-теги. Это позволяет построить URL-адрес, используя значения входящего сообщения. Например, если для URL-адреса задано значение <code>example.com/{{{topic}}}</code>, то автоматически будет вставлено значение <code>msg.topic</code>. Использование {{{...}}} предотвращает HTML-кодирование таких символов, как / & и т.д.
</p>
<p>
Узел может также автоматически кодировать <code>msg.payload</code> как параметры для GET-запроса. В этом случае <code>msg.payload</code> должен быть объектом.
</p>
<p>
<b>Примечание</b>. При работе через прокси-сервер следует либо установить стандартную переменную среды <code>http_proxy=...</code> и перезапустить Node-RED, либо использовать конфигурацию прокси-сервера. Если была установлена конфигурация прокси, то она имеет приоритет над переменной среды.
</p>
<h4>Использование нескольких узлов HTTP-запросов</h4>
<p>
Чтобы использовать более одного из этих узлов в одном потоке, нужно позаботиться о свойстве <code>msg.headers</code>. Первый узел установит это свойство с заголовками ответа. Затем следующий узел будет использовать эти заголовки для своего запроса - обычно это неправильный подход. Если свойство <code>msg.headers</code> остается неизменным между узлами, оно будет проигнорировано вторым узлом. Чтобы установить пользовательские заголовки, сначала необходимо удалить <code>msg.headers</code> или сбросить до пустого объекта: <code>{}</code>.
</p>
<h4>Обработка куки</h4>
<p>
Свойство <code>cookies</code>, передаваемое узлу, должно быть объектом пар имя/значение. Значением может быть либо строка для установки значения куки, либо это может быть объект с единственным свойством <code>value</code>.
</p>
<p>
Все куки, возвращаемые запросом, передаются обратно в свойство <code>responseCookies</code>.
</p>
<h4>Обработка типов контента</h4>
<p>
Если <code>msg.payload</code> является объектом, узел автоматически установит тип содержимого запроса в <code>application/json</code> и закодирует тело как таковое.
</p>
<p>
Чтобы закодировать запрос как данные формы, для <code>msg.headers["content-type"]</code> должно быть установлено <code>application/x-www-form-urlencoded</code>.
</p>
<h4>Загрузка файла</h4>
<p>
Чтобы выполнить загрузку файла, для <code>msg.headers["content-type"]</code> должно быть установлено значение <code>multipart/form-data</code>, а <code>msg.payload</code> переданный на узел должен быть объектом со следующей структурой:
</p>
<pre><code>{
"КЛЮЧ": {
"value": СОДЕРЖИМОЕАЙЛА,
"options": {
"filename": "ИМЯ_ФАЙЛА"
}
}
}</code></pre>
<p>
На местах <code>КЛЮЧ</code>, <code>СОДЕРЖИМОЕАЙЛА</code> и <code>ИМЯ_ФАЙЛА</code> должны быть установлены соответствующие значения.
</p>
</script>

View File

@ -0,0 +1,52 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="websocket in">
<p>
Входной узел WebSocket.
</p>
<p>
По умолчанию данные, полученные из WebSocket, будут в <code>msg.payload</code>. Сокет можно настроить так, чтобы он ожидал правильно сформированную строку JSON. В этом случае он будет анализировать JSON и отправлять полученный объект как полное сообщение.
</p>
</script>
<script type="text/html" data-help-name="websocket out">
<p>
Выходной узел WebSocket.
</p>
<p>
По умолчанию <code>msg.payload</code> будет отправлено через WebSocket. Сокет может быть сконфигурирован для кодирования всего объекта <code>msg</code> в виде строки JSON и отправки его через WebSocket.
</p>
<p>
Если сообщение, поступающее на этот узел, началось с узла WebSocket In, сообщение будет отправлено обратно клиенту, который запустил поток. В противном случае сообщение будет передано всем подключенным клиентам.
</p>
<p>
Если Вы хотите передать сообщение, которое началось на узле WebSocket In, всем подключенным клиентам, тогда Вы должны удалить свойство <code>msg._session</code> в потоке.
</p>
</script>
<script type="text/html" data-help-name="websocket-listener">
<p>
Этот узел конфигурации создает конечную точку WebSocket-сервера, используя указанный путь.
</p>
</script>
<script type="text/html" data-help-name="websocket-client">
<p>
Этот узел конфигурации соединяет WebSocket-клиент с указанным URL-адресом.
</p>
</script>

View File

@ -0,0 +1,57 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="tcp in">
<p>
Предоставляет выбор TCP-входов. Можно либо подключаться к удаленному TCP-порту, либо принимать входящие подключения.
</p>
<p>
<b>Примечание.</b> В некоторых системах Вам могут потребоваться права root или администратора для доступа к портам ниже 1024.
</p>
</script>
<script type="text/html" data-help-name="tcp out">
<p>
Предоставляет выбор TCP-выходов. Может подключаться к удаленному TCP-порту, принимать входящие подключения или отвечать на сообщения, полученные от узла TCP In.
</p>
<p>
Отправляются только данные <code>msg.payload</code>.
</p>
<p>
Если <code>msg.payload</code> является строкой, содержащей Base64-кодировку двоичных данных, опция Base64-декодирования приведет к ее преобразованию обратно в двоичный файл перед отправкой.
</p>
<p>
Если <code>msg._session</code> отсутствует, данные отправляется <b>всем</b> подключенным клиентам.
</p>
<p>
<b>Примечание.</b> В некоторых системах Вам могут потребоваться права root или администратора для доступа к портам ниже 1024.
</p>
</script>
<script type="text/html" data-help-name="tcp request">
<p>
Простой узел TCP-запроса - отправляет <code>msg.payload</code> на tcp-порт сервера и ожидает ответа.
</p>
<p>
Подключается, отправляет &quot;запрос&quot; и читает &quot;ответ&quot;. Он может либо подсчитать количество возвращенных символов в фиксированный буфер, сопоставить указанный символ перед возвратом, дождаться фиксированного времени ожидания от первого ответа и затем вернуться, сидеть и дожидаться данных, или отправить, а затем немедленно закрыть соединение, не дожидаясь ответа.
</p>
<p>
Ответ будет выведен в <code>msg.payload</code> в виде буфера, поэтому Вы можете захотеть применить к нему .toString().
</p>
<p>
Если Вы оставите tcp-хост или порт пустыми, они должны быть установлены с помощью свойств <code>msg.host</code> и <code>msg.port</code> в каждом сообщении, отправляемом узлу.
</p>
</script>

View File

@ -0,0 +1,42 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="udp in">
<p>
Входной узел UDP, который создает <code>msg.payload</code>, содержащий буфер, строку или base64-строку. Поддерживает многоадресную рассылку.
</p>
<p>
Он также предоставляет <code>msg.ip</code> и <code>msg.port</code> для IP-адреса и порта, с которого было получено сообщение.
</p>
<p>
<b>Примечание</b>. В некоторых системах Вам могут потребоваться права root или администратора для доступа к портам ниже 1024 и/или широковещательной рассылки.
</p>
</script>
<script type="text/html" data-help-name="udp out">
<p>
Этот узел отправляет <code>msg.payload</code> на назначенный UDP-хост и порт. Поддерживает многоадресную рассылку.
</p>
<p>
Вы также можете использовать <code>msg.ip</code> и <code>msg.port</code> для установки значений пункта назначения, но статически настроенные значения имеют приоритет.
</p>
<p>
Если Вы выберете широковещательную рассылку, то либо задайте в качестве адреса локальный широковещательный IP-адрес, либо попробуйте 255.255.255.255, который является глобальным широковещательным адресом.
</p>
<p>
<b>Примечание</b>. В некоторых системах Вам могут потребоваться права root или администратора для доступа к портам ниже 1024 и/или широковещательной рассылки.
</p>
</script>

View File

@ -0,0 +1,72 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="csv">
<p>
Выполняет преобразования между строкой в CSV формате и ее представлением в JavaScript-объекте, в любом направлении.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | массив | строка</span></dt>
<dd>JavaScript объект, массив или CSV-строка.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | массив | строка</span></dt>
<dd>
<ul>
<li>Если вход является значением строкового типа, узел пытается проанализировать ее как CSV и создает объект JavaScript из пар ключ/значение для каждой строки. Затем узел либо отправит сообщение для каждой строки или одно сообщение, содержащее массив объектов.</li>
<li>Если вход является JavaScript объектом, узел пытается построить CSV-строку.</li>
<li>Если вход является массивом простых значений, узел построит однострочную CSV-строку.</li>
<li>Если вход является массивом массивов или массивом объектов, создается многострочная CSV-строка.</li>
</ul>
</dd>
</dl>
<h3>Подробности</h3>
<p>
Шаблон столбцов может содержать упорядоченный список имен столбцов. При преобразовании CSV в объект имена столбцов будут использоваться в качестве имен свойств. Кроме того, имена столбцов могут быть взяты из первой строки CSV.
</p>
<p>
При преобразовании в CSV шаблон столбцов используется для определения того, какие свойства извлекать из объекта и в каком порядке.
</p>
<p>
Если шаблон пуст, то узел может использовать простой список свойств, разделенных запятыми, предоставленных в <code>msg.columns</code>, чтобы определить, что извлечь. Если этого нет, то все свойства объекта выводятся в том порядке, в котором они были найдены в первой строке.
</p>
<p>
Если входные данные являются массивом, то шаблон столбцов используется только для необязательного генерирования строки с заголовками столбцов.
</p>
<p>
Если выбрана опция 'разбирать числовые значения', строковые числовые значения будут возвращаться в виде чисел. К примеру, среднее значение в CSV-строке '1,"1,5",2'.
</p>
<p>
Если выбрана опция 'включать пустые строковые значения', пустые строки будут возвращениы в результате. К примеру, среднее значение в CSV-строке '"1","",3'.
</p>
<p>
Если выбрана опция 'включить null-значения', null-значения будут возвращениы в результате. К примеру, среднее значение в CSV-строке '"1",,3'.
</p>
<p>
Узел может принимать входные данные, состоящие из нескольких частей, при условии, что свойство <code>parts</code> установлено правильно, например, из узла file-in или узла split.
</p>
<p>
При выводе нескольких сообщений они будут иметь свойство <code>parts</code> и формировать полную последовательность сообщений.
</p>
<p>
<b>Примечание.</b> В шаблоне столбцов должна использоваться запятая для разделения - даже если для данных выбран другой разделитель.
</p>
</script>

View File

@ -0,0 +1,40 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="html">
<p>
Извлекает элементы из HTML-документа, хранящегося в <code>msg.payload</code>, с помощью CSS-селектора.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка</span></dt>
<dd>HTML-строка, из которой извлекать элементы.</dd>
<dt class="optional">select <span class="property-type">строка</span></dt>
<dd>если селектор не настроен на панели редактирования узла, то он может быть установлен как свойство msg.</dd>
</dl>
<h3>Выход</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">массив | строка</span></dt>
<dd>Результатом может быть либо одно сообщение, в котором payload содержит массив найденных элементов, либо несколько сообщений, каждое из которых содержит найденный элемент. Если отправлено несколько сообщений, у них также будет установлен <code>parts</code>.</dd>
</dl>
<h3>Подробности</h3>
<p>
Этот узел поддерживает комбинацию селекторов CSS и jQuery. См. <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_blank">документацию css-select</a> для получения дополнительной информации о поддерживаемом синтаксисе.
</p>
</script>

View File

@ -0,0 +1,56 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="json">
<p>
Выполняет преобразования между строкой в JSON формате и ее представлением в JavaScript-объекте, в любом направлении.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>JavaScript объект или JSON-строка.</dd>
<dt>schema<span class="property-type">объект</span></dt>
<dd>Необязательный объект JSON-схемы для проверки данных. Свойство будет удалено перед отправкой <code>msg</code> следующему узлу.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>
<ul>
<li>Если вход является JSON-строкой, узел пытается проанализировать ее как JavaScript объект.</li>
<li>Если вход является JavaScript объектом, узел создает JSON-строку. Строка может быть при желании отформатирована.</li>
</ul>
</dd>
<dt>schemaError<span class="property-type">массив</span></dt>
<dd>Если проверка JSON-схемы завершится неудачно, узел catch будет иметь свойство <code>schemaError</code>, содержащее массив ошибок.</dd>
</dl>
<h3>Подробности</h3>
<p>
По умолчанию узел работает с <code>msg.payload</code>, но его можно настроить для преобразования любого свойства сообщения.
</p>
<p>
Узел также может быть сконфигурирован для обеспечения конкретной кодировки вместо переключения между ними. Это можно использовать, например, с узлом <code>HTTP In</code>, чтобы гарантировать, что данные payload являются объектом, даже если входящий запрос неправильно установил свой тип содержимого для узла HTTP In, чтобы выполнить преобразование.
</p>
<p>
Если узел настроен на то, чтобы свойство кодировалось как строка, и он получает строку, дальнейшие проверки этого свойства выполняться не будут. Он не будет проверять, является ли строка допустимым JSON, и не будет переформатировать ее, если выбрана опция форматирования.
</p>
<p>
Подробнее о JSON-схеме Вы можете узнать в спецификации <a href="http://json-schema.org/latest/json-schema-validation.html">здесь</a>.
</p>
</script>

View File

@ -0,0 +1,60 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="xml">
<p>
Выполняет преобразования между строкой в XML формате и ее представлением в JavaScript-объекте, в любом направлении.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>JavaScript объект или XML-строка.</dd>
<dt class="optional">options <span class="property-type">объект</span></dt>
<dd>Это необязательное свойство может использоваться для передачи любых параметров, поддерживаемых библиотекой, которая используется для преобразования в/из XML. См. <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">документацию xml2js</a> для получения дополнительной информации.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>
<ul>
<li>Если вход является значением строкового типа, узел пытается проанализировать ее как XML и создает объект JavaScript.</li>
<li>Если вход является JavaScript объектом, узел пытается построить XML-строку.</li>
</ul>
</dd>
</dl>
<h3>Подробности</h3>
<p>
При преобразовании между XML и объектом любые XML-атрибуты по умолчанию добавляются как свойство с именем <code>$</code>. Любое текстовое содержимое добавляется как свойство с именем <code>_</code>. Эти имена свойств могут быть указаны в конфигурации узла.
</p>
<p>
Например, следующий XML:
</p>
<pre>&lt;p class="tag"&gt;Hello World&lt;/p&gt;</pre>
<p>
будет преобразован в:
</p>
<pre>{
"p": {
"$": {
"class": "tag"
},
"_": "Hello World"
}
}</pre>
</script>

View File

@ -0,0 +1,38 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="yaml">
<p>
Выполняет преобразования между строкой в YAML формате и ее представлением в JavaScript-объекте, в любом направлении.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>JavaScript объект или YAML-строка.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка</span></dt>
<dd>
<ul>
<li>Если вход является YAML-строкой, узел пытается проанализировать ее как JavaScript объект.</li>
<li>Если вход является JavaScript объектом, узел создает YAML-строку.</li>
</ul>
</dd>
</dl>
</script>

View File

@ -0,0 +1,175 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="split">
<p>
Разбивает сообщение на последовательность сообщений.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">объект | строка | массив | буфер</span></dt>
<dd>Поведение узла определяется типом <code>msg.payload</code>:
<ul>
<li><b>строка</b>/<b>буфер</b> - сообщение разделяется с помощью указанного символа (по умолчанию: <code>\n</code>), последовательности буфера или фиксированной длины.</li>
<li><b>массив</b> - сообщение разбивается на отдельные элементы массива или массивы фиксированной длины.</li>
<li><b>объект</b> - сообщение отправляется для каждой пары ключ/значение объекта.</li>
</ul>
</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>parts<span class="property-type">объект</span></dt>
<dd>Это свойство содержит информацию о том, как сообщение было отделено от исходного сообщения. При передаче на узел <b>join</b> последовательность может быть собрана в одно сообщение. Свойство имеет следующие свойства:
<ul>
<li><code>id</code> - идентификатор группы сообщений</li>
<li><code>index</code> - позиция в группе</li>
<li><code>count</code> - если известно, общее количество сообщений в группе. Смотрите 'потоковый режим' ниже.</li>
<li><code>type</code> - тип сообщения: строка = string; массив = array; объект = object; буфер = buffer</li>
<li><code>ch</code> - для строки или буфера данные, использованные для разделения сообщения, в виде строки или массива байтов</li>
<li><code>key</code> - для объекта - ключ свойства, из которого было создано это сообщение. Узел также может быть сконфигурирован для копирования этого значения в другие свойства сообщения, такие как <code>msg.topic</code>.</li>
<li><code>len</code> - длина каждого сообщения при разделении с использованием значения фиксированной длины</li>
</ul>
</dd>
</dl>
<h3>Подробности</h3>
<p>
Этот узел облегчает создание потока, который выполняет общие действия над последовательностью сообщений, перед тем как с помощью узла <b>join</b> объединить последовательность в одно сообщение.
</p>
<p>
Он использует свойство <code>msg.parts</code> для отслеживания отдельных частей последовательности.
</p>
<h4>Потоковый режим</h4>
<p>
Узел также может использоваться для переформатирования потока сообщений. Например, последовательное устройство, которое отправляет завершенные новой строкой команды, может доставлять одно сообщение с частичной командой в конце. В 'потоковом режиме' этот узел будет разбивать сообщение и отправлять каждый завершенный сегмент. Если в конце есть частичный сегмент, узел удержит его и добавит к следующему полученному сообщению.
</p>
<p>
При работе в этом режиме узел не будет устанавливать свойство <code>msg.parts.count</code>, так как он не знает, сколько сообщений ожидать в потоке. Это означает, что его нельзя использовать с узлом <b>join</b> в его автоматическом режиме.
</p>
</script>
<script type="text/html" data-help-name="join">
<p>
Объединяет последовательности сообщений в одно сообщение.
</p>
<p>
Доступны три режима:
</p>
<dl>
<dt>автоматический</dt>
<dd>При использовании с узлом <b>split</b> он автоматически объединит сообщения, чтобы отменить выполненное разделение.</dd>
<dt>ручной</dt>
<dd>Объединяет последовательности сообщений различными способами.</dd>
<dt>агрегация последовательности</dt>
<dd>Применяет JSONata-выражение ко всем сообщениям в последовательности, чтобы свести его к одному сообщению.</dd>
</dl>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">объект</span></dt>
<dd>Чтобы автоматически соединить последовательность сообщений, у них всех должно быть установлено это свойство. Узел <b>split</b> генерирует это свойство, но его можно создать вручную. Оно имеет следующие свойства:
<ul>
<li><code>id</code> - идентификатор группы сообщений</li>
<li><code>index</code> - позиция в группе</li>
<li><code>count</code> - общее количество сообщений в группе</li>
<li><code>type</code> - тип сообщения: строка = string; массив = array; объект = object; буфер = buffer</li>
<li><code>ch</code> - для строки или буфера данные, использованные для разделения сообщения, в виде строки или массива байтов</li>
<li><code>key</code> - для объекта - ключ свойства, из которого было создано это сообщение</li>
<li><code>len</code> - длина каждого сообщения при разделении с использованием значения фиксированной длины</li>
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>
Если установлено, узел добавит payload, а затем отправит выходное сообщение в своем текущем состоянии. Если вы не хотите добавлять payload, удалите его из msg.
</dd>
</dl>
<h3>Подробности</h3>
<h4>Автоматический режим</h4>
<p>
В автоматическом режиме используется свойство <code>parts</code> входящих сообщений, чтобы определить способ объединения последовательности. Это позволяет автоматически отменять действие узла <b>split</b>.
</p>
<h4>Ручной режим</h4>
<p>
Когда узел настроен на соединение в ручном режиме, он может объединять последовательности сообщений в различные результаты:
</p>
<ul>
<li><b>строка</b> или <b>буфер</b> - создается путем объединения выбранного свойства каждого сообщения с указанными символами соединения или буфером.</li>
<li><b>массив</b> - создается путем добавления каждого выбранного свойства или всего сообщения в выходной массив.</li>
<li><b>объект ключей/значений</b> - создается с использованием свойства каждого сообщения для определения ключа, под которым хранится требуемое значение.</li>
<li><b>слитый объект</b> - создается путем объединения свойства каждого сообщения в один объект.</li>
</ul>
<p>
Другие свойства исходящего сообщения берутся из последнего сообщения, полученного перед отправкой результата.
</p>
<p>
Свойство <i>кол-во</i> может быть установлено для количества сообщений, которое должно быть получено перед генерацией выходного сообщения. Для выходных данных объекта, когда это число достигнуто, узел может быть настроен на отправку сообщения для каждого последующего полученного сообщения.
</p>
<p>
Свойство <i>время (сек)</i> может быть установлено, чтобы инициировать отправку нового сообщения с использованием того, что было получено до сих пор.
</p>
<p>
Если сообщение получено с установленным свойством <b>msg.complete</b>, выходное сообщение завершается и отправляется. Это сбрасывает любой подсчет частей.
</p>
<p>
Если сообщение получено с установленным свойством <b>msg.reset</b>, частично завершенное сообщение удаляется и не отправляется. Это сбрасывает любой подсчет частей.
</p>
<h4>Режим агрегации последовательности</h4>
<p>
Когда настроено объединение в режиме агрегирования, выражение применяется к каждому сообщению в последовательности, и результат накапливается для создания одного сообщения.
</p>
<dl class="message-properties">
<dt>Начальное значение</dt>
<dd>Начальное значение накопленного значения (<code>$A</code>).</dd>
<dt>Агрегирующее выражение</dt>
<dd>Выражение JSONata, которое вызывается для каждого сообщения в последовательности. Результат передается следующему вызову выражения как накопленное значение. В выражении могут использоваться следующие специальные переменные:
<ul>
<li><code>$A</code>: накопленное значение, </li>
<li><code>$I</code>: индекс сообщения в последовательности, </li>
<li><code>$N</code>: количество сообщений в последовательности.</li>
</ul>
</dd>
<dt>Исправляющее выражение</dt>
<dd>Необязательное выражение JSONata, которое применяется после того, как агрегирующее выражение было применено ко всем сообщениям в последовательности. В выражении могут использоваться следующие специальные переменные:
<ul>
<li><code>$A</code>: накопленное значение, </li>
<li><code>$N</code>: количество сообщений в последовательности.</li>
</ul>
</dd>
<p>
По умолчанию агрегирующее выражение применяется по порядку, от первого до последнего сообщения последовательности. При желании его можно применять в обратном порядке.
</p>
</dl>
<p>
<b>Пример:</b> следующие параметры, при получении последовательности числовых значений, рассчитывают среднее значение:
<ul>
<li><b>Агрегирующее выражение</b>: <code>$A+payload</code></li>
<li><b>Начальное значение</b>: <code>0</code></li>
<li><b>Исправляющее выражение</b>: <code>$A/$N</code></li>
</ul>
</p>
<h4>Хранение сообщений</h4>
<p>
Этот узел будет буферизировать сообщения внутри, чтобы работать между последовательностями. Параметр <code>nodeMessageBufferMaxLength</code> можно использовать для ограничения количества сообщений, которые узел будут буферизовать.
</p>
</script>

View File

@ -0,0 +1,56 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="sort">
<p>
Функция, которая сортирует свойство сообщения или последовательность сообщений.
</p>
<p>
Когда узел настроен сортировать свойство сообщения, он сортирует данные массива в указанном свойстве сообщения.
</p>
<p>
Когда узел настроен сортировать последовательность сообщений, он будет переупорядочивать сообщения.
</p>
<p>
Порядок сортировки может быть:
</p>
<ul>
<li><b>восходящий</b>,</li>
<li><b>нисходящий</b>.</li>
</ul>
<p>
Для чисел, числовое упорядочивание можно указать с помощью флажка.
</p>
<p>
Когда выбрана сортировка свойства, ключом сортировки может быть значение элемента или выражение JSONata. Когда выбрана сортировка последовательности сообщений, ключом сортировка может быть свойство сообщения или выражение JSONata.
</p>
<p>
При сортировке последовательности сообщений узел сортировки полагается на полученные сообщения, чтобы установить <code>msg.parts</code>. Узел split генерирует это свойство, но его можно создать вручную. Оно имеет следующие свойства:
</p>
<p>
<ul>
<li><code>id</code> - идентификатор группы сообщений</li>
<li><code>index</code> - позиция в группе</li>
<li><code>count</code> - общее количество сообщений в группе</li>
</ul>
</p>
<p>
<b>Примечание.</b> Этот узел внутренне хранит сообщения для своей работы. Чтобы предотвратить непредвиденное использование памяти, можно указать максимальное количество хранимых сообщений. По умолчанию количество сообщений не ограничено.
<ul>
<li>Свойство <code>nodeMessageBufferMaxLength</code> устанавливается в <b>settings.js</b>.</li>
</ul>
</p>
</script>

View File

@ -0,0 +1,45 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="batch">
<p>
Создает последовательности сообщений на основе различных правил.
</p>
<h3>Подробности</h3>
<p>
Существует три режима создания последовательностей сообщений:
</p>
<dl>
<dt>Количество сообщений</dt>
<dd>группирует сообщения в последовательности заданной длины. Параметр <b>совпадения</b> указывает, сколько сообщений в конце одной последовательности следует повторить в начале следующей последовательности.</dd>
<dt>Интервал времени</dt>
<dd>группирует сообщения, поступающие в указанный интервал. Если в течение интервала сообщения не поступают, узел может при желании отправлять пустое сообщение.</dd>
<dt>Объединение последовательностей</dt>
<dd>создает последовательность сообщений путем объединения входящих последовательностей. Каждое сообщение должно иметь свойство <code>msg.topic</code> и свойство <code>msg.parts</code>, определяющие его последовательность. Узел настраивается со списком значений <code>topic</code> для идентификации порядка объединения последовательностей.
</dd>
</dl>
<h4>Хранение сообщений</h4>
<p>
Этот узел будет буферизировать сообщения внутри, чтобы работать между последовательностями. Параметр <code>nodeMessageBufferMaxLength</code> можно использовать для ограничения количества сообщений, которые узел будут буферизовать.
</p>
<p>
Если сообщение получено с установленным свойством <b>msg.reset</b>, буферизованные сообщения удаляются и не отправляются.
</p>
</script>

View File

@ -0,0 +1,86 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="file">
<p>
Записывает <code>msg.payload</code> в файл, либо добавляя в конец, либо заменяя существующий контент. Кроме того, он может удалить файл.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">строка</span></dt>
<dd>Если не настроено в узле, это необязательное свойство устанавливает имя файла, который будет обновлен.</dd>
</dl>
<h3>Выход</h3>
<p>
По завершении записи входное сообщение отправляется на выходной порт.
</p>
<h3>Подробности</h3>
<p>
Данные каждого сообщения будут добавлены в конец файла, при желании добавляя символ новой строки (\n) между каждым.
</p>
<p>
Если используется <code>msg.filename</code>, файл будет закрыт после каждой записи. Для лучшей производительности используйте фиксированное имя файла.
</p>
<p>
Он может быть настроен на перезапись всего файла, а не на добавление. Например, при записи двоичных данных в файл, например изображение, следует использовать эту опцию и отключить опцию добавления новой строки.
</p>
<p>
Кодировка данных, записанных в файл, может быть выбрана в списке кодировок.
</p>
<p>
Кроме того, этот узел может быть настроен на удаление файла.
</p>
</script>
<script type="text/html" data-help-name="file in">
<p>
Читает содержимое файла как строковый тип или двоичный буфер.
</p>
<h3>Принимает</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">строка</span></dt>
<dd>если не задано в конфигурации узла, это свойство устанавливает имя файла для чтения.</dd>
</dl>
<h3>Выводит</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">строка | буфер</span></dt>
<dd>Содержимое файла в виде строки или двоичного буфера.</dd>
<dt class="optional">filename <span class="property-type">строка</span></dt>
<dd>Если не настроено в узле, это необязательное свойство устанавливает имя файла для чтения.</dd>
</dl>
<h3>Подробности</h3>
<p>
Имя файла должно быть абсолютным путем, иначе оно будет относительно рабочего каталога процесса Node-RED.
</p>
<p>
В Windows может быть необходимо кодировать разделители пути двойной косой чертой, например: <code>\\Users\\myUser</code>.
</p>
<p>
При желании текстовый файл можно разбить на строки, выводя по одному сообщению на строку, или двоичный файл разбить на более мелкие фрагменты буфера - размер блока зависит от операционной системы, но обычно составляет 64 КБ (Linux/Mac) или 41 КБ (Windows).
</p>
<p>
При разбиении на несколько сообщений каждое сообщение будет иметь свойство <code>parts</code>, формирующее полную последовательность сообщений.
</p>
<p>
Кодировка входных данных может быть выбрана из списка кодировок, если выходной формат - строка.
</p>
</script>

View File

@ -0,0 +1,39 @@
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/html" data-help-name="watch">
<p>
Наблюдает за изменениями каталога или файла.
</p>
<p>
Вы можете ввести список разделенных запятыми каталогов и/или файлов. Вам нужно взять в кавычки "..." те из них, в которых есть пробелы.
</p>
<p>
В Windows Вы должны использовать двойную обратную косую черту \\ в любых именах каталогов.
</p>
<p>
Полное имя фактически измененного файла помещается в <code>msg.payload</code> и <code>msg.filename</code>, а строковая версия списка наблюдения возвращается в <code>msg.topic</code>.
</p>
<p>
<code>msg.file</code> содержит только краткое имя файла, который изменился. <code>msg.type</code> содержит тип измененной единицы, обычно это <i>file</i> для файла или <i>directory</i> для директории, тогда как <code>msg.size</code> содержит размер файла в байтах.
</p>
<p>
В Linux <i>все</i> является файлом, и может быть под наблюдением...
</p>
<p>
<b>Примечание.</b> Наблюдаемые каталог или файл должны существовать. Если файл или каталог будет удален, они могут перестать отслеживаться, даже если они будут созданы заново.
</p>
</script>

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/nodes", "name": "@node-red/nodes",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",
@ -31,7 +31,7 @@
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "3.14.0", "js-yaml": "3.14.0",
"media-typer": "1.1.0", "media-typer": "1.1.0",
"mqtt": "4.2.1", "mqtt": "4.2.5",
"multer": "1.4.2", "multer": "1.4.2",
"mustache": "4.0.1", "mustache": "4.0.1",
"on-headers": "1.0.2", "on-headers": "1.0.2",

View File

@ -103,7 +103,7 @@ function installModule(module,version,url) {
installName = url; installName = url;
} else { } else {
log.warn(log._("server.install.install-failed-url",{name:module,url:url})); log.warn(log._("server.install.install-failed-url",{name:module,url:url}));
e = new Error("Invalid url"); const e = new Error("Invalid url");
e.code = "invalid_module_url"; e.code = "invalid_module_url";
reject(e); reject(e);
return; return;
@ -120,7 +120,7 @@ function installModule(module,version,url) {
module = info.name; module = info.name;
} else { } else {
log.warn(log._("server.install.install-failed-name",{name:module})); log.warn(log._("server.install.install-failed-name",{name:module}));
e = new Error("Invalid module name"); const e = new Error("Invalid module name");
e.code = "invalid_module_name"; e.code = "invalid_module_name";
reject(e); reject(e);
return; return;
@ -136,7 +136,7 @@ function installModule(module,version,url) {
} }
var installDir = settings.userDir || process.env.NODE_RED_HOME || "."; var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
var args = ['install','--no-audit','--no-update-notifier','--no-fund','--save','--save-prefix="~"','--production',installName]; var args = ['install','--no-audit','--no-update-notifier','--no-fund','--save','--save-prefix=~','--production',installName];
log.trace(npmCommand + JSON.stringify(args)); log.trace(npmCommand + JSON.stringify(args));
exec.run(npmCommand,args,{ exec.run(npmCommand,args,{
cwd: installDir cwd: installDir

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/registry", "name": "@node-red/registry",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,10 +16,10 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/util": "1.2.0", "@node-red/util": "1.3.0-beta.1",
"semver": "6.3.0", "semver": "6.3.0",
"tar": "6.0.5", "tar": "6.0.5",
"uglify-js": "3.11.2", "uglify-js": "3.11.6",
"when": "3.7.8" "when": "3.7.8"
} }
} }

View File

@ -38,12 +38,20 @@ function handleCommsEvent(event) {
publish(event.topic,event.data,event.retain); publish(event.topic,event.data,event.retain);
} }
function handleStatusEvent(event) { function handleStatusEvent(event) {
var status = { if (!event.status) {
text: event.status.text, delete retained["status/"+event.id]
fill: event.status.fill, } else if (!event.status.text && !event.status.fill && !event.status.shape) {
shape: event.status.shape if (retained["status/"+event.id]) {
}; publish("status/"+event.id,{},false);
publish("status/"+event.id,status,true); }
} else {
var status = {
text: event.status.text,
fill: event.status.fill,
shape: event.status.shape
};
publish("status/"+event.id,status,true);
}
} }
function handleRuntimeEvent(event) { function handleRuntimeEvent(event) {
runtime.log.trace("runtime event: "+JSON.stringify(event)); runtime.log.trace("runtime event: "+JSON.stringify(event));

View File

@ -319,6 +319,11 @@ class Flow {
node.error(err); node.error(err);
} }
} }
if (removedMap[stopList[i]]) {
events.emit("node-status",{
id: node.id
});
}
} }
} }
return Promise.all(promises); return Promise.all(promises);

View File

@ -18,6 +18,7 @@ const clone = require("clone");
const Flow = require('./Flow').Flow; const Flow = require('./Flow').Flow;
const context = require('../nodes/context'); const context = require('../nodes/context');
const util = require("util"); const util = require("util");
const events = require("../events");
const redUtil = require("@node-red/util").util; const redUtil = require("@node-red/util").util;
const flowUtil = require("./util"); const flowUtil = require("./util");
@ -308,7 +309,26 @@ class Subflow extends Flow {
super.start(diff); super.start(diff);
} }
/**
* Stop this subflow.
* The `stopList` argument helps define what needs to be stopped in the case
* of a modified-nodes/flows type deploy.
* @param {[type]} stopList [description]
* @param {[type]} removedList [description]
* @return {[type]} [description]
*/
stop(stopList, removedList) {
const nodes = Object.keys(this.activeNodes);
return super.stop(stopList, removedList).then(res => {
nodes.forEach(id => {
events.emit("node-status",{
id: id
});
})
return res;
})
}
/** /**
* Get environment variable of subflow * Get environment variable of subflow
* @param {String} name name of env var * @param {String} name name of env var
@ -456,9 +476,9 @@ function remapSubflowNodes(nodes,nodeMap) {
var node = nodes[id]; var node = nodes[id];
if (node.wires) { if (node.wires) {
var outputs = node.wires; var outputs = node.wires;
for (j=0;j<outputs.length;j++) { for (var j=0;j<outputs.length;j++) {
wires = outputs[j]; var wires = outputs[j];
for (k=0;k<wires.length;k++) { for (var k=0;k<wires.length;k++) {
if (nodeMap[outputs[j][k]]) { if (nodeMap[outputs[j][k]]) {
outputs[j][k] = nodeMap[outputs[j][k]].id outputs[j][k] = nodeMap[outputs[j][k]].id
} else { } else {

View File

@ -711,6 +711,7 @@ module.exports = {
* @return a promise for the loading of the config * @return a promise for the loading of the config
*/ */
load: load, load: load,
loadFlows: load,
get:getNode, get:getNode,
eachNode: eachNode, eachNode: eachNode,

View File

@ -25,6 +25,9 @@ var storageModule;
var settingsAvailable; var settingsAvailable;
var sessionsAvailable; var sessionsAvailable;
var Mutex = require('async-mutex').Mutex;
const settingsSaveMutex = new Mutex();
var libraryFlowsCachedResult = null; var libraryFlowsCachedResult = null;
function moduleSelector(aSettings) { function moduleSelector(aSettings) {
@ -114,7 +117,7 @@ var storageModuleInterface = {
}, },
saveSettings: function(settings) { saveSettings: function(settings) {
if (settingsAvailable) { if (settingsAvailable) {
return storageModule.saveSettings(settings); return settingsSaveMutex.runExclusive(() => storageModule.saveSettings(settings))
} else { } else {
return when.resolve(); return when.resolve();
} }

View File

@ -34,11 +34,9 @@ function checkForConfigFile(dir) {
} }
var localfilesystem = { var localfilesystem = {
init: function(_settings, runtime) { init: async function(_settings, runtime) {
settings = _settings; settings = _settings;
var promises = [];
if (!settings.userDir) { if (!settings.userDir) {
if (checkForConfigFile(process.env.NODE_RED_HOME)) { if (checkForConfigFile(process.env.NODE_RED_HOME)) {
settings.userDir = process.env.NODE_RED_HOME settings.userDir = process.env.NODE_RED_HOME
@ -49,33 +47,28 @@ var localfilesystem = {
} }
} }
if (!settings.readOnly) { if (!settings.readOnly) {
promises.push(fs.ensureDir(fspath.join(settings.userDir,"node_modules"))); await fs.ensureDir(fspath.join(settings.userDir,"node_modules"));
} }
sessions.init(settings); sessions.init(settings);
promises.push(runtimeSettings.init(settings)); await runtimeSettings.init(settings);
promises.push(library.init(settings)); await library.init(settings);
promises.push(projects.init(settings, runtime)); await projects.init(settings, runtime);
var packageFile = fspath.join(settings.userDir,"package.json"); var packageFile = fspath.join(settings.userDir,"package.json");
var packagePromise = Promise.resolve();
if (!settings.readOnly) { if (!settings.readOnly) {
packagePromise = function() { try {
try { fs.statSync(packageFile);
fs.statSync(packageFile); } catch(err) {
} catch(err) { var defaultPackage = {
var defaultPackage = { "name": "node-red-project",
"name": "node-red-project", "description": "A Node-RED Project",
"description": "A Node-RED Project", "version": "0.0.1",
"version": "0.0.1", "private": true
"private": true };
}; return util.writeFile(packageFile,JSON.stringify(defaultPackage,"",4));
return util.writeFile(packageFile,JSON.stringify(defaultPackage,"",4));
}
return true;
} }
} }
return Promise.all(promises).then(packagePromise);
}, },

View File

@ -0,0 +1,186 @@
{
"runtime": {
"welcome": "Добро пожаловать в Node-RED",
"version": "Версия __component__: __version__",
"unsupported_version": "Неподдерживаемая версия __component__. Требуется: __requires__ Найдено: __version__",
"paths": {
"settings": "Файл настроек : __path__",
"httpStatic": "HTTP статика : __path__"
}
},
"server": {
"loading": "Загрузка узлов палитры",
"palette-editor": {
"disabled": "Редактор палитры отключен : пользовательские настройки",
"npm-not-found": "Редактор палитры отключен : команда npm не найдена",
"npm-too-old": "Редактор палитры отключен : устаревшая версия npm. Требуется npm >= 3.x"
},
"errors": "Не удалось зарегистрировать __count__ тип узла",
"errors_plural_2": "Не удалось зарегистрировать __count__ типа узла",
"errors_plural_5": "Не удалось зарегистрировать __count__ типов узла",
"errors-help": "Запустите с -v для деталей",
"missing-modules": "Недостающие модули узлов:",
"node-version-mismatch": "Модуль узла не может быть загружен в этой версии. Требуется: __version__ ",
"type-already-registered": "'__type__' уже зарегистрирован модулем __module__",
"removing-modules": "Удаление модулей из конфига",
"added-types": "Добавлены типы узлов:",
"removed-types": "Удалены типы узлов:",
"install": {
"invalid": "Неверное имя модуля",
"installing": "Установка модуля: __name__, версия: __version__",
"installed": "Установлен модуль: __name__",
"install-failed": "Установка не удалась",
"install-failed-long": "Установка модуля __name__ не удалась:",
"install-failed-not-found": "Модуль $t(server.install.install-failed-long) не найден",
"install-failed-name": "$t(server.install.install-failed-long) неверное имя модуля: __name__",
"install-failed-url": "$t(server.install.install-failed-long) неверный url: __url__",
"upgrading": "Обновление модуля: __name__ до версии: __version__",
"upgraded": "Обновлен модуль: __name__. Перезапустите Node-RED, чтобы использовать новую версию",
"upgrade-failed-not-found": "Версия $t(server.install.install-failed-long) не найдена",
"uninstalling": "Удаление модуля: __name__",
"uninstall-failed": "Удаление не удалось",
"uninstall-failed-long": "Удаление модуля __name__ не удалось:",
"uninstalled": "Удален модуль: __name__"
},
"unable-to-listen": "Невозможно прослушивать __listenpath__",
"port-in-use": "Ошибка: порт используется",
"uncaught-exception": "Непойманное исключение:",
"admin-ui-disabled": "Пользовательский интерфейс администратора отключен",
"now-running": "Сервер теперь работает на __listenpath__",
"failed-to-start": "Не удалось запустить сервер:",
"headless-mode": "Работает в безголовом (headless) режиме",
"httpadminauth-deprecated": "использование httpAdminAuth устарело. Используйте adminAuth вместо этого",
"https": {
"refresh-interval": "Обновление настроек https каждые __interval__ часов",
"settings-refreshed": "Настройки сервера https обновлены",
"refresh-failed": "Не удалось обновить настройки https: __message__",
"nodejs-version": "httpsRefreshInterval требует Node.js 11 или выше",
"function-required": "httpsRefreshInterval требует, чтобы свойство https было функцией"
}
},
"api": {
"flows": {
"error-save": "Ошибка сохранения потоков: __message__",
"error-reload": "Ошибка перезагрузки потоков: __message__"
},
"library": {
"error-load-entry": "Ошибка загрузки записи библиотеки '__path__': __message__",
"error-save-entry": "Ошибка сохранения записи библиотеки '__path__': __message__",
"error-load-flow": "Ошибка загрузки потока '__path__': __message__",
"error-save-flow": "Ошибка сохранения потока '__path__': __message__"
},
"nodes": {
"enabled": "Включены типы узлов:",
"disabled": "Отлючены типы узлов:",
"error-enable": "Не удалось включить узел:"
}
},
"comms": {
"error": "Ошибка канала связи: __message__",
"error-server": "Ошибка сервера связи: __message__",
"error-send": "Ошибка связи при отправке: __message__"
},
"settings": {
"user-not-available": "Не удается сохранить настройки пользователя: __message__",
"not-available": "Настройки недоступны",
"property-read-only": "Свойство '__prop__' доступно только для чтения"
},
"nodes": {
"credentials": {
"error":"Ошибка при загрузке учетных данных: __message__",
"error-saving":"Ошибка при сохранении учетных данных: __message__",
"not-registered": "Тип учетных данных '__type__' не зарегистрирован",
"system-key-warning": "\n\n---------------------------------------------------------------------\nВаш файл учетных данных потока зашифрован с использованием\nсгенерированного системой ключа.\n\nЕсли сгенерированный системой ключ по какой-либо причине утерян, файл\nс Вашими учетными данными не подлежит восстановлению. Вам придется\nудалить его и повторно ввести свои учетные данные.\n\nВы должны установить свой собственный ключ, используя опцию\n'credentialSecret' в Вашем файле настроек. После этого Node-RED\nповторно зашифрует Ваш файл учетных данных, используя выбранный Вами\nключ, при следующем развертывании изменений.\n---------------------------------------------------------------------\n"
},
"flows": {
"safe-mode": "Потоки остановлены в безопасном режиме. Разверните, чтобы запустить.",
"registered-missing": "Отсутствует зарегистрированный тип: __type__",
"error": "Ошибка загрузки потоков: __message__",
"starting-modified-nodes": "Запуск измененных узлов",
"starting-modified-flows": "Запуск измененных потоков",
"starting-flows": "Запуск потоков",
"started-modified-nodes": "Запущены измененные узлы",
"started-modified-flows": "Запущены измененные потоки",
"started-flows": "Запущены потоки",
"stopping-modified-nodes": "Остановка измененных узлов",
"stopping-modified-flows": "Остановка измененных потоков",
"stopping-flows": "Остановка потоков",
"stopped-modified-nodes": "Остановлены измененные узлы",
"stopped-modified-flows": "Остановлены измененные потоки",
"stopped-flows": "Остановлены потоки",
"stopped": "Остановлен",
"stopping-error": "Ошибка остановки узла: __message__",
"added-flow": "Добавление потока: __label__",
"updated-flow": "Обновлен поток: __label__",
"removed-flow": "Удален поток: __label__",
"missing-types": "Ожидание регистрации отсутствующих типов:",
"missing-type-provided": " - __type__ (предоставлен npm-модулем __module__)",
"missing-type-install-1": "Чтобы установить любой из этих отсутствующих модулей, запустите:",
"missing-type-install-2": "в каталоге:"
},
"flow": {
"unknown-type": "Неизвестный тип: __type__",
"missing-types": "недостающие типы",
"error-loop": "Сообщение превысило максимальное количество уловов"
},
"index": {
"unrecognised-id": "Нераспознанный идентификатор: __id__",
"type-in-use": "Тип используется: __msg__",
"unrecognised-module": "Нераспознанный модуль: __module__"
},
"registry": {
"localfilesystem": {
"module-not-found": "Не удается найти модуль '__module__'"
}
}
},
"storage": {
"index": {
"forbidden-flow-name": "запрещенное имя потока"
},
"localfilesystem": {
"user-dir": "Каталог пользователя : __path__",
"flows-file": "Файл потоков : __path__",
"create": "Создание нового файла __type__",
"empty": "Существующий файл __type__ пуст",
"invalid": "Существующий файл __type__ не является допустимым json",
"restore": "Восстановление резервной копии файла __type__ : __path__",
"restore-fail": "Восстановление резервной копии файла __type__ не удалось : __message__",
"fsync-fail": "Запись данных файла __path__ на диск не удалась : __message__",
"projects": {
"changing-project": "Установка активного проекта : __project__",
"active-project": "Активный проект : __project__",
"project-not-found": "Проект не найден : __project__",
"no-active-project": "Нет активного проекта : используется файл потоков по умолчанию",
"disabled": "Проекты отключены : editorTheme.projects.enabled=false",
"disabledNoFlag": "Проекты отключены : установите editorTheme.projects.enabled=true , чтобы включить",
"git-not-found": "Проекты отключены : команда git не найдена",
"git-version-old": "Проекты отключены : git __version__ не поддерживается. Требуется 2.x",
"summary": "Node-RED проект",
"readme": "### О проекте\n\nЭто README.md файл Вашего проекта. Он помогает пользователям понять, что\nделает Ваш проект, как его использовать и все остальное, что им может\nпонадобиться знать."
}
}
},
"context": {
"log-store-init": "Хранилище контекста : '__name__' [__info__]",
"error-loading-module": "Ошибка загрузки хранилища контекста: __message__",
"error-loading-module2": "Ошибка загрузки хранилища контекста '__module__': __message__",
"error-module-not-defined": "У хранилища контекста '__storage__' отсутствует опция 'module'",
"error-invalid-module-name": "Неверное имя хранилища контекста: '__name__'",
"error-invalid-default-module": "Хранилище контекста по умолчанию неизвестно: '__storage__'",
"unknown-store": "Задано неизвестное хранилище контекста '__name__'. Используется хранилище по умолчанию.",
"localfilesystem": {
"invalid-json": "Неверный JSON в файле контекста '__file__'",
"error-circular": "Контекст __scope__ содержит циклическую ссылку, которая не может быть сохранена",
"error-write": "Ошибка записи контекста: __message__"
}
}
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/runtime", "name": "@node-red/runtime",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,8 +16,8 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/registry": "1.2.0", "@node-red/registry": "1.3.0-beta.1",
"@node-red/util": "1.2.0", "@node-red/util": "1.3.0-beta.1",
"async-mutex": "0.2.4", "async-mutex": "0.2.4",
"clone": "2.1.2", "clone": "2.1.2",
"express": "4.17.1", "express": "4.17.1",

View File

@ -774,6 +774,12 @@ function encodeObject(msg,opts) {
data: value.toString() data: value.toString()
} }
} }
} else if (typeof value === 'bigint') {
value = {
__enc__: true,
type: 'bigint',
data: value.toString()
}
} else if (value && value.constructor) { } else if (value && value.constructor) {
if (value.type === "Buffer") { if (value.type === "Buffer") {
value.__enc__ = true; value.__enc__ = true;
@ -808,6 +814,13 @@ function encodeObject(msg,opts) {
} else if (msgType === "number") { } else if (msgType === "number") {
msg.format = "number"; msg.format = "number";
msg.msg = msg.msg.toString(); msg.msg = msg.msg.toString();
} else if (msgType === "bigint") {
msg.format = "bigint";
msg.msg = {
__enc__: true,
type: 'bigint',
data: msg.msg.toString()
};
} else if (msg.msg === null || msgType === "undefined") { } else if (msg.msg === null || msgType === "undefined") {
msg.format = (msg.msg === null)?"null":"undefined"; msg.format = (msg.msg === null)?"null":"undefined";
msg.msg = "(undefined)"; msg.msg = "(undefined)";

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/util", "name": "@node-red/util",
"version": "1.2.0", "version": "1.3.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",
@ -18,9 +18,9 @@
"clone": "2.1.2", "clone": "2.1.2",
"i18next": "15.1.2", "i18next": "15.1.2",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.8.3", "jsonata": "1.8.4",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"moment-timezone": "^0.5.31", "moment-timezone": "0.5.32",
"when": "3.7.8" "when": "3.7.8"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red", "name": "node-red",
"version": "1.2.0", "version": "1.3.0-beta.1",
"description": "Low-code programming for event-driven applications", "description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org", "homepage": "http://nodered.org",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -31,10 +31,10 @@
"flow" "flow"
], ],
"dependencies": { "dependencies": {
"@node-red/editor-api": "1.2.0", "@node-red/editor-api": "1.3.0-beta.1",
"@node-red/runtime": "1.2.0", "@node-red/runtime": "1.3.0-beta.1",
"@node-red/util": "1.2.0", "@node-red/util": "1.3.0-beta.1",
"@node-red/nodes": "1.2.0", "@node-red/nodes": "1.3.0-beta.1",
"basic-auth": "2.0.1", "basic-auth": "2.0.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"express": "4.17.1", "express": "4.17.1",

View File

@ -1,17 +1,14 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * This is the default settings file provided by Node-RED.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * It can contain any valid JavaScript code that will get run when Node-RED
* you may not use this file except in compliance with the License. * is started.
* You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * Lines that start with // are commented out.
* Each entry should be separated from the entries above and below by a comma ','
* *
* Unless required by applicable law or agreed to in writing, software * For more information about individual settings, refer to the documentation:
* distributed under the License is distributed on an "AS IS" BASIS, * https://nodered.org/docs/user-guide/runtime/configuration
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/ **/
module.exports = { module.exports = {

View File

@ -6,4 +6,5 @@ npm install --no-save \
webdriverio@^4.14.4 \ webdriverio@^4.14.4 \
chromedriver \ chromedriver \
wdio-browserstack-service@^0.1.19 \ wdio-browserstack-service@^0.1.19 \
browserstack-local@^1.4.4 browserstack-local@^1.4.4 \
mosca@^2.8.3

View File

@ -555,14 +555,68 @@ describe('CSV node', function() {
}); });
it('should convert an array of objects to a multi-line csv', function(done) { it('should convert an array of objects to a multi-line csv', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, var flow = [ { id:"n1", type:"csv", temp:"a,d,c,b", wires:[["n2"]] },
{id:"n2", type:"helper"} ]; {id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() { helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1"); var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2"); var n2 = helper.getNode("n2");
n2.on("input", function(msg) { n2.on("input", function(msg) {
try { try {
msg.should.have.property('payload', '4,3,2,1\n1,2,3,4\n'); msg.should.have.property('payload', '4,1,2,3\n1,4,3,2\n');
done();
}
catch(e) { done(e); }
});
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
n1.emit("input", {payload:testJson});
});
});
it('should convert an array of objects to a multi-line csv and add a header', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:"all", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', 'a,b,c,d\n4,3,2,1\n1,2,3,4\n');
done();
}
catch(e) { done(e); }
});
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
n1.emit("input", {payload:testJson});
});
});
it('should convert an array of objects to a multi-line csv without a template', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', '1,3,2,4\n4,2,3,1\n');
done();
}
catch(e) { done(e); }
});
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
n1.emit("input", {payload:testJson});
});
});
it('should convert an array of objects to a multi-line csv without a template and with a header', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"all", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,2,3,1\n');
done(); done();
} }
catch(e) { done(e); } catch(e) { done(e); }

View File

@ -211,6 +211,82 @@ describe("runtime-api/comms", function() {
}); });
}).catch(done); }).catch(done);
}) })
it('retains non-blank status message',function(done){
eventHandlers['node-status']({
id: "node1234",
status: {text:"hello"}
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
messages.should.have.length(1);
messages[0].should.have.property("topic","status/node1234");
messages[0].should.have.property("data",{text:"hello", fill: undefined, shape: undefined});
done();
});
}).catch(done);
})
it('does not retain blank status message',function(done){
eventHandlers['node-status']({
id: "node1234",
status: {}
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
messages.should.have.length(0);
done();
});
}).catch(done);
})
it('does not send blank status if first status',function(done){
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
eventHandlers['node-status']({
id: "node5678",
status: {}
})
messages.should.have.length(0);
done()
})
}).catch(done);
});
it('sends blank status if replacing retained',function(done){
eventHandlers['node-status']({
id: "node5678",
status: {text:"hello"}
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() {
messages.should.have.length(1);
eventHandlers['node-status']({
id: "node5678",
status: {}
})
messages.should.have.length(2);
done()
})
}).catch(done);
});
it('does not retain initial status blank message',function(done){
eventHandlers['node-status']({
id: "my-event",
status: {}
})
messages.should.have.length(0);
comms.addConnection({client: clientConnection}).then(function() {
return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() {
messages.should.have.length(1);
messages[0].should.have.property("topic","my-event");
messages[0].should.have.property("data","my-payload");
done();
});
}).catch(done);
})
it('retained messages get cleared',function(done) { it('retained messages get cleared',function(done) {
eventHandlers['comms']({ eventHandlers['comms']({
topic: "my-event", topic: "my-event",