Merge branch 'dev' into groups

This commit is contained in:
Nick O'Leary
2020-03-30 23:42:52 +01:00
151 changed files with 5315 additions and 1453 deletions

View File

@@ -1,6 +1,6 @@
---
name: Bug report
about: Reproducable software issues in the core of Node-RED
about: Reproducible software issues in the core of Node-RED
title: ''
labels: ''
assignees: ''

View File

@@ -29,6 +29,6 @@ the [forum](https://discourse.nodered.org) or
<!-- Put an `x` in the boxes that apply -->
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
- [ ] For non-bugfix PRs, I have discussed this change on the mailing list/slack team.
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
- [ ] I have run `grunt` to verify the unit tests pass
- [ ] I have added suitable unit tests to cover the new/changed functionality

File diff suppressed because it is too large Load Diff

View File

@@ -195,7 +195,8 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.0.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/marked/marked.min.js",
"node_modules/marked/marked.min.js",
"node_modules/dompurify/dist/purify.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/i18next/i18next.min.js",
"node_modules/jsonata/jsonata-es5.min.js",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "1.0.3",
"version": "1.0.4",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -24,7 +24,7 @@
}
],
"dependencies": {
"ajv": "6.10.2",
"ajv": "6.12.0",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
@@ -34,47 +34,49 @@
"cookie": "0.4.0",
"cookie-parser": "1.4.4",
"cors": "2.8.5",
"cron": "1.7.2",
"cron": "1.8.2",
"denque": "1.4.1",
"express": "4.17.1",
"express-session": "1.17.0",
"fs-extra": "8.1.0",
"fs.notify": "0.0.4",
"hash-sum": "2.0.0",
"https-proxy-agent": "2.2.4",
"https-proxy-agent": "5.0.0",
"i18next": "15.1.2",
"iconv-lite": "0.5.0",
"iconv-lite": "0.5.1",
"is-utf8": "0.2.1",
"js-yaml": "3.13.1",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.0",
"jsonata": "1.8.1",
"media-typer": "1.1.0",
"memorystore": "1.6.1",
"memorystore": "1.6.2",
"mime": "2.4.4",
"mqtt": "2.18.8",
"multer": "1.4.2",
"mustache": "3.0.2",
"mustache": "4.0.0",
"node-red-node-rbe": "^0.2.6",
"node-red-node-sentiment": "^0.1.6",
"node-red-node-tail": "^0.1.0",
"nopt": "4.0.1",
"oauth2orize": "1.11.0",
"on-headers": "1.0.2",
"passport": "0.4.0",
"passport": "0.4.1",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.4.1",
"request": "2.88.0",
"semver": "6.3.0",
"uglify-js": "3.6.9",
"uglify-js": "3.8.0",
"when": "3.7.8",
"ws": "6.2.1",
"xml2js": "0.4.22"
"xml2js": "0.4.23"
},
"optionalDependencies": {
"bcrypt": "3.0.6"
},
"devDependencies": {
"marked": "0.8.0",
"dompurify": "2.0.8",
"grunt": "~1.0.4",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.2",
@@ -102,7 +104,7 @@
"mocha": "^5.2.0",
"mosca": "^2.8.3",
"node-red-node-test-helper": "^0.2.3",
"node-sass": "^4.13.0",
"node-sass": "^4.13.1",
"should": "^8.4.0",
"sinon": "1.17.7",
"stoppable": "^1.1.0",

View File

@@ -36,6 +36,7 @@ var log = require("@node-red/util").log; // TODO: separate module
passport.use(strategies.bearerStrategy.BearerStrategy);
passport.use(strategies.clientPasswordStrategy.ClientPasswordStrategy);
passport.use(strategies.anonymousStrategy);
passport.use(strategies.tokensStrategy);
var server = oauth2orize.createServer();
@@ -60,7 +61,7 @@ function init(_settings,storage) {
function needsPermission(permission) {
return function(req,res,next) {
if (settings && settings.adminAuth) {
return passport.authenticate(['bearer','anon'],{ session: false })(req,res,function() {
return passport.authenticate(['bearer','tokens','anon'],{ session: false })(req,res,function() {
if (!req.user) {
return next();
}
@@ -100,7 +101,10 @@ function login(req,res) {
}
} else if (mergedAdminAuth.type === "strategy") {
var urlPrefix = (settings.httpAdminRoot==='/')?"":settings.httpAdminRoot;
var urlPrefix = (settings.httpAdminRoot||"").replace(/\/$/,"");
if (urlPrefix.length > 0) {
urlPrefix += "/";
}
response = {
"type":"strategy",
"prompts":[{type:"button",label:mergedAdminAuth.strategy.label, url: urlPrefix + "auth/strategy"}]

View File

@@ -123,9 +123,38 @@ AnonymousStrategy.prototype.authenticate = function(req) {
});
}
function TokensStrategy() {
passport.Strategy.call(this);
this.name = 'tokens';
}
util.inherits(TokensStrategy, passport.Strategy);
TokensStrategy.prototype.authenticate = function(req) {
var self = this;
var token = null;
if (Users.tokenHeader() === 'authorization') {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
token = req.headers.authorization.split(' ')[1];
}
} else {
token = req.headers[Users.tokenHeader()];
}
if (token) {
Users.tokens(token).then(function(admin) {
if (admin) {
self.success(admin,{scope:admin.permissions});
} else {
self.fail(401);
}
});
} else {
self.fail(401);
}
}
module.exports = {
bearerStrategy: bearerStrategy,
clientPasswordStrategy: clientPasswordStrategy,
passwordTokenExchange: passwordTokenExchange,
anonymousStrategy: new AnonymousStrategy()
anonymousStrategy: new AnonymousStrategy(),
tokensStrategy: new TokensStrategy()
}

View File

@@ -59,7 +59,9 @@ function getDefaultUser() {
var api = {
get: get,
authenticate: authenticate,
default: getDefaultUser
default: getDefaultUser,
tokens: getDefaultUser,
tokenHeader: "authorization"
}
function init(config) {
@@ -105,6 +107,12 @@ function init(config) {
} else {
api.default = getDefaultUser;
}
if (config.tokens && typeof config.tokens === "function") {
api.tokens = config.tokens;
if (config.tokenHeader && typeof config.tokenHeader === "string") {
api.tokenHeader = config.tokenHeader.toLowerCase();
}
}
}
function cleanUser(user) {
if (user && user.hasOwnProperty('password')) {
@@ -118,5 +126,7 @@ module.exports = {
init: init,
get: function(username) { return api.get(username).then(cleanUser)},
authenticate: function() { return api.authenticate.apply(null, arguments) },
default: function() { return api.default(); }
default: function() { return api.default(); },
tokens: function(token) { return api.tokens(token); },
tokenHeader: function() { return api.tokenHeader }
};

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "1.0.3",
"version": "1.0.4",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,21 +16,21 @@
}
],
"dependencies": {
"@node-red/util": "1.0.3",
"@node-red/editor-client": "1.0.3",
"@node-red/util": "1.0.4",
"@node-red/editor-client": "1.0.4",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.0",
"express": "4.17.1",
"memorystore": "1.6.1",
"memorystore": "1.6.2",
"mime": "2.4.4",
"mustache": "3.0.2",
"mustache": "4.0.0",
"oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.4.0",
"passport": "0.4.1",
"when": "3.7.8",
"ws": "6.2.1"
},

View File

@@ -34,11 +34,11 @@
"view" : "Ansicht",
"grid" : "Gitter",
"showGrid" : "Raster anzeigen",
"snapGrid" : "Einrasten am Raster",
"snapGrid" : "Am Raster ausrichten",
"gridSize" : "Rastergröße",
"textDir" : "Textrichtung",
"defaultDir" : "Standard",
"ltr" : "Links-nach-rechts",
"ltr" : "Von links nach rechts",
"rtl" : "Von rechts nach links",
"auto" : "Kontextuell"
},
@@ -53,15 +53,15 @@
"import" : "Import",
"export" : "Exportieren",
"search" : "Flows durchsuchen",
"searchInput" : "durchsuchen Sie Ihre Flows",
"searchInput" : "Flows durchsuchen",
"subflows" : "Subflow",
"createSubflow" : "Subflow erstellen",
"selectionToSubflow" : "Auswahl für Subflow",
"selectionToSubflow" : "Auswahl zu Subflow",
"flows" : "Flows",
"add" : "Hinzufügen",
"rename" : "Umbenennen",
"delete" : "Löschen",
"keyboardShortcuts" : "Tastaturkurzbefehle",
"keyboardShortcuts" : "Tastenkürzel",
"login" : "Anmelden",
"logout" : "Abmelden",
"editPalette" : "Palette verwalten",
@@ -217,7 +217,7 @@
"remote" : "Ferne Änderungen",
"reviewChanges" : "Änderungen prüfen",
"noBinaryFileShowed" : "Der Inhalt der Binärdatei kann nicht angezeigt",
"viewCommitDiff" : "Änderungen festschreiben",
"viewCommitDiff" : "Änderungen committen",
"compareChanges" : "Änderungen vergleichen",
"saveConflict" : "Konfliktlösung speichern",
"conflictHeader" : "<span> __resolved__ </span> von <span> __unresolved__ </span> -Konflikten behoben",
@@ -226,8 +226,8 @@
"newVersionError" : "Neue Version enthält keine gültige JSON-Datei:"
},
"subflow" : {
"editSubflow" : "Flowschablone bearbeiten: __name__",
"edit" : "Flowsschablone bearbeiten",
"editSubflow" : "Subflow bearbeiten: __name__",
"edit" : "Subflow bearbeiten",
"subflowInstances" : "Es ist __count__ Instanz dieser Subflow-Vorlage vorhanden.",
"subflowInstances_plural" : "Es gibt __count__ Instanzen dieser Subflow-Vorlage.",
"editSubflowProperties" : "Eigenschaften bearbeiten",
@@ -266,7 +266,7 @@
}
},
"keyboard" : {
"title" : "Tastaturkurzbefehle",
"title" : "Tastenkürzel",
"keyboard" : "Tastatur",
"filterActions" : "Filteraktionen",
"shortcut" : "Direktaufruf",
@@ -283,7 +283,7 @@
"exportNode" : "Node exportieren",
"nudgeNode" : "Ausgewählte Nodes verschieben (1px)",
"moveNode" : "Ausgewählte Nodes verschieben (20px)",
"toggleSidebar" : "Seitenleiste ein-/ausschalten",
"toggleSidebar" : "Seitenleiste ein-/ausblenden",
"copyNode" : "Ausgewählte Nodes kopieren",
"cutNode" : "Ausgewählte Nodes ausschneiden",
"pasteNode" : "Node einfügen",
@@ -308,7 +308,7 @@
},
"palette" : {
"noInfo" : "Keine Informationen verfügbar",
"filter" : "Filter Nodes",
"filter" : "Nodes filtern",
"search" : "Suchmodule",
"addCategory" : "Neu hinzufügen ...",
"label" : {
@@ -366,11 +366,11 @@
"remove" : "entfernen",
"update" : "Update auf __version__",
"updated" : "aktualisiert",
"install" : "installieren",
"installed" : "installiert",
"install" : "Installieren",
"installed" : "Installiert",
"loading" : "Kataloge werden geladen ...",
"tab-nodes" : "Nodes",
"tab-install" : "installieren",
"tab-install" : "Installieren",
"sort" : "Sortierung:",
"sortAZ" : "a-z",
"sortRecent" : "kürzlich",
@@ -452,7 +452,7 @@
"name" : "Kontextdaten",
"label" : "Kontext",
"none" : "keine ausgewählt",
"refresh" : "Aktualisierung zum Laden",
"refresh" : "Zum Aktualisieren neu laden",
"empty" : "leer",
"node" : "Node",
"flow" : "Flow",
@@ -477,7 +477,7 @@
"none" : "Keine",
"install" : "installieren",
"removeFromProject" : "Aus Projekt entfernen",
"addToProject" : "zu Projekt hinzufügen",
"addToProject" : "Zu Projekt hinzufügen",
"files" : "Dateien",
"flow" : "Flow",
"credentials" : "Berechtigungsnachweis",
@@ -510,7 +510,7 @@
},
"userSettings" : {
"committerDetail" : "Committer-Details",
"committerTip" : "Leer Wert für Systemstandardwert belassen",
"committerTip" : "Leer lassen für Systemstandard",
"userName" : "Benutzername",
"email" : "E-Mail",
"sshKeys" : "SSH-Schlüssel",
@@ -544,7 +544,7 @@
"revertChanges" : "Änderungen zurücksetzen",
"localChanges" : "Lokale Änderungen",
"none" : "Keine",
"conflictResolve" : "Alle Konflikte wurden aufgelöst. Festschreiben der Änderungen, um den Mischvorgang abzuschließen.",
"conflictResolve" : "Alle Konflikte wurden aufgelöst. Committe die Änderungen, um den Merge Request abzuschließen.",
"localFiles" : "Lokale Dateien",
"all" : "alle",
"unmergedChanges" : "Nicht zusammengeführte Änderungen",

View File

@@ -636,7 +636,6 @@
"removeFromProject": "remove from project",
"addToProject": "add to project",
"files": "Files",
"package": "Package",
"flow": "Flow",
"credentials": "Credentials",
"package":"Package",
@@ -1001,7 +1000,8 @@
"passphrase": "Passphrase",
"retry": "Retry",
"update-failed": "Failed to update auth",
"unhandled": "Unhandled error response"
"unhandled": "Unhandled error response",
"host-key-verify-failed": "<p>Host key verification failed.</p><p>The repository host key could not be verified. Please update your <code>known_hosts</code> file and try again."
},
"create-branch-list": {
"invalid": "Invalid branch",

View File

@@ -24,7 +24,7 @@
"buffer": "buffer",
"object": "对象",
"jsonString": "JSON字符串",
"undefined": "定义",
"undefined": "定义",
"null": "空"
}
},
@@ -1008,6 +1008,7 @@
"en-US": "英文",
"ja": "日语",
"ko": "韩文",
"zh-CN": "简体中文"
"zh-CN": "简体中文",
"zh-TW": "繁体中文"
}
}

View File

@@ -262,5 +262,9 @@
"$distinct": {
"args": "array",
"desc": "返回一个数组,其中重复的值已从`数组`中删除"
},
"$type": {
"args": "value",
"desc": "以字符串形式返回`值`的类型。 如果该`值`未定义,则将返回`未定义`"
}
}

View File

@@ -15,6 +15,17 @@
"next": "下一步",
"clone": "複製專案",
"cont": "Continue"
},
"type": {
"string": "字符串",
"number": "數值",
"boolean": "布林",
"array": "數組",
"buffer": "buffer",
"object": "對象",
"jsonString": "JSON字符串",
"undefined": "未定義",
"null": "空"
}
},
"workspace": {
@@ -29,8 +40,7 @@
"enabled": "有效",
"disabled": "無效",
"info": "詳細描述",
"selectNodes": "點擊節點用於選擇",
"tip": "詳細描述支援Markdown羽量級標記語言並將出現在資訊標籤中。"
"selectNodes": "點擊節點用於選擇"
},
"menu": {
"label": {
@@ -45,14 +55,14 @@
"ltr": "從左到右",
"rtl": "從右到左",
"auto": "上下文",
"language": "Language",
"browserDefault": "Browser default"
"language": "語言",
"browserDefault": "瀏覽器默認"
},
"sidebar": {
"show": "顯示側邊欄"
},
"palette": {
"show": "Show palette"
"show": "顯示控制板"
},
"settings": "設置",
"userSettings": "使用者設置",
@@ -81,10 +91,7 @@
"projects-new": "新專案",
"projects-open": "開啟專案",
"projects-settings": "專案設定",
"showNodeLabelDefault": "顯示新添加節點的標籤",
"clipboard": "剪貼簿",
"library": "庫",
"examples": "範例"
"showNodeLabelDefault": "顯示新添加節點的標籤"
}
},
"actions": {
@@ -204,8 +211,7 @@
},
"copyMessagePath": "已複製路徑",
"copyMessageValue": "已複製數值",
"copyMessageValue_truncated": "已複製捨棄的數值",
"selectNodes": "選擇上面的文本並複製到剪貼簿"
"copyMessageValue_truncated": "已複製捨棄的數值"
},
"deploy": {
"deploy": "部署",
@@ -237,7 +243,7 @@
"undeployedChanges": "您有未部署的更改。\n\n離開此頁面將丟失這些更改。",
"improperlyConfigured": "工作區包含一些未正確配置的節點:",
"unknown": "工作區包含一些未知的節點類型:",
"confirm": "確定要部署嗎?",
"confirm": "確定要部署嗎?",
"doNotWarn": "不要再對此發出警告",
"conflict": "伺服器正在運行較新的一組流程。",
"backgroundUpdate": "伺服器上的流程已更新。",
@@ -300,8 +306,7 @@
"errors": {
"noNodesSelected": "<strong>無法創建子流程</strong>: 未選擇節點",
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
},
"format": "標記格式"
}
},
"editor": {
"configEdit": "編輯",
@@ -316,17 +321,53 @@
"addNewType": "添加新的__type__節點",
"nodeProperties": "節點屬性",
"label": "Label",
"color": "顏色",
"portLabels": "埠標籤",
"labelInputs": "輸入",
"labelOutputs": "輸出",
"settingIcon": "Icon",
"default": "默認",
"noDefaultLabel": "無",
"defaultLabel": "使用默認標籤",
"searchIcons": "搜尋 icons",
"searchIcons": "搜尋圖標",
"useDefault": "使用默認",
"description": "描述",
"show": "顯示",
"hide": "隱藏",
"locale": "選擇界面語言",
"icon": "圖標",
"inputType": "輸入類型",
"inputs": {
"input": "輸入",
"select": "選擇",
"checkbox": "復選框",
"spinner": "微調器",
"none": "空",
"hidden": "隱藏屬性"
},
"types": {
"str": "字符串",
"num": "數字",
"bool": "布爾",
"json": "JSON",
"bin": "buffer",
"env": "環境變量"
},
"menu": {
"input": "輸入",
"select": "選擇",
"checkbox": "復選框",
"spinner": "微調器",
"hidden": "僅標簽"
},
"select": {
"label": "標簽",
"value": "值"
},
"spinner": {
"min": "最小值",
"max": "最大值"
},
"errors": {
"scopeChange": "更改範圍將使其他流程中的節點無法使用",
"invalidProperties": "無效的屬性:"
@@ -356,8 +397,9 @@
"cutNode": "剪切所選節點",
"pasteNode": "粘貼節點",
"undoChange": "撤銷上次執行的更改",
"searchBox": "打開搜框",
"managePalette": "管理面板"
"searchBox": "打開搜框",
"managePalette": "管理面板",
"actionList": "動作列表"
},
"library": {
"library": "庫",
@@ -371,28 +413,27 @@
"savedNodes": "保存的節點",
"savedType": "已保存__type__",
"saveFailed": "保存失敗: __message__",
"newFolder": "新文件夾",
"types": {
"local": "本地",
"examples": "例子"
},
"exportToLibrary": "將節點匯出到庫",
"filename": "檔案名",
"folder": "資料夾",
"filenamePlaceholder": "文件",
"fullFilenamePlaceholder": "a/b/文件",
"folderPlaceholder": "a/b",
"breadcrumb": "庫"
"exportToLibrary": "將節點匯出到庫"
},
"palette": {
"noInfo": "無可用資訊",
"filter": "過濾節點",
"search": "搜模組",
"search": "搜模組",
"addCategory": "添加新的...",
"label": {
"subflows": "子流程",
"network": "網絡",
"common": "共通",
"input": "輸入",
"output": "輸出",
"function": "功能",
"sequence": "序列",
"parser": "解析",
"social": "社交",
"storage": "存儲",
"analysis": "分析",
@@ -459,7 +500,7 @@
"sortRecent": "日期順序",
"more": "增加__count__個",
"errors": {
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制瞭解更多資訊",
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制瞭解更多資訊",
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
"removeFailed": "無法刪除: __module__<br>__message__<br>查看日誌瞭解更多資訊",
"updateFailed": "無法更新: __module__<br>__message__<br>查看日誌瞭解更多資訊",
@@ -529,8 +570,10 @@
"none": "無",
"subflows": "子流程",
"flows": "流程",
"filterUnused": "未使用",
"filterAll": "所有",
"showAllConfigNodes": "顯示所有配置節點",
"filterUnused": "未使用",
"showAllUnusedConfigNodes": "顯示所有未使用的配置節點",
"filtered": "__count__ 個隱藏"
},
"context": {
@@ -543,7 +586,9 @@
"flow": "流程",
"global": "全局的",
"deleteConfirm": "你確定要刪除這個項目嗎?",
"autoRefresh": "自動刷新"
"autoRefresh": "自動刷新",
"refrsh": "刷新",
"delete": "刪除"
},
"palette": {
"name": "節點管理",
@@ -558,6 +603,7 @@
"noSummaryAvailable": "無可用摘要",
"editDescription": "編輯專案描述",
"editDependencies": "編輯項目依賴",
"noDescriptionAvailable": "沒有可用的描述",
"editReadme": "Edit README.md",
"showProjectSettings": "顯示項目設置",
"projectSettings": {
@@ -657,15 +703,15 @@
"moreCommits": "更多提交",
"changeLocalBranch": "變更當地分支",
"createBranchPlaceholder": "查找或創建分支",
"upstream": "上的",
"localOverwrite": "您有可通过切换分支覆的本地更改。您必先提交或撤那些更改。",
"upstream": "上的",
"localOverwrite": "您有可通過切換分支覆的本地更改。您必先提交或撤那些更改。",
"manageRemoteBranch": "管理遠程分支",
"unableToAccess": "無法訪問遠程存儲庫",
"retry": "重試",
"setUpstreamBranch": "設置為上分支",
"setUpstreamBranch": "設置為上分支",
"createRemoteBranchPlaceholder": "查找或創建遠程分支",
"trackedUpstreamBranch": "創建的分支將被設置為跟的上分支。",
"selectUpstreamBranch": "分支將被創建。 在下面選擇以將其設置為被跟的上分支。",
"trackedUpstreamBranch": "創建的分支將被設置為跟的上分支。",
"selectUpstreamBranch": "分支將被創建。 在下面選擇以將其設置為被跟的上分支。",
"pushFailed": "Push失敗因為遠程具有更多的最新提交。請先進行pull與merge然後再嘗試push。",
"push": "push",
"pull": "pull",
@@ -683,7 +729,7 @@
"minsAgo": "__count__分鐘前",
"minsAgo_plural": "__count__分鐘前",
"secondsAgo": "秒前",
"notTracking": "您的本地分支當前未跟遠程分支。",
"notTracking": "您的本地分支當前未跟遠程分支。",
"statusUnmergedChanged": "您的存儲庫中有未合併的更改。您需要解決衝突並提交結果。",
"repositoryUpToDate": "您的存儲庫是最新的。",
"commitsAhead": "您的倉庫領先遠程倉庫__count__次提交。您現在可以push這些提交。",
@@ -748,10 +794,23 @@
},
"jsonEditor": {
"title": "JSON編輯器",
"format": "格式化JSON"
"format": "格式化JSON",
"rawMode": "編輯 JSON",
"uiMode": "Visual編輯器",
"insertAbove": "在上方插入",
"insertBelow": "在下方插入",
"addItem": "添加項目",
"copyPath": "復制路徑到項目",
"expandItems": "展開項目",
"collapseItems": "收合項目",
"duplicate": "重復",
"error": {
"invalidJSON": "無效的JSON: "
}
},
"markdownEditor": {
"title": "Markdown 編輯器",
"expand": "展開",
"format": "F使用markdown格式化",
"heading1": "Heading 1",
"heading2": "Heading 2",
@@ -786,7 +845,7 @@
},
"git-config": {
"setup": "設置您的版本控制客戶端",
"desc0": "Node-RED使用開源工具Git進行版本控制。 它跟對項目文件的更改,並允許您將其推送到遠程存儲庫。",
"desc0": "Node-RED使用開源工具Git進行版本控制。 它跟對項目文件的更改,並允許您將其推送到遠程存儲庫。",
"desc1": "提交一組更改時Git會使用用戶名和電子郵件地址記錄誰進行了更改。 用戶名可以是您想要的任何名稱-不必是您的真實姓名。",
"desc2": "您的Git客戶端已經配置了以下詳細信息。",
"desc3": "您可以稍後在設置對話框的“ Git config”標籤下更改這些設置。",
@@ -905,7 +964,7 @@
"confirm": "您確定要刪除此項目嗎?"
},
"create-project-list": {
"search": "搜您的項目",
"search": "搜您的項目",
"current": "當前的"
},
"require-clean": {
@@ -938,8 +997,19 @@
},
"editor-tab": {
"properties": "屬性",
"envProperties": "環境變量",
"description": "描述",
"appearance": "外觀",
"preview": "UI預覽",
"defaultValue": "默認值",
"env": "環境變量"
},
"languages": {
"de": "德語",
"en-US": "英語",
"ja": "日語",
"ko": "韓語",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文"
}
}

View File

@@ -214,5 +214,57 @@
"$toMillis": {
"args": "timestamp",
"desc": "將ISO 8601格式的字串`timestamp`轉換為從UNIX時間 (1970年1月1日 UTC/GMT的午夜開始到現在的毫秒數。如果該字串的格式不正確則拋出錯誤。"
},
"$env": {
"args": "arg",
"desc": "返回環境變量的值。\n\n這是Node-RED定義的函數。"
},
"$eval": {
"args": "expr [, context]",
"desc": "使用當前上下文來作為評估依據,分析並評估字符串`expr`其中包含文字JSON或JSONata表達式。"
},
"$formatInteger": {
"args": "number, picture",
"desc": "將“數字”轉換為字符串並將其格式化為“圖片”字符串指定的整數表示形式。圖片字符串參數定義了數字的格式並具有與XPath F&O 3.1 規範中的fnformat-integer相同的語法。"
},
"$parseInteger": {
"args": "string, picture",
"desc": "使用“圖片”字符串指定的格式將“字符串”參數的內容解析為整數作為JSON數字。圖片字符串參數與$formatInteger格式相同。."
},
"$error": {
"args": "[str]",
"desc": "引發錯誤並顯示一條消息。 可選的`str`將替代$error()函數評估的默認消息。"
},
"$assert": {
"args": "arg, str",
"desc": "如果`arg`為真,則該函數返回。 如果arg為假則拋出帶有str的異常作為異常消息。"
},
"$single": {
"args": "array, function",
"desc": "返回滿足參數function謂語的array參數中的唯一值 (比如傳遞值時函數返回布林值“true”)。如果匹配值的數量不唯一時,則拋出異常。\n\n應在以下簽名中提供函數`functionvalue [index [array []]]`其中value是數組的每個輸入index是該值的位置整個數組作為第三個參數傳遞。"
},
"$encodeUrl": {
"args": "str",
"desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL組件進行編碼。\n\n示例`$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL進行編碼。\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": "解碼以前由encodeUrlComponent創建的統一資源定位器URL組件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "解碼先前由encodeUrl創建的統一資源定位符URL。 \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": "返回一個數組,其中重復的值已從`數組`中刪除"
},
"$type": {
"args": "value",
"desc": "以字符串形式返回`值`的類型。 如果該`值`未定義,則將返回`未定義`"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "1.0.3",
"version": "1.0.4",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -431,7 +431,7 @@ var RED = (function() {
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.info.set(aboutHeader+marked(data));
RED.sidebar.info.set(aboutHeader+RED.utils.renderMarkdown(data));
RED.sidebar.info.show();
});
}

View File

@@ -38,7 +38,10 @@
this.element.addClass("red-ui-searchBox-input");
this.uiContainer = this.element.wrap("<div>").parent();
this.uiContainer.addClass("red-ui-searchBox-container");
if (this.element.parents("form").length === 0) {
var form = this.element.wrap("<form>").parent();
form.addClass("red-ui-searchBox-form");
}
$('<i class="fa fa-search"></i>').prependTo(this.uiContainer);
this.clearButton = $('<a href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer);
this.clearButton.on("click",function(e) {

View File

@@ -590,8 +590,8 @@ RED.editor = (function() {
// cases, and also prevent browser auto-fill of password
// - the elements cannot be hidden otherwise Chrome will ignore them.
// - the elements need to have id's that imply password/username
$('<div style="position: absolute; top: -2000px;"><input id="red-ui-trap-password" type="password"/></div>').prependTo(dialogForm);
$('<div style="position: absolute; top: -2000px;"><input id="red-ui-trap-username" type="text"/></div>').prependTo(dialogForm);
$('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-password" type="password"/></span>').prependTo(dialogForm);
$('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-username" type="text"/></span>').prependTo(dialogForm);
dialogForm.on("submit", function(e) { e.preventDefault();});
dialogForm.find('input').attr("autocomplete","off");
return dialogForm;
@@ -2355,15 +2355,16 @@ RED.editor = (function() {
buildAppearanceForm(appearanceTab.content,editing_node);
editorTabs.addTab(appearanceTab);
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
trayBody.i18n();
$.getJSON(getCredentialsURL("subflow", subflow.id), function (data) {
subflow.credentials = data;
subflow.credentials._ = $.extend(true,{},data);
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
$("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name"));
trayBody.i18n();
finishedBuilding = true;
done();
});

View File

@@ -102,7 +102,7 @@
var f = $(this).val();
var args = RED._('jsonata:'+f+".args",{defaultValue:''});
var title = "<h5>"+f+"("+args+")</h5>";
var body = marked(RED._('jsonata:'+f+'.desc',{defaultValue:''}));
var body = RED.utils.renderMarkdown(RED._('jsonata:'+f+'.desc',{defaultValue:''}));
$("#red-ui-editor-type-expression-help").html(title+"<p>"+body+"</p>");
})

View File

@@ -107,7 +107,7 @@
clearTimeout(changeTimer);
changeTimer = setTimeout(function() {
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
$(".red-ui-editor-type-markdown-panel-preview").html(marked(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
},200);
})
@@ -116,7 +116,7 @@
}
if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(marked(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
}
panels = RED.panels.create({
id:"red-ui-editor-type-markdown-panels",

View File

@@ -524,12 +524,12 @@ RED.keyboard = (function() {
var pane = $('<div id="red-ui-settings-tab-keyboard"></div>');
$('<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
'<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input id="red-ui-settings-tab-keyboard-filter" type="text" data-i18n="[placeholder]keyboard.filterActions"></div>'+
'<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input autocomplete="off" name="keyboard-filter" id="red-ui-settings-tab-keyboard-filter" type="text" data-i18n="[placeholder]keyboard.filterActions"></div>'+
'<div class="keyboard-shortcut-entry-key" data-i18n="keyboard.shortcut"></div>'+
'<div class="keyboard-shortcut-entry-scope" data-i18n="keyboard.scope"></div>'+
'</div>').appendTo(pane);
pane.find("input").searchBox({
pane.find("#red-ui-settings-tab-keyboard-filter").searchBox({
delay: 100,
change: function() {
var filterValue = $(this).val().trim();

View File

@@ -384,6 +384,7 @@ RED.palette.editor = (function() {
handleCatalogResponse(null,catalog,index,v);
refreshNodeModuleList();
}).fail(function(jqxhr, textStatus, error) {
console.warn("Error loading catalog",catalog,":",error);
handleCatalogResponse(jqxhr,catalog,index);
}).always(function() {
handled++;

View File

@@ -269,7 +269,7 @@ RED.palette = (function() {
RED.view.focus();
var helpText;
if (nt.indexOf("subflow:") === 0) {
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
} else {
helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
}
@@ -408,7 +408,7 @@ RED.palette = (function() {
RED.workspaces.show(nt.substring(8));
e.preventDefault();
});
nodeInfo = marked(def.info||"");
nodeInfo = RED.utils.renderMarkdown(def.info||"");
}
setLabel(nt,d,label,nodeInfo);
@@ -478,7 +478,7 @@ RED.palette = (function() {
} else if (portOutput.length !== 0 && sf.out.length === 0) {
portOutput.remove();
}
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||""));
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||""));
setIcon(paletteNode,sf);
var currentCategory = paletteNode.data('category');

View File

@@ -158,7 +158,7 @@ RED.projects.settings = (function() {
container.empty();
var desc;
if (activeProject.description) {
desc = marked(activeProject.description);
desc = RED.utils.renderMarkdown(activeProject.description);
} else {
desc = '<span class="red-ui-help-info-none">' + RED._("sidebar.project.noDescriptionAvailable") + '</span>';
}

View File

@@ -30,13 +30,13 @@ RED.projects.userSettings = (function() {
$('<div class="red-ui-settings-section-description"></div>').appendTo(gitconfigContainer).text(RED._("editor:sidebar.project.userSettings.committerTip"));
var row = $('<div class="red-ui-settings-row"></div>').appendTo(gitconfigContainer);
$('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.userName")).appendTo(row);
gitUsernameInput = $('<input type="text">').appendTo(row);
$('<label for="user-settings-gitconfig-username"></label>').text(RED._("editor:sidebar.project.userSettings.userName")).appendTo(row);
gitUsernameInput = $('<input type="text" id="user-settings-gitconfig-username">').appendTo(row);
gitUsernameInput.val(currentGitSettings.user.name||"");
row = $('<div class="red-ui-settings-row"></div>').appendTo(gitconfigContainer);
$('<label for=""></label>').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row);
gitEmailInput = $('<input type="text">').appendTo(row);
$('<label for="user-settings-gitconfig-email"></label>').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row);
gitEmailInput = $('<input type="text" id="user-settings-gitconfig-email">').appendTo(row);
gitEmailInput.val(currentGitSettings.user.email||"");
}

View File

@@ -1939,100 +1939,121 @@ RED.projects = (function() {
}
}).fail(function(xhr,textStatus,err) {
var responses;
if (options.responses && options.responses[xhr.status]) {
responses = options.responses[xhr.status];
if (typeof responses === 'function') {
resultCallback = responses;
resultCallbackArgs = {error:responses.statusText};
return;
} else if (options.handleAuthFail !== false && xhr.responseJSON.code === 'git_auth_failed') {
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
} else if (options.handleAuthFail !== false && (xhr.responseJSON.code === 'git_auth_failed' || xhr.responseJSON.code === 'git_host_key_verification_failed')) {
if (xhr.responseJSON.code === 'git_auth_failed') {
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
var message = $('<div>'+
var message = $('<div>'+
'<div class="form-row">'+RED._("projects.send-req.auth-req")+':</div>'+
'<div class="form-row"><div style="margin-left: 20px;">'+url+'</div></div>'+
'</div>');
var isSSH = false;
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-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-key">SSH Key</label>').appendTo(row);
var projectRepoSSHKeySelect = $('<select id="projects-user-auth-key">').width('70%').appendTo(row);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
var isSSH = false;
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-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-key">SSH Key</label>').appendTo(row);
var projectRepoSSHKeySelect = $('<select id="projects-user-auth-key">').width('70%').appendTo(row);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
//TODO: handle no keys yet setup
}
});
if (count === 0) {
//TODO: handle no keys yet setup
}
});
row = $('<div class="form-row"></div>').appendTo(message);
$('<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);
}
row = $('<div class="form-row"></div>').appendTo(message);
$('<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);
}
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
//id: "node-dialog-delete",
//class: 'leftButton',
text: RED._("common.label.cancel"),
click: function() {
notification.close();
}
},{
text: '<span><i class="fa fa-refresh"></i> ' +RED._("projects.send-req.retry") +'</span>',
click: function() {
body = body || {};
var authBody = {};
if (isSSH) {
authBody.keyFile = $('#projects-user-auth-key').val();
authBody.passphrase = $('#projects-user-auth-passphrase').val();
} else {
authBody.username = $('#projects-user-auth-username').val();
authBody.password = $('#projects-user-auth-password').val();
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
//id: "node-dialog-delete",
//class: 'leftButton',
text: RED._("common.label.cancel"),
click: function() {
notification.close();
}
var done = function(err) {
if (err) {
console.log(RED._("projects.send-req.update-failed"));
console.log(err);
},{
text: '<span><i class="fa fa-refresh"></i> ' +RED._("projects.send-req.retry") +'</span>',
click: function() {
body = body || {};
var authBody = {};
if (isSSH) {
authBody.keyFile = $('#projects-user-auth-key').val();
authBody.passphrase = $('#projects-user-auth-passphrase').val();
} else {
sendRequest(options,body);
notification.close();
authBody.username = $('#projects-user-auth-username').val();
authBody.password = $('#projects-user-auth-password').val();
}
var done = function(err) {
if (err) {
console.log(RED._("projects.send-req.update-failed"));
console.log(err);
} else {
sendRequest(options,body);
notification.close();
}
}
sendRequest({
url: "projects/"+activeProject.name+"/remotes/"+(xhr.responseJSON.remote||options.remote||'origin'),
type: "PUT",
responses: {
0: function(error) {
done(error,null);
},
200: function(data) {
done(null,data);
},
400: {
'unexpected_error': function(error) {
done(error,null);
}
},
}
},{auth:authBody});
sendRequest({
url: "projects/"+activeProject.name+"/remotes/"+(xhr.responseJSON.remote||options.remote||'origin'),
type: "PUT",
responses: {
0: function(error) {
done(error,null);
},
200: function(data) {
done(null,data);
},
400: {
'unexpected_error': function(error) {
done(error,null);
}
},
}
},{auth:authBody});
}
}
}
]
});
return;
]
});
return;
} else if (xhr.responseJSON.code === 'git_host_key_verification_failed') {
var message = $('<div>'+
'<div class="form-row">'+RED._("projects.send-req.host-key-verify-failed")+'</div>'+
'</div>');
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
}
]
});
return;
}
} else if (responses[xhr.responseJSON.code]) {
resultCallback = responses[xhr.responseJSON.code];
resultCallbackArgs = xhr.responseJSON;

View File

@@ -231,7 +231,8 @@ RED.sidebar.context = (function() {
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: data.format,
sourceId: id+"."+k,
tools: tools
tools: tools,
path: ""
}).appendTo(propRow.children()[1]);
}
})
@@ -275,7 +276,8 @@ RED.sidebar.context = (function() {
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: data.format,
sourceId: id+"."+k,
tools: tools
tools: tools,
path: ""
}).appendTo(propRow.children()[1]);
}
});
@@ -295,7 +297,8 @@ RED.sidebar.context = (function() {
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: v.format,
sourceId: id+"."+k,
tools: tools
tools: tools,
path: ""
}).appendTo(propRow.children()[1]);
if (contextStores.length > 1) {
$("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))

View File

@@ -15,17 +15,6 @@
**/
RED.sidebar.info = (function() {
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: true,
smartLists: true,
smartypants: false
});
var content;
var sections;
var propertiesSection;
@@ -350,7 +339,7 @@ RED.sidebar.info = (function() {
if (subflowNode && node.type !== "subflow") {
// Selected a subflow instance node.
// - The subflow template info goes into help
helpText = (marked(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
} else {
helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
}
@@ -362,10 +351,10 @@ RED.sidebar.info = (function() {
if (node._def && node._def.info) {
var info = node._def.info;
var textInfo = (typeof info === "function" ? info.call(node) : info);
infoText = infoText + marked(textInfo);
infoText = infoText + RED.utils.renderMarkdown(textInfo);
}
if (node.info) {
infoText = infoText + marked(node.info || "")
infoText = infoText + RED.utils.renderMarkdown(node.info || "")
}
setInfoText(infoText, infoSection.content);

View File

@@ -16,6 +16,28 @@
RED.utils = (function() {
window._marked = window.marked;
window.marked = function(txt) {
console.warn("Use of 'marked()' is deprecated. Use RED.utils.renderMarkdown() instead");
return renderMarkdown(txt);
}
_marked.setOptions({
renderer: new _marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
smartLists: true,
smartypants: false
});
function renderMarkdown(txt) {
var rendered = _marked(txt);
var cleaned = DOMPurify.sanitize(rendered, {SAFE_FOR_JQUERY: true})
return cleaned;
}
function formatString(str) {
return str.replace(/\r?\n/g,"&crarr;").replace(/\t/g,"&rarr;");
}
@@ -1053,6 +1075,7 @@ RED.utils = (function() {
decodeObject: decodeObject,
parseContextKey: parseContextKey,
createIconElement: createIconElement,
sanitize: sanitize
sanitize: sanitize,
renderMarkdown: renderMarkdown
}
})();

View File

@@ -4370,8 +4370,10 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
if (new_ms.length === 1) {
node = new_ms[0];
spliceActive = node.n.hasOwnProperty("_def") &&
node.n._def.inputs > 0 &&
node.n._def.outputs > 0;
((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) &&
((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0))
}
}
RED.keyboard.add("*","escape",function(){

View File

@@ -9,19 +9,15 @@
color: transparent !important;
}
}
.ace_gutter {
background: $text-editor-gutter-background;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.ace_scroller {
background: $text-editor-background;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.ace_scroller {
background: $text-editor-background;
color: $text-editor-color;
}
.ace_marker-layer .ace_active-line {
@@ -37,9 +33,6 @@
.ace_gutter-active-line {
background: $text-editor-gutter-active-line-background;
}
.ace_gutter {
background: $text-editor-gutter-background;
}
.ace_tooltip {
font-family: $primary-font;
line-height: 1.4em;

View File

@@ -32,6 +32,9 @@
right: 5px;
top: 9px;
}
form.red-ui-searchBox-form {
margin: 0;
}
input.red-ui-searchBox-input {
border-radius: 0;
border: none;

View File

@@ -112,7 +112,7 @@
position: absolute;
bottom: 0;
right:0;
zIndex: 101;
z-index: 101;
border-left: 1px solid $primary-border-color;
border-top: 1px solid $primary-border-color;
background: $view-navigator-background;
@@ -122,7 +122,7 @@
stroke-dasharray: 5,5;
pointer-events: none;
stroke: $secondary-border-color;
strokeWidth: 1;
stroke-width: 1;
fill: $view-background;
}

View File

@@ -11,6 +11,7 @@
var length = str.length;
var start = 0;
var inString = false;
var inRegex = false;
var inBox = false;
var quoteChar;
var list = [];
@@ -24,8 +25,13 @@
}
for (var i=0;i<length;i++) {
var c = str[i];
if (!inString) {
if (c === "'" || c === '"') {
if (!inString && !inRegex) {
if (c === "/") {
inRegex = true;
frame = {type:"regex",pos:i};
list.push(frame);
stack.push(frame);
} else if (c === "'" || c === '"') {
inString = true;
quoteChar = c;
frame = {type:"string",pos:i};
@@ -37,6 +43,9 @@
} else if (c === ",") {
frame = {type:",",pos:i};
list.push(frame);
} else if (c === "&") {
frame = {type:"&",pos:i};
list.push(frame);
} else if (/[\(\[\{]/.test(c)) {
frame = {type:"open-block",char:c,pos:i};
list.push(frame);
@@ -44,7 +53,8 @@
} else if (/[\}\)\]]/.test(c)) {
var oldFrame = stack.pop();
if (matchingBrackets[oldFrame.char] !== c) {
//console.log("Stack frame mismatch",c,"at",i,"expected",matchingBrackets[oldFrame.char],"from",oldFrame.pos);
// console.log("Stack frame mismatch",c,"at",i,"expected",matchingBrackets[oldFrame.char],"from",oldFrame.pos);
// console.log(list);
return str;
}
//console.log("Closing",c,"at",i,"compare",oldFrame.type,oldFrame.pos);
@@ -53,19 +63,32 @@
list.push(frame);
}
} else {
if (c === quoteChar) {
// Next char must be a ]
inString = false;
stack.pop();
if (c === "\\") {
// an escaped char - stay in current mode and skip the next char
i++;
}
if (inString) {
if (c === quoteChar) {
// Next char must be a ]
inString = false;
var f = stack.pop();
f.end = i;
}
} else if (inRegex) {
if (c === "/") {
inRegex = false;
var f = stack.pop();
f.end = i;
}
}
}
}
// console.log(stack);
// console.log("list",list);
var result = str;
var indent = 0;
var offset = 0;
var pre,post,indented;
var pre,post,indented,hasNewline;
var longStack = [];
list.forEach(function(f) {
if (f.type === ";" || f.type === ",") {
@@ -73,29 +96,51 @@
pre = result.substring(0,offset+f.pos+1);
post = result.substring(offset+f.pos+1);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
hasNewline = /\n$/.test(pre);
// console.log("A§"+pre+"§\n§"+indented+"§",hasNewline);
result = pre+(hasNewline?"":"\n")+indented;
offset += indented.length-post.length+(hasNewline?0:1);
}
} else if (f.type === "&") {
pre = result.substring(0,offset+f.pos+1);
var lastLineBreak = pre.lastIndexOf("\n");
var lineLength = pre.length - lastLineBreak;
if (lineLength > 70) {
post = result.substring(offset+f.pos+1);
if (!/^\n/.test(post)) {
indented = indentLine(post,indent);
hasNewline = /\n$/.test(pre);
result = pre+(hasNewline?"":"\n")+indented;
offset += indented.length-post.length+(hasNewline?0:1);
}
}
} else if (f.type === "open-block") {
if (f.width > 30) {
if (f.width > 40) {
longStack.push(true);
indent += 4;
pre = result.substring(0,offset+f.pos+1);
post = result.substring(offset+f.pos+1);
hasNewline = /\n$/.test(pre);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
result = pre+(hasNewline?"":"\n")+indented;
offset += indented.length-post.length+(hasNewline?0:1);
} else {
longStack.push(false);
}
} else if (f.type === "close-block") {
if (f.width > 30) {
if (f.width > 40) {
indent -= 4;
pre = result.substring(0,offset+f.pos);
post = result.substring(offset+f.pos);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
hasNewline = /\n *$/.test(pre);
if (hasNewline) {
result = pre + post;
} else {
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
}
}
longStack.pop();
}

View File

@@ -28,6 +28,11 @@ ace.define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/m
}, "identifier");
this.$rules = {
"start" : [
{
token: "string.regexp",
regex: "\\/",
next: "regex"
},
{
token : "string",
regex : "'(?=.)",
@@ -46,34 +51,35 @@ ace.define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/m
token : "constant.numeric", // float
regex : /[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
},
{ token: "keyword",
regex: /λ/
},
{
token: "keyword",
regex: jsonataFunctions
},
{
token : keywordMapper,
regex : "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*"
},
{
token : "punctuation.operator",
regex : /[.](?![.])/
},
{
token : "keyword.operator",
regex : /\|\||<=|>=|\.\.|\*\*|!=|:=|[=<>`!$%&*+\-~\/^]/,
next : "start"
},
{
token : "punctuation.operator",
regex : /[?:,;.]/,
next : "start"
},
{
token : "paren.lparen",
regex : /[\[({]/,
{
token: "keyword",
regex: /λ/
},
{
token: "keyword",
regex: jsonataFunctions
},
{
token : keywordMapper,
regex : "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*"
},
{
token : "punctuation.operator",
regex : /[.](?![.])/
},
{
token : "keyword.operator",
regex : /\|\||<=|>=|\.\.|\*\*|!=|:=|[=<>`!$%&*+\-~\/^]/,
next : "start"
},
{
token : "punctuation.operator",
regex : /[?:,;.]/,
next : "start"
},
{
token : "paren.lparen",
regex : /[\[({]/,
next : "start"
},
{
@@ -86,7 +92,8 @@ ace.define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/m
token : "string",
regex : '"|$',
next : "start"
}, {
},
{
defaultToken: "string"
}
],
@@ -95,9 +102,24 @@ ace.define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/m
token : "string",
regex : "'|$",
next : "start"
}, {
},
{
defaultToken: "string"
}
],
"regex" : [
{
token: "string.regexp",
regex: "\\\\/"
},
{
token: "string.regexp",
regex: "/[sxngimy]*",
next: "start"
},
{
defaultToken: "string.regexp"
}
]
};
};

File diff suppressed because one or more lines are too long

View File

@@ -457,7 +457,7 @@ RED.debug = (function() {
var metaRow = $('<div class="red-ui-debug-msg-meta"></div>').appendTo(msg);
$('<span class="red-ui-debug-msg-date">'+ getTimestamp()+'</span>').appendTo(metaRow);
if (sourceNode) {
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+sanitize(o.name||sourceNode.name||sourceNode.id))
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+(o.name||sourceNode.name||sourceNode.id))
.appendTo(metaRow)
.on("click", function(evt) {
evt.preventDefault();

View File

@@ -18,7 +18,7 @@
<div class="form-row">
<label data-i18n="trigger.send" for="node-input-op1"></label>
<input type="hidden" id="node-input-op1type">
<input style="width: 70%" type="text" id="node-input-op1" placeholder="1">
<input style="width:70%" type="text" id="node-input-op1" placeholder="1">
</div>
<div class="form-row">
<label data-i18n="trigger.then"></label>
@@ -40,12 +40,12 @@
</div>
<div class="form-row node-type-wait">
<label></label>
<input type="checkbox" id="node-input-extend" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
<input type="checkbox" id="node-input-extend" style="margin-left:0px; vertical-align:top; width:auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
</div>
<div class="form-row node-type-wait">
<label data-i18n="trigger.then-send"></label>
<input type="hidden" id="node-input-op2type">
<input style="width: 70%" type="text" id="node-input-op2" placeholder="0">
<input style="width:70%" type="text" id="node-input-op2" placeholder="0">
</div>
<div class="form-row">
<label data-i18n="trigger.label.reset" style="width:auto"></label>
@@ -109,9 +109,11 @@
$(".node-type-duration").hide();
}
else if ($(this).val() == "loop") {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").hide();
$(".node-type-duration").show();
} else {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").show();
$(".node-type-duration").show();
}
@@ -175,9 +177,7 @@
}
if ($("#node-then-type").val() == "loop") {
$("#node-input-duration").val($("#node-input-duration").val() * -1);
}
}
}
});
</script>

View File

@@ -76,6 +76,7 @@ module.exports = function(RED) {
var node = this;
node.topics = {};
var npay = {};
var pendingMessages = [];
var activeMessagePromise = null;
var processMessageQueue = function(msg) {
@@ -124,6 +125,7 @@ module.exports = function(RED) {
node.status({});
}
else {
if (node.op2type === "payl") { npay[topic] = RED.util.cloneMessage(msg); }
if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) {
promise = Promise.resolve();
if (node.op2type === "pay") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
@@ -188,7 +190,14 @@ module.exports = function(RED) {
});
}
promise.then(() => {
msg2.payload = node.topics[topic].m2;
if (node.op2type === "payl") {
node.send(npay[topic]);
delete npay[topic];
}
else {
msg2.payload = node.topics[topic].m2;
node.send(msg2);
}
delete node.topics[topic];
if (node.op2type === "payl") { node.send(npay); }
else { node.send(msg2); }

View File

@@ -145,7 +145,7 @@ module.exports = function(RED) {
if (error.signal) { msg3.payload.signal = error.signal; }
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
node.log('error:' + error);
if (RED.settings.verbose) { node.log('error:' + error); }
}
else if (node.oldrc === "false") {
msg3 = RED.util.cloneMessage(msg);

View File

@@ -153,7 +153,12 @@ module.exports = function(RED) {
this.brokerurl="mqtt://";
}
if (this.broker !== "") {
this.brokerurl = this.brokerurl+this.broker+":";
//Check for an IPv6 address
if (/(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)/.test(this.broker)) {
this.brokerurl = this.brokerurl+"["+this.broker+"]:";
} else {
this.brokerurl = this.brokerurl+this.broker+":";
}
// port now defaults to 1883 if unset.
if (!this.port){
this.brokerurl = this.brokerurl+"1883";
@@ -464,6 +469,7 @@ module.exports = function(RED) {
this.broker = n.broker;
this.brokerConn = RED.nodes.getNode(this.broker);
var node = this;
var chk = /[\+#]/;
if (this.brokerConn) {
this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
@@ -482,6 +488,7 @@ module.exports = function(RED) {
}
if ( msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
if (chk.test(msg.topic)) { node.warn(RED._("mqtt.errors.invalid-topic")); }
this.brokerConn.publish(msg, done); // send the message
} else {
node.warn(RED._("mqtt.errors.invalid-topic"));

View File

@@ -163,7 +163,7 @@
if (root === "") {
$("#node-config-ws-tip").hide();
} else {
$("#node-config-ws-path").html(root);
$("#node-config-ws-path").html(RED._("node-red:websocket.tip.path2", { path: root }));
$("#node-config-ws-tip").show();
}
}
@@ -235,7 +235,7 @@
</div>
<div class="form-tips">
<span data-i18n="[html]websocket.tip.path1"></span>
<p id="node-config-ws-tip"><span data-i18n="[html]websocket.tip.path2"></span><code><span id="node-config-ws-path"></span></code>.</p>
<p id="node-config-ws-tip"><span id="node-config-ws-path"></span></p>
</div>
</script>

View File

@@ -33,6 +33,10 @@
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-strings"><label style="width:auto; margin-top:7px;" for="node-input-strings"><span data-i18n="csv.label.usestrings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_empty_strings"><label style="width:auto; margin-top:7px;" for="node-input-include_empty_strings"><span data-i18n="csv.label.include_empty_strings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_null_values"><label style="width:auto; margin-top:7px;" for="node-input-include_null_values"><span data-i18n="csv.label.include_null_values"></span></label><br/>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
@@ -74,7 +78,9 @@
ret: {value:'\\n'},
temp: {value:""},
skip: {value:"0"},
strings: {value:true}
strings: {value:true},
include_empty_strings: {value:""},
include_null_values: {value:""}
},
inputs:1,
outputs:1,

View File

@@ -31,6 +31,8 @@ module.exports = function(RED) {
this.skip = parseInt(n.skip || 0);
this.store = [];
this.parsestrings = n.strings;
this.include_empty_strings = n.include_empty_strings || false;
this.include_null_values = n.include_null_values || false;
if (this.parsestrings === undefined) { this.parsestrings = true; }
var tmpwarn = true;
var node = this;
@@ -173,20 +175,29 @@ module.exports = function(RED) {
}
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
j += 1;
k[j] = "";
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
k[j] = line.length - 1 === i ? null : "";
}
else if ((line[i] === "\n") || (line[i] === "\r")) { // handle multiple lines
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
if (line[i-1] === node.sep) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
@@ -204,10 +215,13 @@ module.exports = function(RED) {
// Finished so finalize and send anything left
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array

View File

@@ -292,7 +292,6 @@ module.exports = function(RED) {
reduceMessageGroup(node,msgs,exp,fixup,count,result,done);
}
});
}
function reduceAndSendGroup(node, group, done) {
var is_right = node.reduce_right;
@@ -331,7 +330,7 @@ module.exports = function(RED) {
var pending_count = node.pending_count;
var gid = msg.parts.id;
var count;
if(!pending.hasOwnProperty(gid)) {
if (!pending.hasOwnProperty(gid)) {
if(parts.hasOwnProperty('count')) {
count = msg.parts.count;
}
@@ -361,7 +360,6 @@ module.exports = function(RED) {
}
return done();
}
if (msgs.length === group.count) {
delete pending[gid];
pending_count -= msgs.length;
@@ -408,7 +406,7 @@ module.exports = function(RED) {
if (this.joinerType === "str") {
this.joiner = this.joiner.replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
} else if (this.joinerType === "bin") {
var joinArray = JSON.parse(n.joiner)
var joinArray = JSON.parse(n.joiner || "[]");
if (Array.isArray(joinArray)) {
this.joiner = Buffer.from(joinArray);
} else {
@@ -429,7 +427,7 @@ module.exports = function(RED) {
var completeSend = function(partId) {
var group = inflight[partId];
clearTimeout(group.timeout);
if (group.timeout) { clearTimeout(group.timeout); }
if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; }
if (group.type === 'array' && group.arrayLen > 1) {
var newArray = [];
@@ -448,6 +446,9 @@ module.exports = function(RED) {
buffers.push(joinBuffer);
bufferLen += joinBuffer.length;
}
if (!Buffer.isBuffer(group.payload[i])) {
group.payload[i] = Buffer.from(group.payload[i]);
}
buffers.push(group.payload[i]);
bufferLen += group.payload[i].length;
}
@@ -629,7 +630,13 @@ module.exports = function(RED) {
var group = inflight[partId];
if (payloadType === 'buffer') {
if (property !== undefined) {
inflight[partId].bufferLen += property.length;
if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) {
inflight[partId].bufferLen += property.length;
}
else {
node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg);
return;
}
}
}
if (payloadType === 'object') {

View File

@@ -397,7 +397,7 @@
"message" : "gesamte Nachricht",
"tip" : {
"path1" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Listener kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt.",
"path2" : "Dieser Pfad ist relativ zu ",
"path2" : "Dieser Pfad ist relativ zu <code>__path__</code>.",
"url1" : "URL sollte ws: &#47; & #47; oder wss: &#47; & #47; Schema verwenden und auf einen vorhandenen Websocket-Listener verweisen.",
"url2" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Client kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt."
},

View File

@@ -23,13 +23,13 @@
<script type="text/html" data-help-name="websocket out">
<p>WebSocket Ausgabe-Node.</p>
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
Der Socket kann so konfiguriert werden, dass er das gesamte <code>msg</code> Objekt als JSON-String kodiert und über den WebSocket sendet.</p>
<p>Wenn die an diesem Node ankommende Nachricht an einem WebSocket-Eingangs-Node begann,
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
Andernfalls wird die Nachricht an alle verbundenen Clients gesendet..</p>
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
muss die Eigenschaft <code>msg._session</code> innerhalb des Flow gelöscht werden.</p>
</script>
@@ -37,30 +37,6 @@
<p>Dieser Konfigurations-Node erstellt einen WebSocket Server-Endpunkt unter Verwendung des angegebenen Pfades.</p>
</script>
<!-- WebSocket Client configuration node -->
<script type="text/html" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">
</div>
<div class="form-row node-config-row-tls hide">
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
<input type="text" id="node-config-input-tls">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false" data-i18n="websocket.payload"></option>
<option value="true" data-i18n="websocket.message"></option>
</select>
</div>
<div class="form-tips">
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
<span data-i18n="[html]websocket.tip.url2"></span>
</div>
</script>
<script type="text/html" data-help-name="websocket-client">
<p>Dieser Konfigurations-Node verbindet einen WebSocket-Client mit der angegebenen URL.</p>
</script>

View File

@@ -455,7 +455,7 @@
"message": "entire message",
"tip": {
"path1": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.",
"path2": "This path will be relative to ",
"path2": "This path will be relative to <code>__path__</code>.",
"url1": "URL should use ws:&#47;&#47; or wss:&#47;&#47; scheme and point to an existing websocket listener.",
"url2": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The client can be configured to send or receive the entire message object as a JSON formatted string."
},
@@ -698,7 +698,9 @@
"output": "Output",
"includerow": "include column name row",
"newline": "Newline",
"usestrings": "parse numerical values"
"usestrings": "parse numerical values",
"include_empty_strings": "include empty strings",
"include_null_values": "include null values"
},
"placeholder": {
"columns": "comma-separated column names"
@@ -894,7 +896,8 @@
"fixup": "Fix-up exp"
},
"errors": {
"invalid-expr": "Invalid JSONata expression: __error__"
"invalid-expr": "Invalid JSONata expression: __error__",
"invalid-type": "Cannot join __error__ to buffer"
}
},
"sort" : {

View File

@@ -41,5 +41,5 @@
wait a fixed timeout from first reply and then return, sit and wait for data, or send then close the connection
immediately, without waiting for a reply.</p>
<p>The response will be output in <code>msg.payload</code> as a buffer, so you may want to .toString() it.</p>
<p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties.</p>
<p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties in every message sent to the node.</p>
</script>

View File

@@ -39,6 +39,9 @@
will be used as the property names. Alternatively, the column names can be taken from the first row of the CSV.</p>
<p>When converting to CSV, the column template is used to identify which properties to extract from the object and in what order.</p>
<p>If the input is an array then the columns template is only used to optionally generate a row of column titles.</p>
<p>If 'parse numerical values' option is checked, string numerical values will be returned as numbers, ie. middle value '1,"1.5",2'.</p>
<p>If 'include empty strings' option is checked, empty strings will be returned in result, ie. middle value '"1","",3'.</p>
<p>If 'include null values' option is checked, null values will be returned in result, ie. middle value '"1",,3'.</p>
<p>The node can accept a multi-part input as long as the <code>parts</code> property is set correctly.</p>
<p>If outputting multiple messages they will have their <code>parts</code> property set and form a complete message sequence.</p>
<p><b>Note:</b> the column template must be comma separated - even if a different separator is chosen for the data.</p>

View File

@@ -91,7 +91,8 @@
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>If set, the node will send its output message in its current state.</dd>
<dd>If set, the node will append the payload, and then send the output message in its current state.
If you don't wish to append the payload, delete it from the msg.</dd>
</dl>
<h3>Details</h3>

View File

@@ -49,12 +49,6 @@
<dd>The contents of the file as either a string or binary buffer.</dd>
<dt class="optional">filename <span class="property-type">string</span></dt>
<dd>If not configured in the node, this optional property sets the name of the file to be read.</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>deprecated</i>: If enabled in the node, when the node hits an error
reading the file, it will send a message with no <code>payload</code>
and this <code>error</code> property set to the error details. This
mode of behaviour is deprecated and not enabled by default for new
instances of the node. See below for more information.</dd>
</dl>
<h3>Details</h3>
<p>The filename should be an absolute path, otherwise it will be relative to
@@ -65,11 +59,5 @@
<p>When split into multiple messages, each message will have a <code>parts</code>
property set, forming a complete message sequence.</p>
<p>Encoding of input data can be specified from list of encodings if output format is string.</p>
<h4>Legacy error handling</h4>
<p>Before Node-RED 0.17, if this node hit an error whilst reading the file, it would
send a message with no <code>msg.payload</code> and <code>msg.error</code> set to the
details of the error. This is a deprecated mode of behaviour for the node that new
instances will not do. If required, this mode can be re-enabled within the node
configuration.</p>
<p>Errors should be caught and handled using a Catch node.</p>
</script>

View File

@@ -455,7 +455,7 @@
"message": "メッセージ全体を送信/受信",
"tip": {
"path1": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。",
"path2": "This path will be relative to ",
"path2": "このパスは <code>__path__</code> の相対パスになります。",
"url1": "URLには ws:&#47;&#47; または wss:&#47;&#47; スキーマを使用して、存在するwebsocketリスナを設定してください。",
"url2": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。"
},
@@ -892,7 +892,8 @@
"fixup": "最終調整式"
},
"errors": {
"invalid-expr": "JSONata式が不正: __error__"
"invalid-expr": "JSONata式が不正: __error__",
"invalid-type": "__error__ をバッファに連結できません"
}
},
"sort": {

View File

@@ -79,7 +79,7 @@
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>設定されている場合保持しているメッセージを結合して送信します</dd>
<dd>設定されている場合本ノードはペイロードを追加し保持しているメッセージを送信しますペイロードを追加したくない場合はmsgから削除してください</dd>
</dl>
<h3>詳細</h3>

View File

@@ -44,8 +44,6 @@
<dd>ファイルの内容を文字列もしくはバッファで表現します</dd>
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>読み出し対象のファイル名をノードに設定していない場合このプロパティでファイルを指定します</dd>
<dt class="optional">error <span class="property-type">オブジェクト</span></dt>
<dd><i>非推奨</i>: <code>payload</code><code>error</code></dd>
</dl>
<h3>詳細</h3>
<p>ファイルネームは絶対パスでの指定を推奨します絶対パスを指定しない場合はNode-REDプロセスのワーキングディレクトリからの相対パスとして扱います</p>
@@ -53,7 +51,5 @@
<p>テキストファイルの場合行毎に分割して各々メッセージを送信することができますまたバイナリファイルの場合小さな塊のバッファに分割して送信できますバッファの分割単位はオペレーティングシステム依存ですが一般に64k(Linux/Mac)もしくは41k(Windows)です</p>
<p>複数のメッセージに分割する場合各メッセージには<code>parts</code></p>
<p>出力形式が文字列の場合入力データのエンコーディングをエンコーディングリストから選択できます</p>
<h4>旧式のエラー処理</h4>
<p>Node-RED 0.17より前の版ではファイルの読み込み時にエラーが発生すると<code>payload</code><code>error</code></p>
<p>エラーはcatchードで補足して処理することを推奨します</p>
</script>

View File

@@ -446,7 +446,7 @@
"message": "메세지 전체를 송신/수신",
"tip": {
"path1": "표준으로는 <code>payload</code> 가 websocket에서 송신, 수신된 데이터를 기다립니다. 클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다.",
"path2": "This path will be relative to ",
"path2": "This path will be relative to <code>__path__</code>.",
"url1": "URL에는 ws:&#47;&#47; 또는 wss:&#47;&#47; 스키마를 사용하여, 존재하는 websocket리스너를 설정해 주세요.",
"url2": "표준으로는 <code>payload</code> 가 websocket에서 송신,수신될 데이터를 기다립니다.클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다."
},

View File

@@ -455,7 +455,7 @@
"message": "完整信息",
"tip": {
"path1": "默认情况下,<code>payload</code>将包含要发送或从Websocket接收的数据。侦听器可以配置为以JSON格式的字符串发送或接收整个消息对象.",
"path2": "这条路径将相对于 ",
"path2": "这条路径将相对于 <code>__path__</code>.",
"url1": "URL 应该使用ws:&#47;&#47;或者wss:&#47;&#47;方案并指向现有的websocket侦听器.",
"url2": "默认情况下,<code>payload</code> 将包含要发送或从Websocket接收的数据。可以将客户端配置为以JSON格式的字符串发送或接收整个消息对象."
},
@@ -698,7 +698,7 @@
"output": "输出",
"includerow": "包含列名行",
"newline": "换行符",
"usestrings": "parse numerical values"
"usestrings": "解析数值"
},
"placeholder": {
"columns": "用逗号分割列名"
@@ -898,7 +898,7 @@
}
},
"sort" : {
"sort": "sort",
"sort": "排序",
"target" : "排序属性",
"seq" : "信息队列",
"key" : "键值",
@@ -907,9 +907,9 @@
"ascending" : "升序",
"descending" : "降序",
"as-number" : "作为数值",
"invalid-exp" : "sort节点中存在无效的JSONata表达式",
"too-many" : "sort节点中有太多待定信息",
"clear" : "清空sort节点中的待定信息"
"invalid-exp" : "排序节点中存在无效的JSONata表达式",
"too-many" : "排序节点中有太多待定信息",
"clear" : "清空排序节点中的待定信息"
},
"batch" : {
"batch": "batch",

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p><code>msg.payload</code></p>
<h3>输入</h3>
<dl class="message-properties">
@@ -31,7 +31,7 @@
<p>您可以将此节点配置为删除文件</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>以字符串或二进制缓冲区的形式读取文件的内容</p>
<h3>输入</h3>
<dl class="message-properties">
@@ -44,8 +44,6 @@
<dd>文件的内容可以是字符串也可以是二进制的buffer</dd>
<dt class="optional">filename <span class="property-type">字符串</span></dt>
<dd>如果未在节点配置中设置该属性可以选择要读取的文件名</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>已不推荐使用</i>: <code></code><code>error</code> </dd>
</dl>
<h3>详细</h3>
<p>文件名应该是绝对路径否则将相对于Node-RED进程的工作目录</p>
@@ -53,7 +51,5 @@
<p>可以选择将文本文件拆分为几行每行输出一条消息或者将二进制文件拆分为较小的buffer块-块大小取决于操作系统但通常为64kLinux/Mac或41kWindows</p>
<p>当拆分为多条消息时每条消息将具有<code>parts</code></p>
<p>如果输出格式为字符串则可以从编码列表中指定输入数据的编码</p>
<h4>旧版的错误处理</h4>
<p>在Node-RED 0.17之前如果此节点在读取文件时遇到错误它将发送一条不包含<code>msg.payload</code><code>msg.error</code><code>msg.error</code></p>
<p>应该使用Catch节点来捕获并处理错误</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/x-red" data-help-name="inject">
<p>手動或定期得將消息注入流程中消息的有效荷載可以為多種類型包括字符串JavaScript對象或當前時間</p>
<h3>輸出</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">various</span></dt>
<dd>指定的消息的有效荷載</dd>
<dt class="optional">topic <span class="property-type">字符串</span></dt>
<dd>可以在節點中配置的可選屬性</dd>
</dl>
<h3>詳細</h3>
<p>通過使用特定的有效荷載注入節點可以啟動流程默認有效荷載是當前時間的時間戳以毫秒為單位自1970年1月1日起</p>
<p>該節點還支持注入字符串數字布林值JavaScript對象或流程/全局上下文值</p>
<p>默認情況下節點可以通過在編輯器中單擊節點按鈕來手動觸發同時也可以被設置為定期或按計劃注入</p>
<p>另一個可選的設置是在每次啟動流程時注入一次</p>
<p>可以指定的最大<i>間隔</i>596/24 使scheduler</p>
<p><b>注意</b>:選項<i>“時間間隔” </i><i>特定時間 </i>使用了標準cron系統。這意味著因此“20分鐘”並不表示在此之後20分鐘而是每小時的20分鐘40分鐘。如果您希望設定為從現在開始的每20分鐘那麽請使用<i>“間隔” </i>選項</p>
<p><b>注意</b>: 使</p>
</script>

View File

@@ -0,0 +1,25 @@
<!--
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/x-red" data-help-name="debug">
<p>調試側邊欄選項卡和運行時日志中顯示選定的消息屬性 默認情況下它會顯示<code>msg.payload</code>JSONata</p>
<h3>詳細</h3>
<p>調試側邊欄會提供已發消息的結構化視圖方便您查詢消息的結構</p>
<p>JavaScript對象和數組可以根據需要來折疊或擴展緩衝區對象可以顯示爲原始數據也可以顯示爲字符串</p>
<p>對任意條消息調試側邊欄還會顯示接收消息的時間發送消息的節點以及消息類型等信息單擊源節點ID將在工作區中顯示該節點</p>
<p>節點上的按鈕可用于啓用或禁用其輸出建議禁用或刪除所有未使用的調試節點</p>
<p>還可以通過配置節點將所有消息發送到運行時的日志或將簡短的數據32個字符內在調試節點下的狀態文本上顯示</p>
</script>

View File

@@ -0,0 +1,24 @@
<!--
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/x-red" data-help-name="complete">
<p>當另一個節點完成對消息的處理時觸發流程</p>
<h3>詳細</h3>
<p>如果一個節點通知運行時它已完成消息的處理該節點可用于觸發第二個流程</p>
<p>這個節點可以與沒有輸出端口的節點一起使用例如在使用電子郵件發送節點來發送郵件後觸發一個流程</p>
<p>此節點只能被設置爲處理流程中某個所選節點的事件與Catch節點不同您不能指定所有節點模式並以流程中的所有節點爲目標</p>
<p>並非所有節點都會觸發此事件這取決于它們是否支持于Node-RED 1.0中引入的此功能</p>
</script>

View File

@@ -0,0 +1,36 @@
<!--
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/x-red" 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>引發錯誤的節點的ID</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>默認情況下該節點將捕獲同一標簽頁上任何節點抛出的錯誤或者它可以針對特定節點或配置爲僅捕獲另一個目標捕獲節點尚未捕獲的錯誤</p>
<p>當錯誤發生時所有匹配的catch節點都會收到錯誤消息</p>
<p>如果在子流程中發送了錯誤則該錯誤將由子流程中的任意捕獲節點處理如果子流程中不存在捕獲節點則那錯誤將被傳播到子流程實例所在的標簽頁</p>
<p>如果消息已經具有<code>error</code><code>error</code><code>_error</code></p>
</script>

View File

@@ -0,0 +1,33 @@
<!--
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/x-red" 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>報告狀態的節點的ID</dd>
<dt>status.source.name <span class="property-type">字符串</span></dt>
<dd>報告狀態的節點的名稱如果已設置</dd>
</dl>
<h3>詳細</h3>
<p>該節點不包含<code>有效荷載</code></p>
<p>默認情況下節點會獲取同一工作空間標簽頁上報告所有節點的狀態可以通過配置來設定目標節點</p>
</script>

View File

@@ -0,0 +1,31 @@
<!--
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/x-red" data-help-name="link in">
<p>在流程之間創建虛擬連線</p>
<h3>詳細</h3>
<p>該節點可以連接到任何標簽頁上存在的任何<code>link out</code></p>
<p>僅當選擇鏈接節點時才會顯示鏈接節點之間的鏈接如果有指向另一個選項卡的鏈接則顯示一個虛擬節點單擊該虛擬節點將帶您到相應的選項卡</p>
<p><b>注意</b></p>
</script>
<script type="text/x-red" data-help-name="link out">
<p>在流程之間創建虛擬連線</p>
<h3>詳細</h3>
<p>該節點可以連接到任何標簽頁上存在的任何<code>link in</code></p>
<p>僅當選擇鏈接節點時才會顯示鏈接節點之間的鏈接如果有指向另一個選項卡的鏈接則顯示一個虛擬節點單擊該虛擬節點將帶您到相應的選項卡</p>
<p><b>注意</b></p>
</script>

View File

@@ -0,0 +1,21 @@
<!--
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/x-red" data-help-name="comment">
<p>可用于向流程添加注釋的節點</p>
<h3>詳細</h3>
<p>編輯面板接受Markdown語法輸入的文本將在信息側面板中顯示</p>
</script>

View File

@@ -0,0 +1,24 @@
<!--
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/x-red" data-help-name="unknown">
<p>您安裝的Node-RED無法識別該節點的類型</p>
<h3>詳細</h3>
<p><i>如果在此狀態下部署節點其配置會被保存但是在安裝缺少的類型之前流程不會開始</i></p>
<p>使用<code> Menu-Manage Palette </code>使<b>npm install &lt;module&gt;</b>Node-Red</p>
<p>另一種可能是您已經安裝了此節點類型但是缺少必須的依賴項您應檢查Node-RED的啓動日志中是否有與缺少節點有關的錯誤消息</p>
<p>以上方法都不適用時您可以聯系該流程的作者以獲取缺少的節點類型的副本</p>
</script>

View File

@@ -0,0 +1,51 @@
<!--
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/x-red" data-help-name="function">
<p>定義對接收到的消息進行處理的JavaScript代碼函數的主體</p>
<p>輸入消息在名爲<code>msg</code>JavaScript</p>
<p>通常<code>msg</code><code>msg.payload</code></p>
<p>該函數一般會返回一個消息對象或多個消息對象但也可以爲了停止流程而什麽都不返回</p>
<h3>詳細</h3>
<p>請參見<a target="_blank" href="http://nodered.org/docs/writing-functions.html">在線文檔</a></p>
<h4>傳送消息</h4>
<p>要將消息傳遞到流程中的下一個節點請返回消息或調用<code>node.send(messages)</code></p>
<p>它將返回/send:</p>
<ul>
<li>單個消息對象 - 傳遞給連接到第一個輸出的節點</li>
<li>消息對象數組傳遞給連接到相應輸出的節點</li>
</ul>
<p>如果數組元素是數組則將多個消息發送到相應的輸出</p>
<p>無論return方法是單個值還是數組元素如果返回值爲null則不會發送任何消息</p>
<h4>日志輸出和錯誤處理</h4>
<p>使用以下功能輸出日志信息和輸出錯誤</p>
<ul>
<li><code>node.log("Log message")</code></li>
<li><code>node.warn("Warning")</code></li>
<li><code>node.error("Error")</code></li>
</ul>
</p>
<p>使用catch節點可以進行錯誤處理 要由catch節點處理請將<code>msg</code><code>node.error</code></p>
<pre>node.error("Error",msg);</pre>
<h4>訪問節點信息</h4>
<p>您可以使用以下屬性來在代碼中引用節點ID和名稱</p>
<ul>
<li><code>node.id</code> - ID</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,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/x-red" data-help-name="switch">
<p>按屬性值來分配消息的傳送路線</p>
<h3>詳細</h3>
<p>根據接收到的消息評估指定的規則然後將消息發送到與匹配的規則相對應的輸出端口</p>
<p>可以將節點設置爲一旦發現一個匹配的規則則停止後續的匹配</p>
<p>對于評估規則可以使用消息屬性流程上下文/全局上下文屬性環境變量和JSONata表達式的評估結果</p>
<h4>規則</h4>
<p>有四種規則</p>
<ol>
<li><b></b></li>
<li><b>順序</b></li>
<li><b>JSONata表達式</b></li>
<li><b>其他</b></li>
</ol>
<h4>注釋</h4>
<p><code>is true/false</code><code>is null</code> </p>
<p><code>is empty</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,33 @@
<!--
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/x-red" data-help-name="change">
<p>設置更改刪除或移動消息流程上下文或全局上下文的屬性</p>
<p>如果指定了多個規則則將按定義的順序來應用它們</p>
<h3>詳細</h3>
<p>可用的操作有</p>
<dl class="message-properties">
<dt>設置</dt>
<dd>設置一個屬性該值可以是多種不同類型也可以從現有消息或上下文屬性中獲取</dd>
<dt>置換</dt>
<dd>搜索並替換屬性 如果啓用了正則表達式則可以爲replace with屬性指定捕獲組例如<code>$1</code> </dd>
<dt>刪除</dt>
<dd>刪除一個屬性</dd>
<dt>移動</dt>
<dd>移動或者重命名一個屬性</dd>
</dl>
<p>類型"expression"使用<a href="http://jsonata.org/" target="_new">JSONata</a></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/x-red" 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>scale</center></td><td><center>12</center></td><td><center>120</center></td></tr>
<tr><td><center>limit</center></td><td><center>12</center></td><td><center>100</center></td></tr>
<tr><td><center>wrap</center></td><td><center>12</center></td><td><center>20</center></td></tr>
</table>
</script>

View File

@@ -0,0 +1,46 @@
<!--
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/x-red" data-help-name="template">
<p>根據提供的模板設置屬性</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">object</span></dt>
<dd>一個msg對象其中包含著用于填充模板的信息</dd>
<dt class="optional">template <span class="property-type">string</span></dt>
<dd><code>msg.payload</code>msg</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">object</span></dt>
<dd>由來自傳入msg的屬性來填充已配置的模板後輸出的帶有屬性的msg</dd>
</dl>
<h3>詳細</h3>
<p>默認情況下使用<i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>格式如有需要也可以切換其他格式</p>
<p>例如:
<pre>Hello {{payload.name}}. Today is {{date}}</pre>
<p>receives a message containing:
<pre>{
date: "Monday",
payload: {
name: "Fred"
}
}</pre>
<p>輸出的消息將會是:
<pre>Hello Fred. Today is Monday</pre>
<p>也可以使用流程上下文或全局上下文中的屬性<code>{{flow.name}}</code><code>{{global.name}}</code><code>store</code>使<code>{{flow[store].name}}</code><code>{{global[store].name}}</code>
<p><b>注意</b>默認情況下,<i>mustache</i>將在其替換的值中轉義任何非字母數字或HTML實體爲了防止這種情況請使用<code>{{{triple}}}</code>
</script>

View File

@@ -0,0 +1,32 @@
<!--
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/x-red" 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,33 @@
<!--
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/x-red" data-help-name="trigger">
<p>触发后将会发送一条消息如果被拓展或重置则可以选择发送第二条消息</p>
<h3>输入</h3>
<dl class="message-properties">
<dt class="optional">reset</dt>
<dd>如果收到带有此属性的消息则将清除当前正在进行的任何超时或重复且不会触发任何消息</dd>
</dl>
<h3>详细</h3>
<p>该节点可用于在流程中创建一个超时 默认情况下当它收到一条消息时它将发送一条带有<code>1</code>250<code>0</code>使Raspberry Pi GPIOLED</p>
<p>可以将发送的每个消息的有效荷载配置为各种值包括不发送任何内容的选项例如将初始消息设置为<i>nothing</i></p>
<p>如果设置为<i>字符串</i>类型,则该节点支持<i>mustache</i>模板语法</p>
<p>如果节点收到具有<code>reset</code><code></code></p>
<p>可以将节点配置为以固定的时间间隔重新发送消息直到被收到的消息重置为止</p>
<p>可选可以将节点配置为将带有<code>msg.topic</code></p>
</script>

View File

@@ -0,0 +1,74 @@
<!--
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/x-red" 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節點進程的kill信號類型</dd>
<dt class="optional">pid <span class="property-type">數值|字符串</span></dt>
<dd>要殺死的現有exec節點進程的進程ID</dd>
</dl>
<h3>輸出</h3>
<ol class="node-ports">
<li>標准輸出(stdout)
<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">object</span></dt>
<dd>僅執行模式一個返回代碼對象的副本在端口3上也可用</dd>
</dl>
</li>
<li>標准error輸出(stderr)
<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">object</span></dt>
<dd>僅執行模式一個返回代碼對象的副本在端口3上也可用</dd>
</dl>
</li>
<li>返回代碼
<dl class="message-properties">
<dt>payload <span class="property-type">object</span></dt>
<dd>一個包含返回代碼以及<code>message</code><code>signal</code></dd>
</dl>
</li>
</ol>
<h3>詳細</h3>
<p>默認情況下使用<code>exec</code>調調<code>{code0}</code></p>
<p>可選可以選擇使用<code>spawn</code>stdoutstderr<code>{code0}</code></p>
<p>錯誤可能會在第三個端口<code>msg.payload</code><code>message</code><code>signal</code></p>
<p>運行的命令是在節點內定義的帶有附加<code>msg.payload</code></p>
<p>帶空格的命令或參數應該用引號引起來<code>這是一個參數</code></p>
<p>返回的<code>有效荷載</code>通常是<i>字符串</i>類型除非檢測到非UTF8字符在這種情況下它會是<i>buffer</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

@@ -6,7 +6,9 @@
"name": "名稱",
"username": "使用者名稱",
"password": "密碼",
"property": "屬性"
"property": "屬性",
"selectNodes": "選擇節點...",
"expand": "擴展"
},
"status": {
"connected": "已連接",
@@ -35,7 +37,22 @@
"stopped": "停止",
"failed": "注入失敗: __error__",
"label": {
"repeat": "重複"
"repeat": "重複",
"flow": "流上下午",
"global": "全局上下文",
"str": "字符串",
"num": "數值",
"bool": "布爾值",
"json": "JSON對象",
"bin": "buffer",
"date": "時間戳",
"env": "環境變量",
"object": "對象",
"string": "字符串",
"boolean": "布爾值",
"number": "數值",
"Array": "數組",
"invalid": "無效的JSON對象"
},
"timestamp": "時間戳記",
"none": "無",
@@ -72,13 +89,11 @@
"catch": {
"catch": "監測所有節點",
"catchNodes": "監測__number__個節點",
"catchUncaught": "捕獲:未捕獲",
"label": {
"source": "監測範圍",
"node": "節點",
"type": "類型",
"selectAll": "全選",
"sortByLabel": "按名稱排序",
"sortByType": "按類型排序"
"uncaught": "忽略其他捕獲節點處理的錯誤"
},
"scope": {
"all": "所有節點",
@@ -90,10 +105,6 @@
"statusNodes": "報告__number__個節點狀態",
"label": {
"source": "報告狀態範圍",
"node": "節點",
"type": "類型",
"selectAll": "全選",
"sortByLabel": "按名稱排序",
"sortByType": "按類型排序"
},
"scope": {
@@ -101,8 +112,13 @@
"selected": "指定節點"
}
},
"complete": {
"completeNodes": "完成: __number__個節點"
},
"debug": {
"output": "輸出",
"none": "None",
"invalid-exp": "無效的JSONata表達式: __error__",
"msgprop": "資訊屬性",
"msgobj": "完整資訊",
"to": "目標",
@@ -124,7 +140,11 @@
"filterCurrent": "當前流程",
"debugNodes": "除錯節點",
"clearLog": "清空日誌",
"openWindow": "在新視窗打開"
"filterLog": "過濾日誌",
"openWindow": "在新視窗打開",
"copyPath": "復制路徑",
"copyPayload": "復制值",
"pinPath": "固定展開"
},
"messageMenu": {
"collapseAll": "折疊所有路徑",
@@ -146,26 +166,33 @@
"key": "私密金鑰",
"passphrase": "密碼",
"ca": "CA證書",
"verify-server-cert":"驗證伺服器憑證"
"verify-server-cert": "驗證伺服器憑證",
"servername": "服務器名"
},
"placeholder": {
"cert":"憑證路徑 (PEM 格式)",
"key":"私密金鑰路徑 (PEM 格式)",
"ca":"CA憑證路徑 (PEM 格式)",
"passphrase":"私密金鑰密碼 (可選)"
"cert": "憑證路徑 (PEM 格式)",
"key": "私密金鑰路徑 (PEM 格式)",
"ca": "CA憑證路徑 (PEM 格式)",
"passphrase": "私密金鑰密碼 (可選)",
"servername": "用於SNI"
},
"error": {
"missing-file": "未提供證書/金鑰檔案"
}
},
"exec": {
"exec": "exec",
"spawn": "spawn",
"label": {
"command": "命令",
"append": "追加",
"timeout": "超時",
"timeoutplace": "可選填",
"return": "輸出",
"seconds": "秒"
"seconds": "秒",
"stdout": "標準輸出",
"stderr": "標準錯誤輸出",
"retcode": "返回碼"
},
"placeholder": {
"extraparams": "額外的輸入參數"
@@ -177,16 +204,18 @@
"oldrc": "使用舊式輸出 (相容模式)"
},
"function": {
"function": "函數",
"label": {
"function": "函數",
"outputs": "輸出"
},
"error": {
"inputListener":"無法在函數中監聽對'注入'事件",
"non-message-returned":"函數節點嘗試返回類型為 __type__ 的資訊"
"inputListener": "無法在函數中監聽對'注入'事件",
"non-message-returned": "函數節點嘗試返回類型為 __type__ 的資訊"
}
},
"template": {
"template": "模板",
"label": {
"template": "模版",
"property": "屬性",
@@ -233,21 +262,21 @@
"limit": "限制",
"limitTopic": "限制主題",
"random": "隨機",
"units" : {
"units": {
"second": {
"plural" : "秒",
"plural": "秒",
"singular": "秒"
},
"minute": {
"plural" : "分鐘",
"plural": "分鐘",
"singular": "分鐘"
},
"hour": {
"plural" : "小時",
"plural": "小時",
"singular": "小時"
},
"day": {
"plural" : "天",
"plural": "天",
"singular": "天"
}
}
@@ -272,6 +301,9 @@
"wait-reset": "等待被重置",
"wait-for": "等待",
"wait-loop": "週期性重發",
"for": "處理",
"bytopics": "每個msg.topic",
"alltopics": "所有消息",
"duration": {
"ms": "毫秒",
"s": "秒",
@@ -284,12 +316,13 @@
"trigger-block": "觸發並阻止",
"trigger-loop": "週期性重發",
"reset": "重置觸發節點條件 如果:",
"resetMessage":"msg.reset已設置",
"resetPayload":"msg.payload等於",
"resetMessage": "msg.reset已設置",
"resetPayload": "msg.payload等於",
"resetprompt": "可選填"
}
},
"comment": {
"comment": "注釋"
},
"unknown": {
"label": {
@@ -303,26 +336,32 @@
"example": "e.g. localhost",
"output": "輸出",
"qos": "QoS",
"retain": "保持",
"clientid": "使用者端ID",
"port": "埠",
"keepalive": "Keepalive計時(秒)",
"cleansession": "使用新的會話",
"use-tls": "使用安全連接 (SSL/TLS)",
"tls-config":"TLS 設置",
"verify-server-cert":"驗證伺服器憑證",
"tls-config": "TLS 設置",
"verify-server-cert": "驗證伺服器憑證",
"compatmode": "使用舊式MQTT 3.1支援"
},
"sections-label": {
"birth-message": "連接時發送的消息(出生消息)",
"will-message": "意外斷開連接時的發送消息Will消息",
"close-message": "斷開連接前發送的消息(關閉消息)"
},
"tabs-label": {
"connection": "連接",
"security": "安全",
"will": "Will信息",
"birth": "Birth信息"
"messages": "消息"
},
"placeholder": {
"clientid": "留白則自動隨機生成",
"clientid-nonclean":"如非新會話必須設置使用者端ID",
"clientid-nonclean": "如非新會話必須設置使用者端ID",
"will-topic": "留白將禁止Will資訊",
"birth-topic": "留白將禁止Birth資訊"
"birth-topic": "留白將禁止Birth資訊",
"close-topic": "留白以禁用關閉消息"
},
"state": {
"connected": "已連接到服務端: __broker__",
@@ -333,7 +372,9 @@
"output": {
"buffer": "Buffer",
"string": "字串",
"base64": "Base64編碼字串"
"base64": "Base64編碼字串",
"auto": "自動檢測 (字符串或buffer)",
"json": "解析的JSON對象"
},
"true": "是",
"false": "否",
@@ -342,7 +383,9 @@
"not-defined": "主題未設置",
"missing-config": "未設置服務端",
"invalid-topic": "主題無效",
"nonclean-missingclientid": "使用者端ID未設定使用新會話"
"nonclean-missingclientid": "使用者端ID未設定使用新會話",
"invalid-json-string": "無效的JSON字符串",
"invalid-json-parse": "無法解析JSON字符串"
}
},
"httpin": {
@@ -354,12 +397,26 @@
"upload": "接受檔案上傳?",
"status": "狀態碼",
"headers": "Header",
"other": "其他"
"other": "其他",
"paytoqs": "將msg.payload附加為查詢字符串參數",
"utf8String": "UTF8格式的字符串",
"binaryBuffer": "二進制buffer",
"jsonObject": "解析的JSON對象",
"authType": "類型",
"bearerToken": "Token"
},
"setby": "- 用 msg.method 設定 -",
"basicauth": "基本認證",
"use-tls": "使用安全連接 (SSL/TLS) ",
"tls-config":"TLS 設置",
"tls-config": "TLS 設置",
"basic": "基本認證",
"digest": "摘要認證",
"bearer": "bearer認證",
"use-proxy": "使用代理服務器",
"persist": "對連接啟用keep-alive",
"proxy-config": "代理服務器設置",
"use-proxyauth": "使用代理身份驗證",
"noproxy-hosts": "代理例外",
"utf8": "UTF-8 字串",
"binary": "二進位資料",
"json": "JSON對象",
@@ -375,8 +432,11 @@
"no-response": "無響應物件",
"json-error": "JSON 解析錯誤",
"no-url": "未設定 URL",
"deprecated-call":"__method__方法已棄用",
"invalid-transport":"非HTTP傳輸請求"
"deprecated-call": "__method__方法已棄用",
"invalid-transport": "非HTTP傳輸請求",
"timeout-isnan": "超時值不是有效數字,忽略",
"timeout-isnegative": "超時值為負,忽略",
"invalid-payload": "無效的有效載荷"
},
"status": {
"requesting": "請求中"
@@ -395,17 +455,23 @@
"message": "完整資訊",
"tip": {
"path1": "預設情況下,<code>payload</code>將包含要發送或從Websocket接收的資料。偵聽器可以配置為以JSON格式的字串發送或接收整個消息物件.",
"path2": "這條路徑將相對於 ",
"path2": "這條路徑將相對於 <code>__path__</code>.",
"url1": "URL 應該使用ws:&#47;&#47;或者wss:&#47;&#47;方案並指向現有的websocket監聽器.",
"url2": "預設情況下,<code>payload</code> 將包含要發送或從Websocket接收的資料。可以將使用者端配置為以JSON格式的字串發送或接收整個消息物件."
},
"status": {
"connected": "連接數 __count__",
"connected_plural": "連接數 __count__"
},
"errors": {
"connect-error": "ws連接發生了錯誤: ",
"send-error": "發送時發生了錯誤: ",
"missing-conf": "未設置伺服器"
"missing-conf": "未設置伺服器",
"duplicate-path": "同一路徑上不能有兩個WebSocket偵聽器: __path__"
}
},
"watch": {
"watch": "watch",
"label": {
"files": "文件",
"recursive": "遞迴所有子資料夾"
@@ -458,14 +524,12 @@
"connection-closed": "連接已關閉 __host__:__port__",
"connections": "__count__ 個連接",
"connections_plural": "__count__ 個連接"
},
"errors": {
"connection-lost": "連接中斷 __host__:__port__",
"timeout": "超時關閉通訊端連接,埠 __port__",
"cannot-listen": "無法監聽埠 __port__, 錯誤: __error__",
"error": "錯誤: __error__",
"socket-error": "通訊端連接錯誤來自 __host__:__port__",
"no-host": "主機位址或埠未設定",
"connect-timeout": "連接逾時",
@@ -480,14 +544,15 @@
"output": "輸出",
"group": "組",
"interface": "本地IP",
"interfaceprompt": "(可選)本地 IP 綁定到",
"send": "發送一個",
"toport": "到埠",
"address": "地址",
"decode-base64": "是否解碼Base64編碼的資訊?"
"decode-base64": "是否解碼Base64編碼的資訊?",
"interfaceprompt": "(可選)本地 IP 綁定到"
},
"placeholder": {
"interface": "可選eth0的IP地址",
"interfaceprompt": "(可選) 要綁定的本地接口或地址",
"address": "目標IP位址"
},
"udpmsgs": "udp信息",
@@ -529,36 +594,43 @@
"ip-notset": "udp: IP地址未設定",
"port-notset": "udp: 埠未設定",
"port-invalid": "udp: 無效埠號碼",
"alreadyused": "udp: 埠已被佔用"
"alreadyused": "udp: 埠已被佔用",
"ifnotfound": "udp: 接口 __iface__ 未發現"
}
},
"switch": {
"switch": "switch",
"label": {
"property": "屬性",
"rule": "規則",
"repair" : "重建資訊佇列"
"repair": "重建資訊佇列"
},
"previous": "先前值",
"and": "與",
"checkall": "全選所有規則",
"stopfirst": "接受第一條匹配資訊後停止",
"ignorecase": "忽略大小寫",
"rules": {
"btwn":"在之間",
"cont":"包含",
"regex":"匹配規則運算式",
"true":"為真",
"false":"為假",
"null":"為空",
"nnull":"非空",
"head":"head",
"tail":"tail",
"index":"index between",
"exp":"JSONata運算式",
"else":"除此以外"
"btwn": "在之間",
"cont": "包含",
"regex": "匹配規則運算式",
"true": "為真",
"false": "為假",
"null": "為空",
"nnull": "非空",
"istype": "類型是",
"empty": "為空",
"nempty": "非空",
"head": "head",
"tail": "tail",
"index": "index between",
"exp": "JSONata運算式",
"else": "除此以外",
"hask": "擁有鍵"
},
"errors": {
"invalid-expr": "無效的JSONata運算式: __error__",
"too-many" : "Switch節點中有太多待定信息"
"too-many": "Switch節點中有太多待定信息"
}
},
"change": {
@@ -588,6 +660,7 @@
}
},
"range": {
"range": "range",
"label": {
"action": "操作",
"inputrange": "映射輸入資料",
@@ -623,7 +696,8 @@
"firstrow": "第一行包含列名",
"output": "輸出",
"includerow": "包含列名行",
"newline": "分行符號"
"newline": "分行符號",
"usestrings": "解析數值"
},
"placeholder": {
"columns": "用逗號分割列名"
@@ -654,7 +728,8 @@
"html": {
"label": {
"select": "選取項",
"output": "輸出"
"output": "輸出",
"in": "in"
},
"output": {
"html": "選定元素的html內容",
@@ -670,7 +745,9 @@
"errors": {
"dropped-object": "忽略非物件格式的有效負載",
"dropped": "忽略不支援格式的有效負載類型",
"dropped-error": "轉換有效負載失敗"
"dropped-error": "轉換有效負載失敗",
"schema-error": "JSON架構錯誤",
"schema-error-compile": "JSON架構錯誤: 未能編譯架構"
},
"label": {
"o2j": "對象至JSON",
@@ -679,8 +756,8 @@
"property": "屬性",
"actions": {
"toggle": "JSON字串與物件互轉",
"str":"總是轉為JSON字串",
"obj":"總是轉為JS對象"
"str": "總是轉為JSON字串",
"obj": "總是轉為JS對象"
}
}
},
@@ -702,76 +779,6 @@
"xml_js": "此節點僅處理XML字串或JS物件."
}
},
"rpi-gpio": {
"label": {
"gpiopin": "GPIO",
"selectpin": "選擇引腳",
"resistor": "電阻?",
"readinitial": "在部署/重啟時讀取引腳的初始狀態?",
"type": "類型",
"initpin": "初始化引腳狀態?",
"debounce": "去抖動",
"freq": "頻率",
"button": "按鈕",
"pimouse": "Pi滑鼠",
"pikeyboard": "Pi鍵盤",
"left": "左",
"right": "右",
"middle": "中"
},
"resistor": {
"none": "無",
"pullup": "上拉電阻",
"pulldown": "下拉電阻"
},
"digout": "數位輸出",
"pwmout": "PWM輸出",
"servo": "伺服輸出",
"initpin0": "初始引腳電平 - 低(0)",
"initpin1": "初始引腳電平 - 高(1)",
"left": "左",
"right": "右",
"middle": "中",
"any": "任何",
"pinname": "引腳",
"alreadyuse": "已被使用",
"alreadyset": "已被設為",
"tip": {
"pin": "<b>正在使用引腳</b>: ",
"in": "提示: 僅接受數位輸入 - 輸出必須為0或1.",
"dig": "提示: 如用數位輸出 - 輸入必須為0或1.",
"pwm": "提示: 如用PWM輸出 - 輸入必須為0至100之間; 如用高頻率可能會比預期佔用更多CPU資源.",
"ser": "<b>提示</b>: 如用伺服輸出 - 輸入必須為0至100之間. 50為中間值."
},
"types": {
"digout": "數位輸出",
"input": "輸入",
"pullup": "含有上拉電阻的輸入",
"pulldown": "含有下拉電阻的輸入",
"pwmout": "PWM輸出",
"servo": "伺服輸出"
},
"status": {
"stopped": "已停止",
"closed": "已關閉",
"not-running": "不運行"
},
"errors": {
"ignorenode": "忽略樹莓派的特定節點",
"version": "版本命令失敗",
"sawpitype": "查看Pi類型",
"libnotfound": "找不到樹莓派RPi.GPIO的python庫",
"alreadyset": "GPIO引腳 __pin__ 已經被設定為類型: __type__",
"invalidpin": "無效GPIO引腳",
"invalidinput": "無效輸入",
"needtobeexecutable": "__command__須為可運行命令",
"mustbeexecutable": "nrgpio須為可運行",
"commandnotfound": "nrgpio命令不存在",
"commandnotexecutable": "nrgpio命令不可運行",
"error": "錯誤: __error__",
"pythoncommandnotfound": "nrpgio python命令未處於運行狀態"
}
},
"file": {
"label": {
"filename": "檔案名",
@@ -783,7 +790,10 @@
"breaklines": "分拆成行",
"filelabel": "文件",
"sendError": "發生錯誤時發送消息(傳統模式)",
"deletelabel": "刪除 __file__"
"deletelabel": "刪除 __file__",
"encoding": "編碼",
"utf8String": "UTF8字符串",
"binaryBuffer": "二進制buffer"
},
"action": {
"append": "追加至文件",
@@ -801,6 +811,21 @@
"deletedfile": "刪除檔: __file__",
"appendedfile": "追加至文件: __file__"
},
"encoding": {
"none": "默認",
"native": "Native",
"unicode": "Unicode",
"japanese": "日本",
"chinese": "中國",
"korean": "韓國",
"taiwan": "臺灣/香港",
"windows": "Windows代碼頁",
"iso": "ISO代碼頁",
"ibm": "IBM代碼頁",
"mac": "Mac代碼頁",
"koi8": "KOI8代碼頁",
"misc": "其它"
},
"errors": {
"nofilename": "未指定檔案名",
"invaliddelete": "警告:無效刪除。請在配置對話方塊中使用特定的刪除選項",
@@ -812,50 +837,53 @@
"tip": "提示: 檔案名應該是絕對路徑否則它將相對於Node-RED進程的工作目錄。"
},
"split": {
"intro":"基於以下類型拆分<code>msg.payload</code>:",
"object":"<b>對象</b>",
"objectSend":"每個鍵值對作為單個消息發送",
"strBuff":"<b>字串</b> / <b>Buffer</b>",
"array":"<b>陣列</b>",
"splitUsing":"拆分使用",
"splitLength":"固定長度",
"stream":"作為消息流處理",
"addname":" 複製鍵到 "
"split": "split",
"intro": "基於以下類型拆分<code>msg.payload</code>:",
"object": "<b>對象</b>",
"objectSend": "每個鍵值對作為單個消息發送",
"strBuff": "<b>字串</b> / <b>Buffer</b>",
"array": "<b>陣列</b>",
"splitUsing": "拆分使用",
"splitLength": "固定長度",
"stream": "作為消息流處理",
"addname": " 複製鍵到 "
},
"join":{
"mode":{
"mode":"模式",
"auto":"自動",
"merge":"合併序列",
"reduce":"縮減序列",
"custom":"手動"
"join": {
"join": "join",
"mode": {
"mode": "模式",
"auto": "自動",
"merge": "合併序列",
"reduce": "縮減序列",
"custom": "手動"
},
"combine":"合併每個",
"create":"輸出為",
"type":{
"string":"字串",
"array":"陣列",
"buffer":"Buffer",
"object":"鍵值對對象",
"merged":"合併對象"
"combine": "合併每個",
"completeMessage": "完整的消息",
"create": "輸出為",
"type": {
"string": "字串",
"array": "陣列",
"buffer": "Buffer",
"object": "鍵值對對象",
"merged": "合併對象"
},
"using":"使用此值",
"key":"作為鍵",
"joinedUsing":"合併符號",
"send":"發送資訊:",
"afterCount":"達到一定數量的資訊時",
"count":"數量",
"subsequent":"和每個後續的消息",
"afterTimeout":"第一條消息的若時間後",
"seconds":"秒",
"complete":"在收到存在<code>msg.complete</code>的消息後",
"tip":"此模式假定此節點與<i>split</i>相連, 或者接收到的消息有正確配置的<code>msg.parts</code>屬性.",
"too-many" : "join節點中有太多待定信息",
"using": "使用此值",
"key": "作為鍵",
"joinedUsing": "合併符號",
"send": "發送資訊:",
"afterCount": "達到一定數量的資訊時",
"count": "數量",
"subsequent": "和每個後續的消息",
"afterTimeout": "第一條消息的若時間後",
"seconds": "秒",
"complete": "在收到存在<code>msg.complete</code>的消息後",
"tip": "此模式假定此節點與<i>split</i>相連, 或者接收到的消息有正確配置的<code>msg.parts</code>屬性.",
"too-many": "join節點中有太多待定信息",
"merge": {
"topics-label":"合併主題",
"topics":"主題",
"topic" : "主題",
"on-change":"當收到一個新主題時發送已合併資訊"
"topics-label": "合併主題",
"topics": "主題",
"topic": "主題",
"on-change": "當收到一個新主題時發送已合併資訊"
},
"reduce": {
"exp": "Reduce運算式",
@@ -868,43 +896,45 @@
"invalid-expr": "無效的JSONata運算式: __error__"
}
},
"sort" : {
"target" : "排序屬性",
"seq" : "資訊佇列",
"key" : "鍵值",
"elem" : "元素值",
"order" : "順序",
"ascending" : "昇冪",
"descending" : "冪",
"as-number" : "作為數值",
"invalid-exp" : "sort節點中存在無效的JSONata運算式",
"too-many" : "sort節點中有太多待定信息",
"clear" : "清空sort節點中的待定資訊"
"sort": {
"sort": "排序",
"target": "排序屬性",
"seq": "資訊佇列",
"key": "值",
"elem": "元素值",
"order": "順序",
"ascending": "冪",
"descending": "降冪",
"as-number": "作為數值",
"invalid-exp": "排序節點中存在無效的JSONata運算式",
"too-many": "排序節點中有太多待定信息",
"clear": "清空排序節點中的待定資訊"
},
"batch" : {
"batch": {
"batch": "batch",
"mode": {
"label" : "模式",
"num-msgs" : "按指定數量分組",
"interval" : "按時間間隔分組",
"concat" : "按主題分組"
"label": "模式",
"num-msgs": "按指定數量分組",
"interval": "按時間間隔分組",
"concat": "按主題分組"
},
"count": {
"label" : "分組數量",
"overlap" : "隊末隊首重疊數量",
"count" : "數量",
"invalid" : "無效的分組數量或重疊數量"
"label": "分組數量",
"overlap": "隊末隊首重疊數量",
"count": "數量",
"invalid": "無效的分組數量或重疊數量"
},
"interval": {
"label" : "時間間隔",
"seconds" : "秒",
"empty" : "無數據到達時發送空資訊"
"label": "時間間隔",
"seconds": "秒",
"empty": "無數據到達時發送空資訊"
},
"concat": {
"topics-label": "主題",
"topic" : "主題"
"topic": "主題"
},
"too-many" : "batch節點中有太多待定信息",
"unexpected" : "未知模式",
"no-parts" : "資訊中沒有parts屬性"
"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/x-red" data-help-name="tls-config">
<p>TLS連接的配置選項</p>
</script>

View File

@@ -0,0 +1,22 @@
<!--
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/x-red" data-help-name="http proxy">
<p>HTTP代理的配置選項</p>
<h3>詳細</h3>
<p>訪問代理例外列表中的主機時將不使用任何代理</p>
</script>

View File

@@ -0,0 +1,70 @@
<!--
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/x-red" data-help-name="mqtt in">
<p>連接到MQTT代理並訂閱來自指定主題的消息</p>
<h3>輸出</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">字符串 | buffer</span></dt>
<dd>如果不是二進制buffer的話就是字符串</dd>
<dt>topic <span class="property-type">字符串</span></dt>
<dd>MQTT主題使用<code>/</code></dd>
<dt>qos <span class="property-type">數值</span> </dt>
<dd>QoS服務質量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節點輸入或輸出可以共享相同的代理連接</p>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<p>連接到MQTT代理並發布消息</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">字符串 | buffer</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>QoS服務質量0, 最多一次 1, 最少一次 2, 只一次默認值爲0</dd>
<dt class="optional">retain <span class="property-type">布爾值</span></dt>
<dd>設置爲<code>true</code><code>false</code></dd>
</dl>
<h3>詳細</h3>
<p><code>msg.payload</code>ObjectJSONbuffer</p>
<p>可以在節點中配置所使用的主題或者如果留爲空白則可以通過<code>msg.topic</code></p>
<p>同樣可以在節點中配置QoS和保留值或者如果保留空白則分別由<code>msg.qos</code><code>msg.retain</code></p>
<p>該節點需要與要配置的MQTT代理的連接通過單擊鉛筆圖標進行配置</p>
<p>如果需要幾個MQTT節點輸入或輸出可以共享相同的代理連接</p>
</script>
<script type="text/x-red" data-help-name="mqtt-broker">
<p>與MQTT代理的連接設置</p>
<p>創建與代理的連接設置可以在<code>MQTT In</code><code>MQTT Out</code></p>
<p>如果未爲該節點設置客戶端ID並且設置了會話初始化則將生成一個隨機客戶端ID設置客戶端ID時請確保它對于連接目標處的代理是唯一的</p>
<h4>Birth Message</h4>
<p>建立連接後發布在以配置主題中的消息</p>
<h4>Close Message</h4>
<p>在連接正常結束之前重新部署或者關閉了節點時發布在以配置主題中的消息</p>
<h4>Will Message</h4>
<p>當節點意外丟失連接時由代理發布的消息</p>
<h4>WebSockets</h4>
<p>可以將節點配置成使用WebSocket連接使用WebSocket時請在服務器字段中以完整格式描述連接目標的URI 例如</p>
<pre>ws://example.com:4000/mqtt</pre>
</script>

View File

@@ -0,0 +1,81 @@
<!--
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/x-red" data-help-name="http in">
<p>創建用于創建Web服務的HTTP端點</p>
<h3>輸出</h3>
<dl class="message-properties">
<dt>payload</dt>
<dd>GET請求包含任何查詢字符串參數的對象或者包含HTTP請求正文</dd>
<dt>req<span class="property-type">object</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> - cookie</li>
<li><code>files</code> - </li>
</ul>
</dd>
<dt>res<span class="property-type">object</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</p>
</script>
<script type="text/x-red" data-help-name="http response">
<p>將響應發送回從HTTP輸入節點接收的請求</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">string</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">object</span></dt>
<dd>如果設置則提供HTTP頭以包含在響應中</dd>
<dt class="optional">cookies <span class="property-type">object</span></dt>
<dd>如果設置則可用于設置或刪除cookie</dd>
</dl>
<h3>詳細</h3>
<p>還可以在節點本身內設置<code>statusCode</code><code>headers</code>message</p>
<h4>Cookie處理</h4>
<p><code>cookies</code>/使cookieoptions<p>
<p>下面的示例設置兩個cookie-一個名爲<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> - () Cookie</li>
<li><code>expires</code> - () GMT0cookie</li>
<li><code>maxAge</code> - () </li>
<li><code>path</code> - (字符串) Cookie的路徑。默認爲/</li>
<li><code>value</code> - () Cookie使</li>
</ul>
<p>要刪除Cookie請將其<code>value</code><code>null</code></p>
</script>

View File

@@ -0,0 +1,78 @@
<!--
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/x-red" 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</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">object</span></dt>
<dd>設置請求的HTTP頭</dd>
<dt class="optional">cookies <span class="property-type">object</span></dt>
<dd>如果設置則可用于發送帶有請求的cookie</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">字符串 | object | buffer</span></dt>
<dd>響應的正文可以將節點配置爲以字符串形式返回主體嘗試將其解析爲JSON字符串或將其保留爲二進制buffer</dd>
<dt>statusCode <span class="property-type">數值</span></dt>
<dd>響應的狀態碼如果請求無法完成則返回錯誤碼</dd>
<dt>headers <span class="property-type">object</span></dt>
<dd>包含響應頭的對象</dd>
<dt>responseUrl <span class="property-type">字符串</span></dt>
<dd>如果在處理請求時發生任何重定向則此屬性爲最終重定向的URL否則則爲原始請求的URL</dd>
<dt>responseCookies <span class="property-type">object</span></dt>
<dd>如果響應包含cookie則此屬性是每個cookie的名稱/鍵值對的對象</dd>
<dt>redirectList <span class="property-type">數組</span></dt>
<dd>如果請求被重定向了一次或多次則累積的信息將被添加到此屬性location是下一個重定向目標cookie是從重定向源返回的cookie</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>使{{{...}}}mustache/ &</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>Cookie處理</h4>
<p>傳遞給節點的<code>cookies</code>/cookie<code>value</code></p>
<p>請求返回的所有cookie都將在<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>{
"KEY": {
"value": FILE_CONTENTS,
"options": {
"filename": "FILENAME"
}
}
}</code></pre>
<p><code>KEY</code>,<code>FILE_CONTENTS</code><code>FILENAME</code></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/x-red" data-help-name="websocket in">
<p>WebSocket輸入節點</p>
<p>默認情況下從WebSocket接收的數據將位于<code>msg.payload</code>JSONJSON</p>
</script>
<script type="text/x-red" data-help-name="websocket out">
<p>WebSocket輸出節點</p>
<p>默認情況下<code>msg.payload</code>WebSocket<code>msg</code>JSONWebSocket</p>
<p>如果到達此節點的消息是從WebSocket In節點開始的則該消息將發送回觸發流程的客戶端否則消息將廣播給所有連接的客戶端</p>
<p>如果要廣播從WebSocket輸入節點開始的消息則可以應該刪除流中的<code>msg._session</code></p>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<p>此配置節點使用指定的路徑創建WebSocket服務器端點</p>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<p>此配置節點將WebSocket客戶端連接到指定的URL</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/x-red" data-help-name="tcp in">
<p>提供TCP輸入選擇可以連接到遠程TCP端口或接受傳入連接</p>
<p><b>注意</b>root1024</p>
</script>
<script type="text/x-red" data-help-name="tcp out">
<p>提供TCP輸出的選擇可以連接到遠程TCP端口接受傳入的連接或回複從TCP In節點收到的消息</p>
<p>僅發送<code>msg.payload</code></p>
<p>如果<code>msg.payload</code>Base64Base64</p>
<p>如果不存在<code>msg._session</code><b></b></p>
<p><b>注意</b>root1024</p>
</script>
<script type="text/x-red" data-help-name="tcp request">
<p>一個簡單的TCP請求節點<code>msg.payload</code>tcp</p>
<p>連接到服務器發送請求並接收響應 可以從固定數量的字符與指定字符匹配的字符中選擇操作從第一個答複到達起等待指定的時間等待數據到達發送數據並立即取消連接而無需等待答複等操作中進行選擇</p>
<p>響應將在<code>msg.payload</code>buffer<code>.toString()</code></p>
<p>如果將tcp主機或端口留空則必須使用<code>msg.host</code><code>msg.port</code></p>
</script>

View File

@@ -0,0 +1,28 @@
<!--
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/x-red" data-help-name="udp in">
<p>UDP輸入節點<code>msg.payload</code>BufferBase64</p>
<p><code>msg.ip</code><code>msg.port</code>IP</p>
<p><b>注意</b>root使1024</p>
</script>
<script type="text/x-red" 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,43 @@
<!--
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/x-red" data-help-name="csv">
<p>在CSV格式的字符串及其JavaScript對象表示形式之間進行相互轉換</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 數組 | 字符串</span></dt>
<dd>JavaScript對象數組或CSV字符串</dd>
</dl>
<h3>輸出</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 數組 | 字符串</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>如果輸入是數組則列模板僅用于有選擇地生成一行列標題</p>
<p>只要正確設置<code>parts</code></p>
<p>如果輸出多個消息則將設置其<code>parts</code></p>
<p><b>注意</b>使</p>
</script>

View File

@@ -0,0 +1,33 @@
<!--
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/x-red" data-help-name="html">
<p>使用CSS選擇器從<code>msg.payload</code>html</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>Output</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">數組 | 字符串</span></dt>
<dd>結果可以是有效載荷中包含匹配元素的數組的單個消息也可以是多條消息每條消息都包含匹配元素發送多條消息時需要爲消息設置<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 documentation</a> </p>
</script>

View File

@@ -0,0 +1,43 @@
<!--
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/x-red" data-help-name="json">
<p>在JSON字符串及其JavaScript對象表示形式之間相互轉換</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>JavaScript對象或JSON字符串</dd>
<dt>schema<span class="property-type">object</span></dt>
<dd>可選的JSON Schema對象用于驗證有效負載在將<code>msg</code></dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>
<ul>
<li>如果輸入是JSON字符串它將嘗試將其解析爲JavaScript對象</li>
<li>如果輸入是JavaScript對象它將創建一個JSON字符串並可以選擇對此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>content-type使JSONJavaScript</p>
<p>如果指定了轉換爲JSON字符串則不會對收到的字符串進行進一步的檢查也就是說即使指定了格式化選項它也不會檢查字符串是否正確爲JSON或對JSON執行整形</p>
<p>有關JSON模式的更多詳細信息請查閱<a href="http://json-schema.org/latest/json-schema-validation.html">規範</a>.</p>
</script>

View File

@@ -0,0 +1,48 @@
<!--
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/x-red" data-help-name="xml">
<p>在XML字符串及其JavaScript對象表示形式之間進行相互轉換</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>JavaScript對象或XML字符串</dd>
</dl>
<h3>輸出</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>
<ul>
<li>如果輸入是字符串它將嘗試將其解析爲XML並創建一個JavaScript對象</li>
<li>如果輸入是JavaScript對象它將嘗試構建XML字符串</li>
</ul>
</dd>
<dt class="optional">options <span class="property-type">object</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>
<p>在XML和對象之間進行轉換時默認情況下XML屬性會添加到名爲<code>$</code><code>_</code></p>
<p>例如將如下所示轉換以下XML</p>
<pre>&lt;p class="tag"&gt;Hello World&lt;/p&gt;</pre>
<pre>{
"p": {
"$": {
"class": "tag"
},
"_": "Hello World"
}
}</pre>
</script>

View File

@@ -0,0 +1,34 @@
<!--
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/x-red" data-help-name="yaml">
<p>在YAML格式的字符串及其JavaScript對象表示形式之間相互轉換</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>JavaScript對象或YAML字符串</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串</span></dt>
<dd>
<ul>
<li>如果輸入是YAML字符串它將嘗試將其解析爲JavaScript對象</li>
<li>如果輸入是JavaScript對象它將創建一個YAML字符串</li>
</ul>
</dd>
</dl>
</script>

View File

@@ -0,0 +1,133 @@
<!--
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/x-red" data-help-name="split">
<p>將一條消息拆分爲一系列消息</p>
<h3>輸入</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">object | 字符串 | 數組 | buffer</span></dt>
<dd>節點的行爲由<code>msg.payload</code>:
<ul>
<li><b>字符串</b>/<b>buffer</b> - 使<code>\n</code></li>
<li><b>數組</b> - </li>
<li><b>object</b> - /</li>
</ul>
</dd>
</dl>
<h3>輸出</h3>
<dl class="message-properties">
<dt>parts<span class="property-type">object</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> - -///buffer</li>
<li><code>ch</code> - buffer</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/x-red" data-help-name="join">
<p>將消息序列合並爲一條消息.</p>
<p>共有三種模式</p>
<dl>
<dt>自動模式</dt>
<dd><b>split</b></dd>
<dt>手動模式</dt>
<dd>手動地以各種方式合並消息序列</dd>
<dt>列聚合模式</dt>
<dd>對消息列中的所有消息應用表達式以將其簡化爲單個消息</dd>
</dl>
<h3>輸入</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">object</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> - -///buffer</li>
<li><code>ch</code> - buffer</li>
<li><code>key</code> - <code>msg.topic</code>/li>
<li><code>len</code> - 使</li>
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>如果設置則節點將以其當前狀態發送其輸出消息</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>merged object</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,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/x-red" 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表達式來對屬性值進行排序還可以是message屬性或JSONata表達式來對消息序列進行排序<p>
<p>在對消息序列進行排序時排序節點依賴于接收到的消息來設置<code>msg.parts</code></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,34 @@
<!--
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/x-red" data-help-name="batch">
<p>根據各種規則創建消息序列</p>
<h3>詳細</h3>
<p>有三種創建消息序列的模式</p>
<dl>
<dt>訊息數</dt>
<dd>將消息分組爲給定長度的序列 <b>overlap</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>
</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="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">字符串 | buffer</span></dt>
<dd>文件的內容可以是字符串也可以是二進制的buffer</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>可以選擇將文本文件拆分爲幾行每行輸出一條消息或者將二進制文件拆分爲較小的buffer塊-塊大小取決于操作系統但通常爲64kLinux/Mac或41kWindows</p>
<p>當拆分爲多條消息時每條消息將具有<code>parts</code></p>
<p>如果輸出格式爲字符串則可以從編碼列表中指定輸入數據的編碼</p>
<p>應該使用Catch節點來捕獲並處理錯誤</p>
</script>

View File

@@ -0,0 +1,25 @@
<!--
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/x-red" data-help-name="watch">
<p>監視目錄或文件中的更改</p>
<p>您可以輸入用逗號分隔的目錄和/或文件的列表您需要在所有帶有空格的地方加上引號...</p>
<p>在Windows上必須在任何目錄名稱中使用雙反斜杠<code>\\</code></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>everything</i></p>
<p><b>注意</b>使</p>
</script>

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "1.0.3",
"version": "1.0.4",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -15,30 +15,30 @@
}
],
"dependencies": {
"ajv": "6.10.2",
"ajv": "6.12.0",
"body-parser": "1.19.0",
"cheerio": "0.22.0",
"content-type": "1.0.4",
"cookie-parser": "1.4.4",
"cookie": "0.4.0",
"cors": "2.8.5",
"cron": "1.7.2",
"cron": "1.8.2",
"denque": "1.4.1",
"fs-extra": "8.1.0",
"fs.notify": "0.0.4",
"hash-sum": "2.0.0",
"https-proxy-agent": "2.2.4",
"https-proxy-agent": "5.0.0",
"is-utf8": "0.2.1",
"js-yaml": "3.13.1",
"media-typer": "1.1.0",
"mqtt": "2.18.8",
"multer": "1.4.2",
"mustache": "3.0.2",
"mustache": "4.0.0",
"on-headers": "1.0.2",
"raw-body": "2.4.1",
"request": "2.88.0",
"ws": "6.2.1",
"xml2js": "0.4.22",
"iconv-lite": "0.5.0"
"xml2js": "0.4.23",
"iconv-lite": "0.5.1"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "1.0.3",
"version": "1.0.4",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,9 +16,9 @@
}
],
"dependencies": {
"@node-red/util": "1.0.3",
"@node-red/util": "1.0.4",
"semver": "6.3.0",
"uglify-js": "3.6.9",
"uglify-js": "3.8.0",
"when": "3.7.8"
}
}

View File

@@ -57,6 +57,8 @@ var api = module.exports = {
* Sets the current flow configuration
* @param {Object} opts
* @param {User} opts.user - the user calling the api
* @param {Object} opts.flows - the flow configuration: `{flows: [..], credentials: {}}`
* @param {Object} opts.deploymentType - the type of deployment - "full", "nodes", "flows", "reload"
* @param {Object} opts.req - the request to log (optional)
* @return {Promise<Flows>} - the active flow configuration
* @memberof @node-red/runtime_flows
@@ -83,7 +85,7 @@ var api = module.exports = {
return reject(err);
}
}
apiPromise = runtime.nodes.setFlows(flows.flows,deploymentType);
apiPromise = runtime.nodes.setFlows(flows.flows,flows.credentials,deploymentType);
}
apiPromise.then(function(flowId) {
return resolve({rev:flowId});

View File

@@ -193,6 +193,7 @@ Node.prototype.emit = function(event, ...args) {
*/
Node.prototype._emitInput = function(arg) {
var node = this;
this.metric("receive", arg);
if (node._inputCallback) {
// Just one callback registered.
try {
@@ -448,7 +449,6 @@ Node.prototype.receive = function(msg) {
if (!msg._msgid) {
msg._msgid = redUtil.generateId();
}
this.metric("receive",msg);
this.emit("input",msg);
};

View File

@@ -420,15 +420,39 @@ function createRootContext() {
Object.defineProperties(obj, {
get: {
value: function(key, storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback()
return;
}
return undefined;
}
},
set: {
value: function(key, value, storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback()
return
}
}
},
keys: {
value: function(storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback();
return;
}
return undefined;
}
}
@@ -436,25 +460,48 @@ function createRootContext() {
return obj;
}
function getContext(localId,flowId,parent) {
var contextId = localId;
/**
* Get a flow-level context object.
* @param {string} flowId [description]
* @param {string} parentFlowId the id of the parent flow. undefined
* @return {object}} the context object
*/
function getFlowContext(flowId,parentFlowId) {
if (contexts.hasOwnProperty(flowId)) {
return contexts[flowId];
}
var parentContext = contexts[parentFlowId];
if (!parentContext) {
parentContext = createRootContext();
contexts[parentFlowId] = parentContext;
// throw new Error("Flow "+flowId+" is missing parent context "+parentFlowId);
}
var newContext = createContext(flowId,undefined,parentContext);
contexts[flowId] = newContext;
return newContext;
}
function getContext(nodeId, flowId) {
var contextId = nodeId;
if (flowId) {
contextId = localId+":"+flowId;
contextId = nodeId+":"+flowId;
}
if (contexts.hasOwnProperty(contextId)) {
return contexts[contextId];
}
var newContext = createContext(contextId,undefined,parent);
var newContext = createContext(contextId);
if (flowId) {
var node = flows.get(flowId);
var parent = undefined;
if (node && node.type.startsWith("subflow:")) {
parent = node.context().flow;
var flowContext = contexts[flowId];
if (!flowContext) {
// This is most likely due to a unit test for a node which doesn't
// initialise the flow properly.
// To keep things working, initialise the missing context.
// This *does not happen* in normal node-red operation
flowContext = createContext(flowId,undefined,createRootContext());
contexts[flowId] = flowContext;
}
else {
parent = createRootContext();
}
var flowContext = getContext(flowId,undefined,parent);
Object.defineProperty(newContext, 'flow', {
value: flowContext
});
@@ -466,6 +513,39 @@ function getContext(localId,flowId,parent) {
return newContext;
}
//
// function getContext(localId,flowId,parent) {
// var contextId = localId;
// if (flowId) {
// contextId = localId+":"+flowId;
// }
// console.log("getContext",localId,flowId,"known?",contexts.hasOwnProperty(contextId));
// if (contexts.hasOwnProperty(contextId)) {
// return contexts[contextId];
// }
// var newContext = createContext(contextId,undefined,parent);
// if (flowId) {
// var node = flows.get(flowId);
// console.log("flows,get",flowId,node&&node.type)
// var parent = undefined;
// if (node && node.type.startsWith("subflow:")) {
// parent = node.context().flow;
// }
// else {
// parent = createRootContext();
// }
// var flowContext = getContext(flowId,undefined,parent);
// Object.defineProperty(newContext, 'flow', {
// value: flowContext
// });
// }
// Object.defineProperty(newContext, 'global', {
// value: contexts['global']
// })
// contexts[contextId] = newContext;
// return newContext;
// }
function deleteContext(id,flowId) {
if(!hasConfiguredStore){
// only delete context if there's no configured storage.
@@ -517,6 +597,7 @@ module.exports = {
load: load,
listStores: listStores,
get: getContext,
getFlowContext:getFlowContext,
delete: deleteContext,
clean: clean,
close: close

View File

@@ -18,6 +18,7 @@ var clone = require("clone");
var redUtil = require("@node-red/util").util;
var flowUtil = require("./util");
var events = require("../../events");
const context = require('../context');
var Subflow;
var Log;
@@ -54,6 +55,8 @@ class Flow {
this.catchNodes = [];
this.statusNodes = [];
this.path = this.id;
// Ensure a context exists for this flow
this.context = context.getFlowContext(this.id,this.parent.id);
}
/**

Some files were not shown because too many files have changed in this diff Show More