mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'dev' into pr_2583
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -22,3 +22,4 @@ packages/node_modules/@node-red/editor-client/public | ||||
| !test/**/node_modules | ||||
| docs | ||||
| !packages/node_modules/**/docs | ||||
| .vscode | ||||
| @@ -2,6 +2,7 @@ sudo: false | ||||
| language: node_js | ||||
| matrix: | ||||
|   include: | ||||
|     - node_js: "14" | ||||
|     - node_js: "12" | ||||
|     - node_js: "10" | ||||
|       script: | ||||
|   | ||||
| @@ -125,6 +125,7 @@ module.exports = function(grunt) { | ||||
|                 src: [ | ||||
|                     // Ensure editor source files are concatenated in | ||||
|                     // the right order | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/polyfills.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/red.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/events.js", | ||||
| @@ -151,6 +152,7 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", | ||||
| @@ -163,6 +165,8 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", | ||||
| @@ -177,6 +181,7 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/group.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red", | ||||
|     "version": "1.0.6", | ||||
|     "version": "1.1.0", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "http://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
| @@ -48,6 +48,7 @@ | ||||
|         "js-yaml": "3.13.1", | ||||
|         "json-stringify-safe": "5.0.1", | ||||
|         "jsonata": "1.8.3", | ||||
|         "lodash.clonedeep": "^4.5.0", | ||||
|         "media-typer": "1.1.0", | ||||
|         "memorystore": "1.6.2", | ||||
|         "mime": "2.4.4", | ||||
| @@ -104,7 +105,7 @@ | ||||
|         "minami": "1.2.3", | ||||
|         "mocha": "^5.2.0", | ||||
|         "mosca": "^2.8.3", | ||||
|         "node-red-node-test-helper": "^0.2.3", | ||||
|         "node-red-node-test-helper": "^0.2.5", | ||||
|         "node-sass": "^4.13.1", | ||||
|         "should": "^8.4.0", | ||||
|         "sinon": "1.17.7", | ||||
|   | ||||
| @@ -44,6 +44,7 @@ module.exports = { | ||||
|             user: req.user, | ||||
|             module: req.body.module, | ||||
|             version: req.body.version, | ||||
|             url: req.body.url, | ||||
|             req: apiUtils.getRequestLogObject(req) | ||||
|         } | ||||
|         runtimeAPI.nodes.addModule(opts).then(function(info) { | ||||
|   | ||||
| @@ -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(); | ||||
|                 } | ||||
|   | ||||
| @@ -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() | ||||
| } | ||||
|   | ||||
| @@ -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 } | ||||
| }; | ||||
|   | ||||
| @@ -59,6 +59,12 @@ function init(settings,_server,storage,runtimeAPI) { | ||||
|         }); | ||||
|         adminApp.use(corsHandler); | ||||
|  | ||||
|         if (settings.httpAdminMiddleware) { | ||||
|             if (typeof settings.httpAdminMiddleware === "function") { | ||||
|                 adminApp.use(settings.httpAdminMiddleware) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         auth.init(settings,storage); | ||||
|  | ||||
|         var maxApiRequestSize = settings.apiMaxLength || '5mb'; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-api", | ||||
|     "version": "1.0.6", | ||||
|     "version": "1.1.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,8 +16,8 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "1.0.6", | ||||
|         "@node-red/editor-client": "1.0.6", | ||||
|         "@node-red/util": "1.1.0", | ||||
|         "@node-red/editor-client": "1.1.0", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "body-parser": "1.19.0", | ||||
|         "clone": "2.1.2", | ||||
|   | ||||
| @@ -14,7 +14,13 @@ | ||||
|             "back": "Back", | ||||
|             "next": "Next", | ||||
|             "clone": "Clone project", | ||||
|             "cont": "Continue" | ||||
|             "cont": "Continue", | ||||
|             "style": "Style", | ||||
|             "line": "Outline", | ||||
|             "fill": "Fill", | ||||
|             "label": "Label", | ||||
|             "color": "Color", | ||||
|             "position": "Position" | ||||
|         }, | ||||
|         "type": { | ||||
|             "string": "string", | ||||
| @@ -28,6 +34,13 @@ | ||||
|             "null": "null" | ||||
|         } | ||||
|     }, | ||||
|     "event": { | ||||
|         "loadPalette": "Loading Palette", | ||||
|         "loadNodeCatalogs": "Loading Node catalogs", | ||||
|         "loadNodes": "Loading Nodes __count__", | ||||
|         "loadFlows": "Loading Flows", | ||||
|         "importFlows": "Adding Flows to workspace" | ||||
|     }, | ||||
|     "workspace": { | ||||
|         "defaultName": "Flow __number__", | ||||
|         "editFlow": "Edit flow: __name__", | ||||
| @@ -91,7 +104,12 @@ | ||||
|             "projects-new": "New", | ||||
|             "projects-open": "Open", | ||||
|             "projects-settings": "Project Settings", | ||||
|             "showNodeLabelDefault": "Show label of newly added nodes" | ||||
|             "showNodeLabelDefault": "Show label of newly added nodes", | ||||
|             "groups": "Groups", | ||||
|             "groupSelection": "Group selection", | ||||
|             "ungroupSelection": "Ungroup selection", | ||||
|             "groupMergeSelection": "Merge selection", | ||||
|             "groupRemoveSelection": "Remove from group" | ||||
|         } | ||||
|     }, | ||||
|     "actions": { | ||||
| @@ -171,6 +189,8 @@ | ||||
|         "node_plural": "__count__ nodes", | ||||
|         "configNode": "__count__ configuration node", | ||||
|         "configNode_plural": "__count__ configuration nodes", | ||||
|         "group": "__count__ group", | ||||
|         "group_plural": "__count__ groups", | ||||
|         "flow": "__count__ flow", | ||||
|         "flow_plural": "__count__ flows", | ||||
|         "subflow": "__count__ subflow", | ||||
| @@ -186,6 +206,9 @@ | ||||
|         "nodesImported": "Imported:", | ||||
|         "nodeCopied": "__count__ node copied", | ||||
|         "nodeCopied_plural": "__count__ nodes copied", | ||||
|         "groupCopied": "__count__ group copied", | ||||
|         "groupCopied_plural": "__count__ groups copied", | ||||
|         "groupStyleCopied": "Group style copied", | ||||
|         "invalidFlow": "Invalid flow: __message__", | ||||
|         "export": { | ||||
|             "selected":"selected nodes", | ||||
| @@ -308,6 +331,13 @@ | ||||
|             "multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection" | ||||
|         } | ||||
|     }, | ||||
|     "group": { | ||||
|         "editGroup": "Edit group: __name__", | ||||
|         "errors": { | ||||
|             "cannotCreateDiffGroups": "Cannot create group using nodes from different groups", | ||||
|             "cannotAddSubflowPorts": "Cannot add subflow ports to a group" | ||||
|         } | ||||
|     }, | ||||
|     "editor": { | ||||
|         "configEdit": "Edit", | ||||
|         "configAdd": "Add", | ||||
| @@ -351,7 +381,8 @@ | ||||
|             "bool": "bool", | ||||
|             "json": "JSON", | ||||
|             "bin": "buffer", | ||||
|             "env": "env variable" | ||||
|             "env": "env variable", | ||||
|             "cred": "credential" | ||||
|         }, | ||||
|         "menu": { | ||||
|             "input": "input", | ||||
| @@ -538,6 +569,7 @@ | ||||
|             "label": "info", | ||||
|             "node": "Node", | ||||
|             "type": "Type", | ||||
|             "group": "Group", | ||||
|             "module": "Module", | ||||
|             "id": "ID", | ||||
|             "status": "Status", | ||||
| @@ -560,7 +592,20 @@ | ||||
|             "nodeHelp": "Node Help", | ||||
|             "none":"None", | ||||
|             "arrayItems": "__count__ items", | ||||
|             "showTips":"You can open the tips from the settings panel" | ||||
|             "showTips":"You can open the tips from the settings panel", | ||||
|             "outline": "Outline", | ||||
|             "empty": "empty", | ||||
|             "globalConfig": "Global Configuration Nodes" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "Help", | ||||
|             "label": "help", | ||||
|             "search": "Search help", | ||||
|             "nodeHelp": "Node Help", | ||||
|             "showHelp": "Show help", | ||||
|             "showInOutline": "Show in outline", | ||||
|             "showTopics": "Show topics", | ||||
|             "noHelp": "No help topic selected" | ||||
|         }, | ||||
|         "config": { | ||||
|             "name": "Configuration nodes", | ||||
| @@ -613,7 +658,6 @@ | ||||
|                 "removeFromProject": "remove from project", | ||||
|                 "addToProject": "add to project", | ||||
|                 "files": "Files", | ||||
|                 "package": "Package", | ||||
|                 "flow": "Flow", | ||||
|                 "credentials": "Credentials", | ||||
|                 "package":"Package", | ||||
| @@ -757,7 +801,8 @@ | ||||
|             "bin": "buffer", | ||||
|             "date": "timestamp", | ||||
|             "jsonata": "expression", | ||||
|             "env": "env variable" | ||||
|             "env": "env variable", | ||||
|             "cred": "credential" | ||||
|         } | ||||
|     }, | ||||
|     "editableList": { | ||||
| @@ -804,9 +849,9 @@ | ||||
|         "expandItems": "Expand items", | ||||
|         "collapseItems": "Collapse items", | ||||
|         "duplicate": "Duplicate", | ||||
| 	"error": { | ||||
| 	    "invalidJSON": "Invalid JSON: " | ||||
| 	} | ||||
|         "error": { | ||||
|             "invalidJSON": "Invalid JSON: " | ||||
|         } | ||||
|     }, | ||||
|     "markdownEditor": { | ||||
|         "title": "Markdown editor", | ||||
| @@ -978,7 +1023,7 @@ | ||||
|             "retry": "Retry", | ||||
|             "update-failed": "Failed to update auth", | ||||
|             "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." | ||||
|             "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.</p>" | ||||
|         }, | ||||
|         "create-branch-list": { | ||||
|             "invalid": "Invalid branch", | ||||
|   | ||||
							
								
								
									
										63
									
								
								packages/node_modules/@node-red/editor-client/locales/ja/editor.json
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										63
									
								
								packages/node_modules/@node-red/editor-client/locales/ja/editor.json
									
									
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -14,7 +14,13 @@ | ||||
|             "back": "戻る", | ||||
|             "next": "進む", | ||||
|             "clone": "プロジェクトをクローン", | ||||
|             "cont": "続ける" | ||||
|             "cont": "続ける", | ||||
|             "style": "形式", | ||||
|             "line": "線", | ||||
|             "fill": "塗りつぶし", | ||||
|             "label": "ラベル", | ||||
|             "color": "色", | ||||
|             "position": "配置" | ||||
|         }, | ||||
|         "type": { | ||||
|             "string": "文字列", | ||||
| @@ -28,6 +34,13 @@ | ||||
|             "null": "null" | ||||
|         } | ||||
|     }, | ||||
|     "event": { | ||||
|         "loadPalette": "パレットを読み込み中", | ||||
|         "loadNodeCatalogs": "ノードカタログを読み込み中", | ||||
|         "loadNodes": "ノードを読み込み中 __count__", | ||||
|         "loadFlows": "フローを読み込み中", | ||||
|         "importFlows": "ワークスペースにフローを追加中" | ||||
|     }, | ||||
|     "workspace": { | ||||
|         "defaultName": "フロー __number__", | ||||
|         "editFlow": "フローを編集: __name__", | ||||
| @@ -67,7 +80,7 @@ | ||||
|             "settings": "設定", | ||||
|             "userSettings": "ユーザ設定", | ||||
|             "nodes": "ノード", | ||||
|             "displayStatus": "ノードの状態を表示", | ||||
|             "displayStatus": "ノードのステータスを表示", | ||||
|             "displayConfig": "ノードの設定", | ||||
|             "import": "読み込み", | ||||
|             "export": "書き出し", | ||||
| @@ -91,7 +104,12 @@ | ||||
|             "projects-new": "新規", | ||||
|             "projects-open": "開く", | ||||
|             "projects-settings": "設定", | ||||
|             "showNodeLabelDefault": "追加したノードのラベルを表示" | ||||
|             "showNodeLabelDefault": "追加したノードのラベルを表示", | ||||
|             "groups": "グループ", | ||||
|             "groupSelection": "選択部分をグループ化", | ||||
|             "ungroupSelection": "選択部分をグループ解除", | ||||
|             "groupMergeSelection": "選択部分をマージ", | ||||
|             "groupRemoveSelection": "グループから削除" | ||||
|         } | ||||
|     }, | ||||
|     "actions": { | ||||
| @@ -171,6 +189,8 @@ | ||||
|         "node_plural": "__count__ 個のノード", | ||||
|         "configNode": "__count__ 個の設定ノード", | ||||
|         "configNode_plural": "__count__ 個の設定ノード", | ||||
|         "group": "__count__ 個のグループ", | ||||
|         "group_plural": "__count__ 個のグループ", | ||||
|         "flow": "__count__ 個のフロー", | ||||
|         "flow_plural": "__count__ 個のフロー", | ||||
|         "subflow": "__count__ 個のサブフロー", | ||||
| @@ -186,6 +206,9 @@ | ||||
|         "nodesImported": "読み込みました:", | ||||
|         "nodeCopied": "__count__ 個のノードをコピーしました", | ||||
|         "nodeCopied_plural": "__count__ 個のノードをコピーしました", | ||||
|         "groupCopied": "__count__ 個のグループをコピーしました", | ||||
|         "groupCopied_plural": "__count__ 個のグループをコピーしました", | ||||
|         "groupStyleCopied": "グループの形式をコピーしました", | ||||
|         "invalidFlow": "不正なフロー: __message__", | ||||
|         "export": { | ||||
|             "selected": "選択したフロー", | ||||
| @@ -308,6 +331,13 @@ | ||||
|             "multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています" | ||||
|         } | ||||
|     }, | ||||
|     "group": { | ||||
|         "editGroup": "__name__ グループを編集", | ||||
|         "errors": { | ||||
|             "cannotCreateDiffGroups": "異なるグループのノードを使用してグループを作成することはできません", | ||||
|             "cannotAddSubflowPorts": "グループにサブフローの端子を追加できません" | ||||
|         } | ||||
|     }, | ||||
|     "editor": { | ||||
|         "configEdit": "編集", | ||||
|         "configAdd": "追加", | ||||
| @@ -351,7 +381,8 @@ | ||||
|             "bool": "真偽", | ||||
|             "json": "JSON", | ||||
|             "bin": "バッファ", | ||||
|             "env": "環境変数" | ||||
|             "env": "環境変数", | ||||
|             "cred": "認証情報" | ||||
|         }, | ||||
|         "menu": { | ||||
|             "input": "入力", | ||||
| @@ -538,6 +569,7 @@ | ||||
|             "label": "情報", | ||||
|             "node": "ノード", | ||||
|             "type": "型", | ||||
|             "group": "グループ", | ||||
|             "module": "モジュール", | ||||
|             "id": "ID", | ||||
|             "status": "状態", | ||||
| @@ -560,7 +592,20 @@ | ||||
|             "nodeHelp": "ノードのヘルプ", | ||||
|             "none": "なし", | ||||
|             "arrayItems": "__count__ 要素", | ||||
|             "showTips": "設定からヒントを表示できます" | ||||
|             "showTips": "設定からヒントを表示できます", | ||||
|             "outline": "アウトライン", | ||||
|             "empty": "空", | ||||
|             "globalConfig": "グローバル設定ノード" | ||||
|         }, | ||||
|         "help": { | ||||
|             "name": "ヘルプ", | ||||
|             "label": "ヘルプ", | ||||
|             "search": "ヘルプを検索", | ||||
|             "nodeHelp": "ノードヘルプ", | ||||
|             "showHelp": "ヘルプを表示", | ||||
|             "showInOutline": "アウトラインに表示", | ||||
|             "showTopics": "トピックを表示", | ||||
|             "noHelp": "ヘルプのトピックが未選択" | ||||
|         }, | ||||
|         "config": { | ||||
|             "name": "ノードの設定を表示", | ||||
| @@ -613,9 +658,9 @@ | ||||
|                 "removeFromProject": "プロジェクトから削除", | ||||
|                 "addToProject": "プロジェクトへ追加", | ||||
|                 "files": "ファイル", | ||||
|                 "package": "パッケージ", | ||||
|                 "flow": "フロー", | ||||
|                 "credentials": "認証情報", | ||||
|                 "package": "パッケージ", | ||||
|                 "packageCreate": "変更が保存された時にファイルが作成されます", | ||||
|                 "fileNotExist": "ファイルが存在しません", | ||||
|                 "selectFile": "ファイルを選択", | ||||
| @@ -756,7 +801,8 @@ | ||||
|             "bin": "バッファ", | ||||
|             "date": "日時", | ||||
|             "jsonata": "JSONata式", | ||||
|             "env": "環境変数" | ||||
|             "env": "環境変数", | ||||
|             "cred": "認証情報" | ||||
|         } | ||||
|     }, | ||||
|     "editableList": { | ||||
| @@ -976,7 +1022,8 @@ | ||||
|             "passphrase": "パスフレーズ", | ||||
|             "retry": "リトライ", | ||||
|             "update-failed": "認証の更新に失敗しました", | ||||
|             "unhandled": "エラー応答が処理されませんでした" | ||||
|             "unhandled": "エラー応答が処理されませんでした", | ||||
|             "host-key-verify-failed": "<p>ホストキーの検証に失敗</p><p>リポジトリのホストキーを検証できませんでした。<code>known_hosts</code>ファイルを更新して、もう一度試してください。</p>" | ||||
|         }, | ||||
|         "create-branch-list": { | ||||
|             "invalid": "不正なブランチ", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-client", | ||||
|     "version": "1.0.6", | ||||
|     "version": "1.1.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -32,11 +32,16 @@ | ||||
|              } | ||||
|          } | ||||
|      } | ||||
|      function emit(evt,arg) { | ||||
|      function emit() { | ||||
|          var evt = arguments[0] | ||||
|          var args = Array.prototype.slice.call(arguments,1); | ||||
|          if (RED.events.DEBUG) { | ||||
|              console.log(evt,args); | ||||
|          } | ||||
|          if (handlers[evt]) { | ||||
|              for (var i=0;i<handlers[evt].length;i++) { | ||||
|                  try { | ||||
|                      handlers[evt][i](arg); | ||||
|                      handlers[evt][i].apply(null, args); | ||||
|                  } catch(err) { | ||||
|                      console.log("RED.events.emit error: ["+evt+"] "+(err.toString())); | ||||
|                      console.log(err); | ||||
|   | ||||
| @@ -21,6 +21,7 @@ RED.history = (function() { | ||||
|         var i; | ||||
|         var len; | ||||
|         var node; | ||||
|         var group; | ||||
|         var subflow; | ||||
|         var modifiedTabs = {}; | ||||
|         var inverseEv; | ||||
| @@ -74,6 +75,15 @@ RED.history = (function() { | ||||
|                         RED.nodes.removeLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.groups) { | ||||
|                     inverseEv.groups = []; | ||||
|                     for (i=0;i<ev.groups.length;i++) { | ||||
|                         group = ev.groups[i]; | ||||
|                         modifiedTabs[group.z] = true; | ||||
|                         inverseEv.groups.push(group); | ||||
|                         RED.nodes.removeGroup(group); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.workspaces) { | ||||
|                     inverseEv.workspaces = []; | ||||
|                     for (i=0;i<ev.workspaces.length;i++) { | ||||
| @@ -193,12 +203,35 @@ RED.history = (function() { | ||||
|                         n.dirty = true; | ||||
|                     }); | ||||
|                 } | ||||
|                 if (ev.groups) { | ||||
|                     inverseEv.groups = []; | ||||
|                     var groupsToAdd = new Set(ev.groups.map(function(g) { return g.id })); | ||||
|                     for (i=0;i<ev.groups.length;i++) { | ||||
|                         RED.nodes.addGroup(ev.groups[i]) | ||||
|                         modifiedTabs[ev.groups[i].z] = true; | ||||
|                         inverseEv.groups.push(ev.groups[i]); | ||||
|                         if (ev.groups[i].g && !groupsToAdd.has(ev.groups[i].g)) { | ||||
|                             group = RED.nodes.group(ev.groups[i].g); | ||||
|                             if (group.nodes.indexOf(ev.groups[i]) === -1) { | ||||
|                                 group.nodes.push(ev.groups[i]); | ||||
|                             } | ||||
|                             RED.group.markDirty(ev.groups[i]) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.nodes) { | ||||
|                     inverseEv.nodes = []; | ||||
|                     for (i=0;i<ev.nodes.length;i++) { | ||||
|                         RED.nodes.add(ev.nodes[i]); | ||||
|                         modifiedTabs[ev.nodes[i].z] = true; | ||||
|                         inverseEv.nodes.push(ev.nodes[i].id); | ||||
|                         if (ev.nodes[i].g) { | ||||
|                             group = RED.nodes.group(ev.nodes[i].g); | ||||
|                             if (group.nodes.indexOf(ev.nodes[i]) === -1) { | ||||
|                                 group.nodes.push(ev.nodes[i]); | ||||
|                             } | ||||
|                             RED.group.markDirty(group) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
| @@ -227,9 +260,12 @@ RED.history = (function() { | ||||
|                                 } | ||||
|                                 node.dirty = true; | ||||
|                             } | ||||
|                             RED.events.emit("nodes:change",node); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|                 if (subflow) { | ||||
|                     RED.events.emit("subflows:change", subflow); | ||||
|                 } | ||||
|             } else if (ev.t == "move") { | ||||
|                 inverseEv = { | ||||
| @@ -260,6 +296,13 @@ RED.history = (function() { | ||||
|                         RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.addToGroup) { | ||||
|                     RED.group.removeFromGroup(ev.addToGroup,ev.nodes.map(function(n) { return n.n }),true); | ||||
|                     inverseEv.removeFromGroup = ev.addToGroup; | ||||
|                 } else if (ev.removeFromGroup) { | ||||
|                     RED.group.addToGroup(ev.removeFromGroup,ev.nodes.map(function(n) { return n.n })); | ||||
|                     inverseEv.addToGroup = ev.removeFromGroup; | ||||
|                 } | ||||
|             } else if (ev.t == "edit") { | ||||
|                 inverseEv = { | ||||
|                     t: "edit", | ||||
| @@ -283,6 +326,17 @@ RED.history = (function() { | ||||
|                         ev.node[i] = ev.changes[i]; | ||||
|                     } | ||||
|                 } | ||||
|                 var eventType; | ||||
|                 switch(ev.node.type) { | ||||
|                     case 'tab': eventType = "flows"; break; | ||||
|                     case 'group': eventType = "groups"; break; | ||||
|                     case 'subflow': eventType = "subflows"; break; | ||||
|                     default: eventType = "nodes"; break; | ||||
|                 } | ||||
|                 eventType += ":change"; | ||||
|                 RED.events.emit(eventType,ev.node); | ||||
|  | ||||
|  | ||||
|                 if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) { | ||||
|                     $("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled); | ||||
|                     $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled); | ||||
| @@ -370,7 +424,9 @@ RED.history = (function() { | ||||
|                 if (ev.nodes) { | ||||
|                     inverseEv.movedNodes = []; | ||||
|                     var z = ev.activeWorkspace; | ||||
|                     RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) { | ||||
|                     var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id}); | ||||
|                     fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id)) | ||||
|                     fullNodeList.forEach(function(n) { | ||||
|                         n.x += ev.subflow.offsetX; | ||||
|                         n.y += ev.subflow.offsetY; | ||||
|                         n.dirty = true; | ||||
| @@ -411,6 +467,9 @@ RED.history = (function() { | ||||
|                 if (ev.subflow) { | ||||
|                     RED.nodes.addSubflow(ev.subflow.subflow); | ||||
|                     inverseEv.subflow = ev.subflow; | ||||
|                     if (ev.subflow.subflow.g) { | ||||
|                         RED.group.addToGroup(RED.nodes.group(ev.subflow.subflow.g),ev.subflow.subflow); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflows) { | ||||
|                     inverseEv.nodes = []; | ||||
| @@ -422,6 +481,9 @@ RED.history = (function() { | ||||
|                 if (ev.movedNodes) { | ||||
|                     ev.movedNodes.forEach(function(nid) { | ||||
|                         nn = RED.nodes.node(nid); | ||||
|                         if (!nn) { | ||||
|                             nn = RED.nodes.group(nid); | ||||
|                         } | ||||
|                         nn.x -= ev.subflow.offsetX; | ||||
|                         nn.y -= ev.subflow.offsetY; | ||||
|                         nn.dirty = true; | ||||
| @@ -435,7 +497,7 @@ RED.history = (function() { | ||||
|                         RED.nodes.addLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if  (ev.createdLinks) { | ||||
|                 if (ev.createdLinks) { | ||||
|                     inverseEv.removedLinks = []; | ||||
|                     for (i=0;i<ev.createdLinks.length;i++) { | ||||
|                         inverseEv.removedLinks.push(ev.createdLinks[i]); | ||||
| @@ -450,6 +512,60 @@ RED.history = (function() { | ||||
|                 if (ev.order) { | ||||
|                     RED.workspaces.order(ev.order); | ||||
|                 } | ||||
|             } else if (ev.t == "createGroup") { | ||||
|                 inverseEv = { | ||||
|                     t: "ungroup", | ||||
|                     dirty: RED.nodes.dirty(), | ||||
|                     groups: [] | ||||
|                 } | ||||
|                 if (ev.groups) { | ||||
|                     for (i=0;i<ev.groups.length;i++) { | ||||
|                         inverseEv.groups.push(ev.groups[i]); | ||||
|                         RED.group.ungroup(ev.groups[i]); | ||||
|                     } | ||||
|                 } | ||||
|             } else if (ev.t == "ungroup") { | ||||
|                 inverseEv = { | ||||
|                     t: "createGroup", | ||||
|                     dirty: RED.nodes.dirty(), | ||||
|                     groups: [] | ||||
|                 } | ||||
|                 if (ev.groups) { | ||||
|                     for (i=0;i<ev.groups.length;i++) { | ||||
|                         inverseEv.groups.push(ev.groups[i]); | ||||
|                         var nodes = ev.groups[i].nodes.slice(); | ||||
|                         ev.groups[i].nodes = []; | ||||
|                         RED.nodes.addGroup(ev.groups[i]); | ||||
|                         RED.group.addToGroup(ev.groups[i],nodes); | ||||
|                     } | ||||
|                 } | ||||
|             } else if (ev.t == "addToGroup") { | ||||
|                 inverseEv = { | ||||
|                     t: "removeFromGroup", | ||||
|                     dirty: RED.nodes.dirty(), | ||||
|                     group: ev.group, | ||||
|                     nodes: ev.nodes, | ||||
|                     reparent: ev.reparent | ||||
|                 } | ||||
|                 if (ev.nodes) { | ||||
|                     RED.group.removeFromGroup(ev.group,ev.nodes,(ev.hasOwnProperty('reparent')&&ev.hasOwnProperty('reparent')!==undefined)?ev.reparent:true); | ||||
|                 } | ||||
|             } else if (ev.t == "removeFromGroup") { | ||||
|                 inverseEv = { | ||||
|                     t: "addToGroup", | ||||
|                     dirty: RED.nodes.dirty(), | ||||
|                     group: ev.group, | ||||
|                     nodes: ev.nodes, | ||||
|                     reparent: ev.reparent | ||||
|                 } | ||||
|                 if (ev.nodes) { | ||||
|                     RED.group.addToGroup(ev.group,ev.nodes); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if(ev.callback && typeof ev.callback === 'function') { | ||||
|                 inverseEv.callback = ev.callback; | ||||
|                 ev.callback(ev); | ||||
|             } | ||||
|  | ||||
|             Object.keys(modifiedTabs).forEach(function(id) { | ||||
| @@ -460,9 +576,8 @@ RED.history = (function() { | ||||
|             }); | ||||
|  | ||||
|             RED.nodes.dirty(ev.dirty); | ||||
|             RED.view.updateActive(); | ||||
|             RED.view.select(null); | ||||
|             RED.view.redraw(true); | ||||
|             RED.palette.refresh(); | ||||
|             RED.workspaces.refresh(); | ||||
|             RED.sidebar.config.refresh(); | ||||
|             RED.subflow.refresh(); | ||||
| @@ -482,6 +597,9 @@ RED.history = (function() { | ||||
|         list: function() { | ||||
|             return undoHistory; | ||||
|         }, | ||||
|         listRedo: function() { | ||||
|             return redoHistory; | ||||
|         }, | ||||
|         depth: function() { | ||||
|             return undoHistory.length; | ||||
|         }, | ||||
|   | ||||
| @@ -44,6 +44,14 @@ | ||||
|         "ctrl-y": "core:redo", | ||||
|         "ctrl-a": "core:select-all-nodes", | ||||
|         "shift-?": "core:show-help", | ||||
|         "w": "core:scroll-view-up", | ||||
|         "d": "core:scroll-view-right", | ||||
|         "s": "core:scroll-view-down", | ||||
|         "a": "core:scroll-view-left", | ||||
|         "shift-w": "core:step-view-up", | ||||
|         "shift-d": "core:step-view-right", | ||||
|         "shift-s": "core:step-view-down", | ||||
|         "shift-a": "core:step-view-left", | ||||
|         "up": "core:move-selection-up", | ||||
|         "right": "core:move-selection-right", | ||||
|         "down": "core:move-selection-down", | ||||
| @@ -53,6 +61,10 @@ | ||||
|         "shift-down": "core:step-selection-down", | ||||
|         "shift-left": "core:step-selection-left", | ||||
|         "ctrl-shift-j": "core:show-previous-tab", | ||||
|         "ctrl-shift-k": "core:show-next-tab" | ||||
|         "ctrl-shift-k": "core:show-next-tab", | ||||
|         "ctrl-shift-g": "core:group-selection", | ||||
|         "ctrl-shift-u": "core:ungroup-selection", | ||||
|         "ctrl-shift-c": "core:copy-group-style", | ||||
|         "ctrl-shift-v": "core:paste-group-style" | ||||
|      } | ||||
| } | ||||
|   | ||||
| @@ -27,13 +27,16 @@ RED.nodes = (function() { | ||||
|     var subflows = {}; | ||||
|     var loadedFlowVersion = null; | ||||
|  | ||||
|     var groups = {}; | ||||
|     var groupsByZ = {}; | ||||
|  | ||||
|     var initialLoad; | ||||
|  | ||||
|     var dirty = false; | ||||
|  | ||||
|     function setDirty(d) { | ||||
|         dirty = d; | ||||
|         RED.events.emit("nodes:change",{dirty:dirty}); | ||||
|         RED.events.emit("workspace:dirty",{dirty:dirty}); | ||||
|     } | ||||
|  | ||||
|     var registry = (function() { | ||||
| @@ -225,6 +228,7 @@ RED.nodes = (function() { | ||||
|     } | ||||
|     function addLink(l) { | ||||
|         links.push(l); | ||||
|         RED.events.emit("links:add",l); | ||||
|     } | ||||
|  | ||||
|     function getNode(id) { | ||||
| @@ -257,7 +261,7 @@ RED.nodes = (function() { | ||||
|                     delete nodeTabMap[node.z][node.id]; | ||||
|                 } | ||||
|                 removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); }); | ||||
|                 removedLinks.forEach(function(l) {links.splice(links.indexOf(l), 1); }); | ||||
|                 removedLinks.forEach(removeLink); | ||||
|                 var updatedConfigNode = false; | ||||
|                 for (var d in node._def.defaults) { | ||||
|                     if (node._def.defaults.hasOwnProperty(d)) { | ||||
| @@ -302,6 +306,10 @@ RED.nodes = (function() { | ||||
|     } | ||||
|  | ||||
|     function moveNodeToTab(node, z) { | ||||
|         if (node.type === "group") { | ||||
|             moveGroupToTab(node,z); | ||||
|             return; | ||||
|         } | ||||
|         if (nodeTabMap[node.z]) { | ||||
|             delete nodeTabMap[node.z][node.id]; | ||||
|         } | ||||
| @@ -310,6 +318,15 @@ RED.nodes = (function() { | ||||
|         } | ||||
|         nodeTabMap[z][node.id] = node; | ||||
|         node.z = z; | ||||
|         RED.events.emit("nodes:change",node); | ||||
|     } | ||||
|     function moveGroupToTab(group, z) { | ||||
|         var index = groupsByZ[group.z].indexOf(group); | ||||
|         groupsByZ[group.z].splice(index,1); | ||||
|         groupsByZ[z] = groupsByZ[z] || []; | ||||
|         groupsByZ[z].push(group); | ||||
|         group.z = z; | ||||
|         RED.events.emit("groups:change",group); | ||||
|     } | ||||
|  | ||||
|     function removeLink(l) { | ||||
| @@ -317,6 +334,7 @@ RED.nodes = (function() { | ||||
|         if (index != -1) { | ||||
|             links.splice(index,1); | ||||
|         } | ||||
|         RED.events.emit("links:remove",l); | ||||
|     } | ||||
|  | ||||
|     function addWorkspace(ws,targetIndex) { | ||||
| @@ -329,38 +347,53 @@ RED.nodes = (function() { | ||||
|         } else { | ||||
|             workspacesOrder.splice(targetIndex,0,ws.id); | ||||
|         } | ||||
|         RED.events.emit('flows:add',ws); | ||||
|         if (targetIndex !== undefined) { | ||||
|             RED.events.emit('flows:reorder',workspacesOrder) | ||||
|         } | ||||
|     } | ||||
|     function getWorkspace(id) { | ||||
|         return workspaces[id]; | ||||
|     } | ||||
|     function removeWorkspace(id) { | ||||
|         delete workspaces[id]; | ||||
|         delete nodeTabMap[id]; | ||||
|         workspacesOrder.splice(workspacesOrder.indexOf(id),1); | ||||
|  | ||||
|         var ws = workspaces[id]; | ||||
|         var removedNodes = []; | ||||
|         var removedLinks = []; | ||||
|         var n; | ||||
|         var node; | ||||
|         for (n=0;n<nodes.length;n++) { | ||||
|             node = nodes[n]; | ||||
|             if (node.z == id) { | ||||
|                 removedNodes.push(node); | ||||
|             } | ||||
|         } | ||||
|         for(n in configNodes) { | ||||
|             if (configNodes.hasOwnProperty(n)) { | ||||
|                 node = configNodes[n]; | ||||
|         var removedGroups = []; | ||||
|         if (ws) { | ||||
|             delete workspaces[id]; | ||||
|             delete nodeTabMap[id]; | ||||
|             workspacesOrder.splice(workspacesOrder.indexOf(id),1); | ||||
|             var n; | ||||
|             var node; | ||||
|             for (n=0;n<nodes.length;n++) { | ||||
|                 node = nodes[n]; | ||||
|                 if (node.z == id) { | ||||
|                     removedNodes.push(node); | ||||
|                 } | ||||
|             } | ||||
|             for(n in configNodes) { | ||||
|                 if (configNodes.hasOwnProperty(n)) { | ||||
|                     node = configNodes[n]; | ||||
|                     if (node.z == id) { | ||||
|                         removedNodes.push(node); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             removedGroups = groupsByZ[id] || []; | ||||
|             removedGroups.forEach(function(g) { | ||||
|                 delete groups[g.id] | ||||
|             }) | ||||
|             delete groupsByZ[id]; | ||||
|  | ||||
|             for (n=0;n<removedNodes.length;n++) { | ||||
|                 var result = removeNode(removedNodes[n].id); | ||||
|                 removedLinks = removedLinks.concat(result.links); | ||||
|             } | ||||
|             RED.events.emit('flows:remove',ws); | ||||
|         } | ||||
|         for (n=0;n<removedNodes.length;n++) { | ||||
|             var result = removeNode(removedNodes[n].id); | ||||
|             removedLinks = removedLinks.concat(result.links); | ||||
|         } | ||||
|         return {nodes:removedNodes,links:removedLinks}; | ||||
|  | ||||
|         return {nodes:removedNodes,links:removedLinks, groups: removedGroups}; | ||||
|     } | ||||
|  | ||||
|     function addSubflow(sf, createNewIds) { | ||||
| @@ -398,6 +431,10 @@ RED.nodes = (function() { | ||||
|             paletteLabel: function() { return RED.nodes.subflow(sf.id).name }, | ||||
|             inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null }, | ||||
|             outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null }, | ||||
|             oneditprepare: function() { | ||||
|                 RED.subflow.buildEditForm("subflow",this); | ||||
|                 RED.subflow.buildPropertiesForm(this); | ||||
|             }, | ||||
|             oneditresize: function(size) { | ||||
|                 // var rows = $(".dialog-form>div:not(.node-input-env-container-row)"); | ||||
|                 var height = size.height; | ||||
| @@ -413,14 +450,18 @@ RED.nodes = (function() { | ||||
|             } | ||||
|         }); | ||||
|         sf._def = RED.nodes.getType("subflow:"+sf.id); | ||||
|         RED.events.emit("subflows:add",sf); | ||||
|     } | ||||
|     function getSubflow(id) { | ||||
|         return subflows[id]; | ||||
|     } | ||||
|     function removeSubflow(sf) { | ||||
|         delete subflows[sf.id]; | ||||
|         delete nodeTabMap[sf.id]; | ||||
|         registry.removeNodeType("subflow:"+sf.id); | ||||
|         if (subflows[sf.id]) { | ||||
|             delete subflows[sf.id]; | ||||
|             delete nodeTabMap[sf.id]; | ||||
|             registry.removeNodeType("subflow:"+sf.id); | ||||
|             RED.events.emit("subflows:remove",sf); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function subflowContains(sfid,nodeid) { | ||||
| @@ -493,6 +534,9 @@ RED.nodes = (function() { | ||||
|         if (n.d === true) { | ||||
|             node.d = true; | ||||
|         } | ||||
|         if (n.g) { | ||||
|             node.g = n.g; | ||||
|         } | ||||
|         if (node.type == "unknown") { | ||||
|             for (var p in n._orig) { | ||||
|                 if (n._orig.hasOwnProperty(p)) { | ||||
| @@ -505,19 +549,33 @@ RED.nodes = (function() { | ||||
|                     node[d] = n[d]; | ||||
|                 } | ||||
|             } | ||||
|             if(exportCreds && n.credentials) { | ||||
|             if (exportCreds) { | ||||
|                 var credentialSet = {}; | ||||
|                 node.credentials = {}; | ||||
|                 for (var cred in n._def.credentials) { | ||||
|                     if (n._def.credentials.hasOwnProperty(cred)) { | ||||
|                         if (n._def.credentials[cred].type == 'password') { | ||||
|                 if (/^subflow:/.test(node.type) && n.credentials) { | ||||
|                     // A subflow instance node can have arbitrary creds | ||||
|                     for (var sfCred in n.credentials) { | ||||
|                         if (n.credentials.hasOwnProperty(sfCred)) { | ||||
|                             if (!n.credentials._ || | ||||
|                                 n.credentials["has_"+cred] != n.credentials._["has_"+cred] || | ||||
|                                 (n.credentials["has_"+cred] && n.credentials[cred])) { | ||||
|                                 n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] || | ||||
|                                 (n.credentials["has_"+sfCred] && n.credentials[sfCred])) { | ||||
|                                 credentialSet[sfCred] = n.credentials[sfCred]; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if (n.credentials) { | ||||
|                     node.credentials = {}; | ||||
|                     // All other nodes have a well-defined list of possible credentials | ||||
|                     for (var cred in n._def.credentials) { | ||||
|                         if (n._def.credentials.hasOwnProperty(cred)) { | ||||
|                             if (n._def.credentials[cred].type == 'password') { | ||||
|                                 if (!n.credentials._ || | ||||
|                                     n.credentials["has_"+cred] != n.credentials._["has_"+cred] || | ||||
|                                     (n.credentials["has_"+cred] && n.credentials[cred])) { | ||||
|                                     credentialSet[cred] = n.credentials[cred]; | ||||
|                                 } | ||||
|                             } else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) { | ||||
|                                 credentialSet[cred] = n.credentials[cred]; | ||||
|                             } | ||||
|                         } else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) { | ||||
|                             credentialSet[cred] = n.credentials[cred]; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -526,6 +584,13 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (n.type === "group") { | ||||
|             node.x = n.x; | ||||
|             node.y = n.y; | ||||
|             node.w = n.w; | ||||
|             node.h = n.h; | ||||
|             node.nodes = node.nodes.map(function(n) { return n.id }); | ||||
|         } | ||||
|         if (n._def.category != "config") { | ||||
|             node.x = n.x; | ||||
|             node.y = n.y; | ||||
| @@ -568,7 +633,7 @@ RED.nodes = (function() { | ||||
|         return node; | ||||
|     } | ||||
|  | ||||
|     function convertSubflow(n) { | ||||
|     function convertSubflow(n, exportCreds) { | ||||
|         var node = {}; | ||||
|         node.id = n.id; | ||||
|         node.type = n.type; | ||||
| @@ -578,6 +643,24 @@ RED.nodes = (function() { | ||||
|         node.in = []; | ||||
|         node.out = []; | ||||
|         node.env = n.env; | ||||
|  | ||||
|         if (exportCreds) { | ||||
|             var credentialSet = {}; | ||||
|             // A subflow node can have arbitrary creds | ||||
|             for (var sfCred in n.credentials) { | ||||
|                 if (n.credentials.hasOwnProperty(sfCred)) { | ||||
|                     if (!n.credentials._ || | ||||
|                         n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] || | ||||
|                         (n.credentials["has_"+sfCred] && n.credentials[sfCred])) { | ||||
|                         credentialSet[sfCred] = n.credentials[sfCred]; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (Object.keys(credentialSet).length > 0) { | ||||
|                 node.credentials = credentialSet; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         node.color = n.color; | ||||
|  | ||||
|         n.in.forEach(function(p) { | ||||
| @@ -633,8 +716,18 @@ RED.nodes = (function() { | ||||
|     /** | ||||
|      * Converts the current node selection to an exportable JSON Object | ||||
|      **/ | ||||
|     function createExportableNodeSet(set, exportedSubflows, exportedConfigNodes) { | ||||
|     function createExportableNodeSet(set, exportedIds, exportedSubflows, exportedConfigNodes) { | ||||
|         var nns = []; | ||||
|  | ||||
|         exportedIds = exportedIds || {}; | ||||
|         set = set.filter(function(n) { | ||||
|             if (exportedIds[n.id]) { | ||||
|                 return false; | ||||
|             } | ||||
|             exportedIds[n.id] = true; | ||||
|             return true; | ||||
|         }) | ||||
|  | ||||
|         exportedConfigNodes = exportedConfigNodes || {}; | ||||
|         exportedSubflows = exportedSubflows || {}; | ||||
|         for (var n=0;n<set.length;n++) { | ||||
| @@ -650,11 +743,11 @@ RED.nodes = (function() { | ||||
|                             subflowSet.push(n); | ||||
|                         } | ||||
|                     }); | ||||
|                     var exportableSubflow = createExportableNodeSet(subflowSet, exportedSubflows, exportedConfigNodes); | ||||
|                     var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes); | ||||
|                     nns = exportableSubflow.concat(nns); | ||||
|                 } | ||||
|             } | ||||
|             if (node.type != "subflow") { | ||||
|             if (node.type !== "subflow") { | ||||
|                 var convertedNode = RED.nodes.convertNode(node); | ||||
|                 for (var d in node._def.defaults) { | ||||
|                     if (node._def.defaults[d].type && node[d] in configNodes) { | ||||
| @@ -671,6 +764,9 @@ RED.nodes = (function() { | ||||
|                     } | ||||
|                 } | ||||
|                 nns.push(convertedNode); | ||||
|                 if (node.type === "group") { | ||||
|                     nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes)); | ||||
|                 } | ||||
|             } else { | ||||
|                 var convertedSubflow = convertSubflow(node); | ||||
|                 nns.push(convertedSubflow); | ||||
| @@ -693,7 +789,12 @@ RED.nodes = (function() { | ||||
|         } | ||||
|         for (i in subflows) { | ||||
|             if (subflows.hasOwnProperty(i)) { | ||||
|                 nns.push(convertSubflow(subflows[i])); | ||||
|                 nns.push(convertSubflow(subflows[i], exportCredentials)); | ||||
|             } | ||||
|         } | ||||
|         for (i in groups) { | ||||
|             if (groups.hasOwnProperty(i)) { | ||||
|                 nns.push(convertNode(groups[i])); | ||||
|             } | ||||
|         } | ||||
|         for (i in configNodes) { | ||||
| @@ -822,6 +923,7 @@ RED.nodes = (function() { | ||||
|             if (n.type != "workspace" && | ||||
|                 n.type != "tab" && | ||||
|                 n.type != "subflow" && | ||||
|                 n.type != "group" && | ||||
|                 !registry.getNodeType(n.type) && | ||||
|                 n.type.substring(0,8) != "subflow:" && | ||||
|                 unknownTypes.indexOf(n.type)==-1) { | ||||
| @@ -871,6 +973,7 @@ RED.nodes = (function() { | ||||
|         var node_map = {}; | ||||
|         var new_nodes = []; | ||||
|         var new_links = []; | ||||
|         var new_groups = []; | ||||
|         var nid; | ||||
|         var def; | ||||
|         var configNode; | ||||
| @@ -1021,7 +1124,6 @@ RED.nodes = (function() { | ||||
|                     } | ||||
|                     node_map[n.id] = configNode; | ||||
|                     new_nodes.push(configNode); | ||||
|                     RED.nodes.add(configNode); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -1038,20 +1140,25 @@ RED.nodes = (function() { | ||||
|                         y:parseFloat(n.y || 0), | ||||
|                         z:n.z, | ||||
|                         type:0, | ||||
|                         wires:n.wires||[], | ||||
|                         inputLabels: n.inputLabels, | ||||
|                         outputLabels: n.outputLabels, | ||||
|                         icon: n.icon, | ||||
|                         info: n.info, | ||||
|                         changed:false, | ||||
|                         _config:{} | ||||
|                     }; | ||||
|                     } | ||||
|                     if (n.type !== "group") { | ||||
|                         node.wires = n.wires||[]; | ||||
|                         node.inputLabels = n.inputLabels; | ||||
|                         node.outputLabels = n.outputLabels; | ||||
|                         node.icon = n.icon; | ||||
|                     } | ||||
|                     if (n.hasOwnProperty('l')) { | ||||
|                         node.l = n.l; | ||||
|                     } | ||||
|                     if (n.hasOwnProperty('d')) { | ||||
|                         node.d = n.d; | ||||
|                     } | ||||
|                     if (n.hasOwnProperty('g')) { | ||||
|                         node.g = n.g; | ||||
|                     } | ||||
|                     if (createNewIds) { | ||||
|                         if (subflow_blacklist[n.z]) { | ||||
|                             continue; | ||||
| @@ -1088,7 +1195,17 @@ RED.nodes = (function() { | ||||
|                     } | ||||
|                     node.type = n.type; | ||||
|                     node._def = def; | ||||
|                     if (n.type.substring(0,7) === "subflow") { | ||||
|                     if (node.type === "group") { | ||||
|                         node._def = RED.group.def; | ||||
|                         for (d in node._def.defaults) { | ||||
|                             if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') { | ||||
|                                 node[d] = n[d]; | ||||
|                                 node._config[d] = JSON.stringify(n[d]); | ||||
|                             } | ||||
|                         } | ||||
|                         node._config.x = node.x; | ||||
|                         node._config.y = node.y; | ||||
|                     } else if (n.type.substring(0,7) === "subflow") { | ||||
|                         var parentId = n.type.split(":")[1]; | ||||
|                         var subflow = subflow_blacklist[parentId]||subflow_map[parentId]||getSubflow(parentId); | ||||
|                         if (createNewIds) { | ||||
| @@ -1178,13 +1295,13 @@ RED.nodes = (function() { | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     addNode(node); | ||||
|                     RED.editor.validateNode(node); | ||||
|                     node_map[n.id] = node; | ||||
|                     // If an 'unknown' config node, it will not have been caught by the | ||||
|                     // proper config node handling, so needs adding to new_nodes here | ||||
|                     if (node.type === "unknown" || node._def.category !== "config") { | ||||
|                         new_nodes.push(node); | ||||
|                     } else if (node.type === "group") { | ||||
|                         new_groups.push(node); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -1219,6 +1336,11 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|                 delete n.wires; | ||||
|             } | ||||
|             if (n.g && node_map[n.g]) { | ||||
|                 n.g = node_map[n.g].id; | ||||
|             } else { | ||||
|                 delete n.g | ||||
|             } | ||||
|             for (var d3 in n._def.defaults) { | ||||
|                 if (n._def.defaults.hasOwnProperty(d3)) { | ||||
|                     if (n._def.defaults[d3].type && node_map[n[d3]]) { | ||||
| @@ -1287,9 +1409,27 @@ RED.nodes = (function() { | ||||
|                 delete n.status.wires; | ||||
|             } | ||||
|         } | ||||
|         for (i=0;i<new_groups.length;i++) { | ||||
|             n = new_groups[i]; | ||||
|             if (n.g && node_map[n.g]) { | ||||
|                 n.g = node_map[n.g].id; | ||||
|             } else { | ||||
|                 delete n.g; | ||||
|             } | ||||
|             n.nodes = n.nodes.map(function(id) { | ||||
|                 return node_map[id]; | ||||
|             }) | ||||
|             addGroup(n); | ||||
|         } | ||||
|         // Now the nodes have been fully updated, add them. | ||||
|         for (i=0;i<new_nodes.length;i++) { | ||||
|             var node = new_nodes[i]; | ||||
|             addNode(node); | ||||
|             RED.editor.validateNode(node); | ||||
|         } | ||||
|  | ||||
|         RED.workspaces.refresh(); | ||||
|         return [new_nodes,new_links,new_workspaces,new_subflows,missingWorkspace]; | ||||
|         return [new_nodes,new_links,new_groups,new_workspaces,new_subflows,missingWorkspace]; | ||||
|     } | ||||
|  | ||||
|     // TODO: supports filter.z|type | ||||
| @@ -1380,6 +1520,9 @@ RED.nodes = (function() { | ||||
|         nodeTabMap = {}; | ||||
|         configNodes = {}; | ||||
|         workspacesOrder = []; | ||||
|         groups = {}; | ||||
|         groupsByZ = {}; | ||||
|  | ||||
|         var subflowIds = Object.keys(subflows); | ||||
|         subflowIds.forEach(function(id) { | ||||
|             RED.subflow.removeSubflow(id) | ||||
| @@ -1397,6 +1540,8 @@ RED.nodes = (function() { | ||||
|         RED.sidebar.config.refresh(); | ||||
|         RED.sidebar.info.refresh(); | ||||
|  | ||||
|         RED.events.emit("workspace:clear"); | ||||
|  | ||||
|         // var node_defs = {}; | ||||
|         // var nodes = []; | ||||
|         // var configNodes = {}; | ||||
| @@ -1408,6 +1553,29 @@ RED.nodes = (function() { | ||||
|         // var loadedFlowVersion = null; | ||||
|     } | ||||
|  | ||||
|     function addGroup(group) { | ||||
|         groupsByZ[group.z] = groupsByZ[group.z] || []; | ||||
|         groupsByZ[group.z].push(group); | ||||
|         groups[group.id] = group; | ||||
|         RED.events.emit("groups:add",group); | ||||
|     } | ||||
|     function removeGroup(group) { | ||||
|         var i = groupsByZ[group.z].indexOf(group); | ||||
|         groupsByZ[group.z].splice(i,1); | ||||
|  | ||||
|         if (group.g) { | ||||
|             if (groups[group.g]) { | ||||
|                 var index = groups[group.g].nodes.indexOf(group); | ||||
|                 groups[group.g].nodes.splice(index,1); | ||||
|             } | ||||
|         } | ||||
|         RED.group.markDirty(group); | ||||
|  | ||||
|         delete groups[group.id]; | ||||
|         RED.events.emit("groups:remove",group); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             RED.events.on("registry:node-type-added",function(type) { | ||||
| @@ -1451,8 +1619,9 @@ RED.nodes = (function() { | ||||
|                     }); | ||||
|                     removeLinks.forEach(removeLink); | ||||
|  | ||||
|  | ||||
|                     RED.view.redraw(true); | ||||
|                     // Force the redraw to be synchronous so the view updates | ||||
|                     // *now* and removes the unknown node | ||||
|                     RED.view.redraw(true, true); | ||||
|                     var result = importNodes(reimportList,false); | ||||
|                     var newNodeMap = {}; | ||||
|                     result[0].forEach(function(n) { | ||||
| @@ -1506,6 +1675,11 @@ RED.nodes = (function() { | ||||
|         subflow: getSubflow, | ||||
|         subflowContains: subflowContains, | ||||
|  | ||||
|         addGroup: addGroup, | ||||
|         removeGroup: removeGroup, | ||||
|         group: function(id) { return groups[id] }, | ||||
|         groups: function(z) { return groupsByZ[z]||[] }, | ||||
|  | ||||
|         eachNode: function(cb) { | ||||
|             for (var n=0;n<nodes.length;n++) { | ||||
|                 if (cb(nodes[n]) === false) { | ||||
|   | ||||
							
								
								
									
										33
									
								
								packages/node_modules/@node-red/editor-client/src/js/polyfills.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								packages/node_modules/@node-red/editor-client/src/js/polyfills.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| (function() { | ||||
|     var isIE11 = !!window.MSInputMethodContext && !!document.documentMode; | ||||
|  | ||||
|     if (isIE11) { | ||||
|         // IE11 does not provide classList on SVGElements | ||||
|         if (! ("classList" in SVGElement.prototype)) { | ||||
|             Object.defineProperty(SVGElement.prototype, 'classList', Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'classList')); | ||||
|         } | ||||
|  | ||||
|         // IE11 does not provide children on SVGElements | ||||
|         if (! ("children" in SVGElement.prototype)) { | ||||
|             Object.defineProperty(SVGElement.prototype, 'children', Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'children')); | ||||
|         } | ||||
|  | ||||
|         Array.from = function() { | ||||
|             if (arguments.length > 1) { | ||||
|                 throw new Error("Node-RED's IE11 Array.from polyfill doesn't support multiple arguments"); | ||||
|             } | ||||
|             var arrayLike = arguments[0] | ||||
|             var result = []; | ||||
|             if (arrayLike.forEach) { | ||||
|                 arrayLike.forEach(function(i) { | ||||
|                     result.push(i); | ||||
|                 }) | ||||
|             } else { | ||||
|                 for (var i=0;i<arrayLike.length;i++) { | ||||
|                     result.push(arrayList[i]); | ||||
|                 } | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
| @@ -75,6 +75,7 @@ var RED = (function() { | ||||
|     } | ||||
|  | ||||
|     function loadNodeList() { | ||||
|         loader.reportProgress(RED._("event.loadPalette"), 20) | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept":"application/json" | ||||
| @@ -83,6 +84,7 @@ var RED = (function() { | ||||
|             url: 'nodes', | ||||
|             success: function(data) { | ||||
|                 RED.nodes.setNodeList(data); | ||||
|                 loader.reportProgress(RED._("event.loadNodeCatalogs"), 25) | ||||
|                 RED.i18n.loadNodeCatalogs(function() { | ||||
|                     loadIconList(loadNodes); | ||||
|                 }); | ||||
| @@ -107,6 +109,7 @@ var RED = (function() { | ||||
|     } | ||||
|  | ||||
|     function loadNodes() { | ||||
|         loader.reportProgress(RED._("event.loadNodes",{count:""}), 30) | ||||
|         var lang = localStorage.getItem("editor-language")||i18n.detectLanguage(); | ||||
|  | ||||
|         $.ajax({ | ||||
| @@ -118,15 +121,19 @@ var RED = (function() { | ||||
|             url: 'nodes', | ||||
|             success: function(data) { | ||||
|                 var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/); | ||||
|                 var totalCount = configs.length; | ||||
|  | ||||
|                 var stepConfig = function() { | ||||
|                     loader.reportProgress(RED._("event.loadNodes",{count:(totalCount-configs.length)+"/"+totalCount}), 30 + ((totalCount-configs.length)/totalCount)*40 ) | ||||
|  | ||||
|                     if (configs.length === 0) { | ||||
|                         $("#red-ui-editor").i18n(); | ||||
|                         $("#red-ui-palette > .red-ui-palette-spinner").hide(); | ||||
|                         $(".red-ui-palette-scroll").removeClass("hide"); | ||||
|                         $("#red-ui-palette-search").removeClass("hide"); | ||||
|                         loadFlows(function() { | ||||
|                             if (RED.settings.theme("projects.enabled",false)) { | ||||
|                                 RED.projects.refresh(function(activeProject) { | ||||
|                         if (RED.settings.theme("projects.enabled",false)) { | ||||
|                             RED.projects.refresh(function(activeProject) { | ||||
|                                 loadFlows(function() { | ||||
|                                     RED.sidebar.info.refresh() | ||||
|                                     if (!activeProject) { | ||||
|                                         // Projects enabled but no active project | ||||
| @@ -140,12 +147,14 @@ var RED = (function() { | ||||
|                                     } | ||||
|                                     completeLoad(); | ||||
|                                 }); | ||||
|                             } else { | ||||
|                             }); | ||||
|                         } else { | ||||
|                             loadFlows(function() { | ||||
|                                 // Projects disabled by the user | ||||
|                                 RED.sidebar.info.refresh() | ||||
|                                 completeLoad(); | ||||
|                             } | ||||
|                         }); | ||||
|                             }); | ||||
|                         } | ||||
|                     } else { | ||||
|                         var config = configs.shift(); | ||||
|                         appendNodeConfig(config,stepConfig); | ||||
| @@ -157,6 +166,7 @@ var RED = (function() { | ||||
|     } | ||||
|  | ||||
|     function loadFlows(done) { | ||||
|         loader.reportProgress(RED._("event.loadFlows"),80 ) | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept":"application/json", | ||||
| @@ -167,6 +177,7 @@ var RED = (function() { | ||||
|                 if (nodes) { | ||||
|                     var currentHash = window.location.hash; | ||||
|                     RED.nodes.version(nodes.rev); | ||||
|                     loader.reportProgress(RED._("event.importFlows"),90 ) | ||||
|                     RED.nodes.import(nodes.flows); | ||||
|                     RED.nodes.dirty(false); | ||||
|                     RED.view.redraw(true); | ||||
| @@ -193,6 +204,7 @@ var RED = (function() { | ||||
|                 return; | ||||
|             } | ||||
|             if (notificationId === "project-update") { | ||||
|                 loader.start("Loading project",0) | ||||
|                 RED.nodes.clear(); | ||||
|                 RED.history.clear(); | ||||
|                 RED.view.redraw(true); | ||||
| @@ -208,6 +220,7 @@ var RED = (function() { | ||||
|                             "revert": RED._("notification.project.revert", {project: msg.project}), | ||||
|                             "merge-complete": RED._("notification.project.merge-complete") | ||||
|                         }[msg.action]; | ||||
|                         loader.end() | ||||
|                         RED.notify("<p>"+message+"</p>"); | ||||
|                         RED.sidebar.info.refresh() | ||||
|                     }); | ||||
| @@ -423,6 +436,12 @@ var RED = (function() { | ||||
|             var id = topic.substring(9); | ||||
|             RED.eventLog.log(id,payload); | ||||
|         }); | ||||
|  | ||||
|         $(".red-ui-header-toolbar").show(); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|             loader.end(); | ||||
|         },100); | ||||
|     } | ||||
|  | ||||
|     function showAbout() { | ||||
| @@ -431,8 +450,7 @@ var RED = (function() { | ||||
|             '<img width="50px" src="red/images/node-red-icon.svg" />'+ | ||||
|             '</div>'; | ||||
|  | ||||
|             RED.sidebar.info.set(aboutHeader+RED.utils.renderMarkdown(data)); | ||||
|             RED.sidebar.info.show(); | ||||
|             RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -472,6 +490,14 @@ var RED = (function() { | ||||
|             {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"}, | ||||
|             {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"}, | ||||
|         ]}); | ||||
|         menuOptions.push({id:"menu-item-group",label:RED._("menu.label.groups"), options: [ | ||||
|             {id:"menu-item-group-group",label:RED._("menu.label.groupSelection"),disabled:true,onselect:"core:group-selection"}, | ||||
|             {id:"menu-item-group-ungroup",label:RED._("menu.label.ungroupSelection"),disabled:true,onselect:"core:ungroup-selection"}, | ||||
|             null, | ||||
|             {id:"menu-item-group-merge",label:RED._("menu.label.groupMergeSelection"),disabled:true,onselect:"core:merge-selection-to-group"}, | ||||
|             {id:"menu-item-group-remove",label:RED._("menu.label.groupRemoveSelection"),disabled:true,onselect:"core:remove-selection-from-group"} | ||||
|         ]}); | ||||
|  | ||||
|         menuOptions.push(null); | ||||
|         if (RED.settings.theme('palette.editable') !== false) { | ||||
|             menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"}); | ||||
| @@ -497,7 +523,6 @@ var RED = (function() { | ||||
|     } | ||||
|  | ||||
|     function loadEditor() { | ||||
|  | ||||
|         RED.workspaces.init(); | ||||
|         RED.statusBar.init(); | ||||
|         RED.view.init(); | ||||
| @@ -524,6 +549,7 @@ var RED = (function() { | ||||
|         } | ||||
|  | ||||
|         RED.subflow.init(); | ||||
|         RED.group.init(); | ||||
|         RED.clipboard.init(); | ||||
|         RED.search.init(); | ||||
|         RED.actionList.init(); | ||||
| @@ -539,13 +565,14 @@ var RED = (function() { | ||||
|         RED.comms.connect(); | ||||
|  | ||||
|         $("#red-ui-main-container").show(); | ||||
|         $(".red-ui-header-toolbar").show(); | ||||
|  | ||||
|  | ||||
|         RED.actions.add("core:show-about", showAbout); | ||||
|  | ||||
|         loadNodeList(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function buildEditor(options) { | ||||
|         var header = $('<div id="red-ui-header"></div>').appendTo(options.target); | ||||
|         var logo = $('<span class="red-ui-header-logo"></span>').appendTo(header); | ||||
| @@ -560,6 +587,10 @@ var RED = (function() { | ||||
|         '</div>').appendTo(options.target); | ||||
|         $('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target); | ||||
|         $('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target); | ||||
|  | ||||
|         loader.init().appendTo("#red-ui-main-container"); | ||||
|         loader.start("...",0); | ||||
|  | ||||
|         $.getJSON(options.apiRootUrl+"theme", function(theme) { | ||||
|             if (theme.header) { | ||||
|                 if (theme.header.url) { | ||||
| @@ -592,12 +623,39 @@ var RED = (function() { | ||||
|         options.target.addClass("red-ui-editor"); | ||||
|  | ||||
|         buildEditor(options); | ||||
|  | ||||
|         RED.i18n.init(options, function() { | ||||
|             RED.settings.init(options, loadEditor); | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     var loader = { | ||||
|         init: function() { | ||||
|             var wrapper = $('<div id="red-ui-loading-progress"></div>').hide(); | ||||
|             var container = $('<div>').appendTo(wrapper); | ||||
|             var label = $('<div>',{class:"red-ui-loading-bar-label"}).appendTo(container); | ||||
|             var bar = $('<div>',{class:"red-ui-loading-bar"}).appendTo(container); | ||||
|             var fill =$('<span>').appendTo(bar); | ||||
|             return wrapper; | ||||
|         }, | ||||
|         start: function(text, prcnt) { | ||||
|             if (text) { | ||||
|                 loader.reportProgress(text,prcnt) | ||||
|             } | ||||
|             $("#red-ui-loading-progress").show(); | ||||
|         }, | ||||
|         reportProgress: function(text, prcnt) { | ||||
|             $(".red-ui-loading-bar-label").text(text); | ||||
|             $(".red-ui-loading-bar span").width(prcnt+"%") | ||||
|         }, | ||||
|         end: function() { | ||||
|             $("#red-ui-loading-progress").hide(); | ||||
|             loader.reportProgress("",0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init | ||||
|         init: init, | ||||
|         loader: loader | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -184,7 +184,7 @@ RED.clipboard = (function() { | ||||
|                 '</div>'+ | ||||
|                 '<div id="red-ui-clipboard-dialog-export-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+ | ||||
|                     '<div id="red-ui-clipboard-dialog-export-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+ | ||||
|                         '<div class="form-row">'+ | ||||
|                         '<div class="form-row" style="height:calc(100% - 30px)">'+ | ||||
|                             '<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+ | ||||
|                         '</div>'+ | ||||
|                         '<div class="form-row" style="text-align: right;">'+ | ||||
| @@ -216,7 +216,7 @@ RED.clipboard = (function() { | ||||
|                             ' <a class="red-ui-button" id="red-ui-clipboard-dialog-import-file-upload-btn"><i class="fa fa-upload"></i> <span data-i18n="clipboard.selectFile"></span></a>'+ | ||||
|                             '<input type="file" id="red-ui-clipboard-dialog-import-file-upload" accept=".json" style="display:none">'+ | ||||
|                         '</div>'+ | ||||
|                         '<div class="form-row">'+ | ||||
|                         '<div class="form-row" style="height:calc(100% - 47px)">'+ | ||||
|                             '<textarea id="red-ui-clipboard-dialog-import-text"></textarea>'+ | ||||
|                         '</div>'+ | ||||
|                     '</div>'+ | ||||
| @@ -474,6 +474,12 @@ RED.clipboard = (function() { | ||||
|             },100) | ||||
|         } | ||||
|  | ||||
|         var dialogHeight = 400; | ||||
|         var winHeight = $(window).height(); | ||||
|         if (winHeight < 600) { | ||||
|             dialogHeight = 400 - (600 - winHeight); | ||||
|         } | ||||
|         $(".red-ui-clipboard-dialog-box").height(dialogHeight); | ||||
|  | ||||
|         dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open"); | ||||
|         popover = RED.popover.create({ | ||||
| @@ -583,6 +589,7 @@ RED.clipboard = (function() { | ||||
|                     nodes = []; | ||||
|                     selection.forEach(function(n) { | ||||
|                         nodes.push(n); | ||||
|                         nodes = nodes.concat(RED.nodes.groups(n.id)); | ||||
|                         nodes = nodes.concat(RED.nodes.filterNodes({z:n.id})); | ||||
|                     }); | ||||
|                 } else { | ||||
| @@ -592,7 +599,8 @@ RED.clipboard = (function() { | ||||
|                 nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'})); | ||||
|             } else if (type === 'red-ui-clipboard-dialog-export-rng-flow') { | ||||
|                 var activeWorkspace = RED.workspaces.active(); | ||||
|                 nodes = RED.nodes.filterNodes({z:activeWorkspace}); | ||||
|                 nodes = RED.nodes.groups(activeWorkspace); | ||||
|                 nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace})); | ||||
|                 var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace); | ||||
|                 nodes.unshift(parentNode); | ||||
|                 nodes = RED.nodes.createExportableNodeSet(nodes); | ||||
| @@ -637,6 +645,14 @@ RED.clipboard = (function() { | ||||
|             $("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click"); | ||||
|         } | ||||
|         tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode); | ||||
|  | ||||
|         var dialogHeight = 400; | ||||
|         var winHeight = $(window).height(); | ||||
|         if (winHeight < 600) { | ||||
|             dialogHeight = 400 - (600 - winHeight); | ||||
|         } | ||||
|         $(".red-ui-clipboard-dialog-box").height(dialogHeight); | ||||
|  | ||||
|         dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" ); | ||||
|  | ||||
|         $("#red-ui-clipboard-dialog-export-text").trigger("focus"); | ||||
| @@ -738,6 +754,8 @@ RED.clipboard = (function() { | ||||
|             RED.actions.add("core:show-library-export-dialog",function() { exportNodes('library') }); | ||||
|             RED.actions.add("core:show-library-import-dialog",function() { importNodes('library') }); | ||||
|  | ||||
|             RED.actions.add("core:show-examples-import-dialog",function() { importNodes('examples') }); | ||||
|  | ||||
|             RED.events.on("editor:open",function() { disabled = true; }); | ||||
|             RED.events.on("editor:close",function() { disabled = false; }); | ||||
|             RED.events.on("search:open",function() { disabled = true; }); | ||||
|   | ||||
							
								
								
									
										203
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | ||||
| RED.colorPicker = (function() { | ||||
|  | ||||
|     function create(options) { | ||||
|         var color = options.value; | ||||
|         var id = options.id; | ||||
|         var colorPalette = options.palette || []; | ||||
|         var width = options.cellWidth || 30; | ||||
|         var height = options.cellHeight || 30; | ||||
|         var margin = options.cellMargin || 2; | ||||
|         var perRow = options.cellPerRow || 6; | ||||
|  | ||||
|         var container = $("<div>",{style:"display:inline-block"}); | ||||
|         var colorHiddenInput = $("<input/>", { id: id, type: "hidden", value: color }).appendTo(container); | ||||
|         var opacityHiddenInput = $("<input/>", { id: id+"-opacity", type: "hidden", value: options.hasOwnProperty('opacity')?options.opacity:"1" }).appendTo(container); | ||||
|  | ||||
|         var colorButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(container); | ||||
|         $('<i class="fa fa-caret-down"></i>').appendTo(colorButton); | ||||
|  | ||||
|         var colorDispContainer = $('<div>',{class:"red-ui-search-result-node"}).appendTo(colorButton); | ||||
|         $('<div>',{class:"red-ui-color-picker-cell-none"}).appendTo(colorDispContainer); | ||||
|         var colorDisp = $('<div>',{class:"red-ui-color-picker-swatch"}).appendTo(colorDispContainer); | ||||
|  | ||||
|  | ||||
|         var refreshDisplay = function(color) { | ||||
|             if (color === "none") { | ||||
|                 colorDisp.addClass('red-ui-color-picker-cell-none').css({ | ||||
|                     "background-color": "", | ||||
|                     opacity: 1 | ||||
|                 }); | ||||
|                 colorDispContainer.css({ | ||||
|                     "border-color":"" | ||||
|                 }) | ||||
|             } else { | ||||
|                 var opacity = parseFloat(opacityHiddenInput.val()) | ||||
|                 colorDisp.removeClass('red-ui-color-picker-cell-none').css({ | ||||
|                     "background-color": color, | ||||
|                     "opacity": opacity | ||||
|                 }); | ||||
|                 var border = RED.utils.getDarkerColor(color); | ||||
|                 if (border[0] === '#') { | ||||
|                     border += Math.round(255*Math.floor(opacity*100)/100).toString(16); | ||||
|                 } else { | ||||
|                     border = ""; | ||||
|                 } | ||||
|  | ||||
|                 colorDispContainer.css({ | ||||
|                     "border-color": border | ||||
|                 }) | ||||
|             } | ||||
|             if (options.hasOwnProperty('opacity')) { | ||||
|                 $(".red-ui-color-picker-opacity-slider-overlay").css({ | ||||
|                     "background-image": "linear-gradient(90deg, transparent 0%, "+color+" 100%)" | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|         colorButton.on("click", function (e) { | ||||
|             var numColors = colorPalette.length; | ||||
|  | ||||
|             var picker = $("<div/>", { | ||||
|                 class: "red-ui-color-picker" | ||||
|             }).css({ | ||||
|                 width: ((width+margin+margin)*perRow)+"px", | ||||
|                 height: Math.ceil(numColors/perRow)*(height+margin+margin)+"+px" | ||||
|             }); | ||||
|             var count = 0; | ||||
|             var row = null; | ||||
|             row = $("<div/>").appendTo(picker); | ||||
|  | ||||
|             var colorInput = $('<input>',{ | ||||
|                 type:"text", | ||||
|                 value:colorHiddenInput.val() | ||||
|             }).appendTo(row); | ||||
|  | ||||
|             colorInput.on("change", function (e) { | ||||
|                 var color = colorInput.val(); | ||||
|                 colorHiddenInput.val(color).trigger('change'); | ||||
|                 refreshDisplay(color); | ||||
|             }); | ||||
|             // if (options.hasOwnProperty('opacity')) { | ||||
|             //     var sliderContainer = $("<div>",{class:"red-ui-color-picker-opacity-slider" | ||||
|             // } | ||||
|  | ||||
|             if (options.none) { | ||||
|                 row = $("<div/>").appendTo(picker); | ||||
|                 var button = $("<button/>", { | ||||
|                     class:"red-ui-color-picker-cell red-ui-color-picker-cell-none" | ||||
|                 }).css({ | ||||
|                     width: width+"px", | ||||
|                     height: height+"px", | ||||
|                     margin: margin+"px" | ||||
|                 }).appendTo(row); | ||||
|                 button.on("click",  function (e) { | ||||
|                     e.preventDefault(); | ||||
|                     colorInput.val("none"); | ||||
|                     colorInput.trigger("change"); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             colorPalette.forEach(function (col) { | ||||
|                 if ((count % perRow) == 0) { | ||||
|                     row = $("<div/>").appendTo(picker); | ||||
|                 } | ||||
|                 var button = $("<button/>", { | ||||
|                     class:"red-ui-color-picker-cell" | ||||
|                 }).css({ | ||||
|                     width: width+"px", | ||||
|                     height: height+"px", | ||||
|                     margin: margin+"px", | ||||
|                     backgroundColor: col, | ||||
|                     "border-color": RED.utils.getDarkerColor(col) | ||||
|                 }).appendTo(row); | ||||
|                 button.on("click",  function (e) { | ||||
|                     e.preventDefault(); | ||||
|                     // colorPanel.hide(); | ||||
|                     colorInput.val(col); | ||||
|                     colorInput.trigger("change"); | ||||
|                 }); | ||||
|                 count++; | ||||
|             }); | ||||
|             if (options.none || options.hasOwnProperty('opacity')) { | ||||
|                 row = $("<div/>").appendTo(picker); | ||||
|                 // if (options.none) { | ||||
|                 //     var button = $("<button/>", { | ||||
|                 //         class:"red-ui-color-picker-cell red-ui-color-picker-cell-none" | ||||
|                 //     }).css({ | ||||
|                 //         width: width+"px", | ||||
|                 //         height: height+"px", | ||||
|                 //         margin: margin+"px" | ||||
|                 //     }).appendTo(row); | ||||
|                 //     button.on("click",  function (e) { | ||||
|                 //         e.preventDefault(); | ||||
|                 //         colorPanel.hide(); | ||||
|                 //         selector.val("none"); | ||||
|                 //         selector.trigger("change"); | ||||
|                 //     }); | ||||
|                 // } | ||||
|                 if (options.hasOwnProperty('opacity')) { | ||||
|                     var sliderContainer = $("<div>",{class:"red-ui-color-picker-opacity-slider"}).appendTo(row); | ||||
|                     sliderContainer.on("mousedown", function(evt) { | ||||
|                         if (evt.target === sliderHandle[0]) { | ||||
|                             return; | ||||
|                         } | ||||
|                         var v = evt.offsetX/sliderContainer.width(); | ||||
|                         sliderHandle.css({ | ||||
|                             left: ( v*(sliderContainer.width() - sliderHandle.outerWidth()))+"px" | ||||
|                         }); | ||||
|                         v = Math.floor(100*v) | ||||
|                         opacityHiddenInput.val(v/100) | ||||
|                         opacityLabel.text(v+"%"); | ||||
|                         refreshDisplay(colorHiddenInput.val()); | ||||
|                     }) | ||||
|                      $("<div>",{class:"red-ui-color-picker-opacity-slider-overlay"}).appendTo(sliderContainer); | ||||
|                     var sliderHandle = $("<div>",{class:"red-ui-color-picker-opacity-slider-handle red-ui-button red-ui-button-small"}).appendTo(sliderContainer).draggable({ | ||||
|                         containment: "parent", | ||||
|                         axis: "x", | ||||
|                         drag: function( event, ui ) { | ||||
|                             var v = Math.max(0,ui.position.left/($(this).parent().width()-$(this).outerWidth())); | ||||
|                             // Odd bug that if it is loaded with a non-0 value, the first time | ||||
|                             // it is dragged it ranges -1 to 99. But every other time, its 0 to 100. | ||||
|                             // The Math.max above makes the -1 disappear. The follow hack ensures | ||||
|                             // it always maxes out at a 100, at the cost of not allowing 99% exactly. | ||||
|                             v = Math.floor(100*v) | ||||
|                             if ( v === 99 ) { | ||||
|                                 v = 100; | ||||
|                             } | ||||
|                             // console.log("uip",ui.position.left); | ||||
|                             opacityHiddenInput.val(v/100) | ||||
|                             opacityLabel.text(v+"%"); | ||||
|                             refreshDisplay(colorHiddenInput.val()); | ||||
|                         } | ||||
|                     }); | ||||
|                     var opacityLabel = $('<small></small>').appendTo(row); | ||||
|                     setTimeout(function() { | ||||
|                         sliderHandle.css({ | ||||
|                             left: (parseFloat(opacityHiddenInput.val())*(sliderContainer.width() - sliderHandle.outerWidth()))+"px" | ||||
|                         }) | ||||
|                         opacityLabel.text(Math.floor(opacityHiddenInput.val()*100)+"%"); | ||||
|                     },50); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var colorPanel = RED.popover.panel(picker); | ||||
|             setTimeout(function() { | ||||
|                 refreshDisplay(colorHiddenInput.val()) | ||||
|             },50); | ||||
|             colorPanel.show({ | ||||
|                 target: colorButton | ||||
|             }) | ||||
|         }); | ||||
|         setTimeout(function() { | ||||
|             refreshDisplay(colorHiddenInput.val()) | ||||
|         },50); | ||||
|         return container; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         create: create | ||||
|     } | ||||
| })(); | ||||
| @@ -183,7 +183,7 @@ | ||||
|             if (this.options.resizeItem) { | ||||
|                 var that = this; | ||||
|                 this.element.children().each(function(i) { | ||||
|                     that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i); | ||||
|                     that.options.resizeItem($(this).children(".red-ui-editableList-item-content"),i); | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
| @@ -223,7 +223,7 @@ | ||||
|                 var items = this.element.children(); | ||||
|                 var that = this; | ||||
|                 items.sort(function(A,B) { | ||||
|                     return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data')); | ||||
|                     return that.activeSort($(A).children(".red-ui-editableList-item-content").data('data'),$(B).children(".red-ui-editableList-item-content").data('data')); | ||||
|                 }); | ||||
|                 $.each(items,function(idx,li) { | ||||
|                     that.element.append(li); | ||||
| @@ -305,7 +305,7 @@ | ||||
|             } | ||||
|             if (this.options.addItem) { | ||||
|                 var index = that.element.children().length-1; | ||||
|                 setTimeout(function() { | ||||
|                 // setTimeout(function() { | ||||
|                     that.options.addItem(row,index,data); | ||||
|                     if (that.activeFilter) { | ||||
|                         try { | ||||
| @@ -321,7 +321,7 @@ | ||||
|                             that.uiContainer.scrollTop(that.element.height()); | ||||
|                         },0); | ||||
|                     } | ||||
|                 },0); | ||||
|                 // },0); | ||||
|             } | ||||
|         }, | ||||
|         addItem: function(data) { | ||||
| @@ -334,7 +334,7 @@ | ||||
|         }, | ||||
|         removeItem: function(data) { | ||||
|             var items = this.element.children().filter(function(f) { | ||||
|                 return data === $(this).find(".red-ui-editableList-item-content").data('data'); | ||||
|                 return data === $(this).children(".red-ui-editableList-item-content").data('data'); | ||||
|             }); | ||||
|             items.remove(); | ||||
|             if (this.options.removeItem) { | ||||
| @@ -342,7 +342,7 @@ | ||||
|             } | ||||
|         }, | ||||
|         items: function() { | ||||
|             return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); }); | ||||
|             return this.element.children().map(function(i) { return $(this).children(".red-ui-editableList-item-content"); }); | ||||
|         }, | ||||
|         empty: function() { | ||||
|             this.element.empty(); | ||||
| @@ -365,14 +365,14 @@ | ||||
|         }, | ||||
|         show: function(item) { | ||||
|             var items = this.element.children().filter(function(f) { | ||||
|                 return item === $(this).find(".red-ui-editableList-item-content").data('data'); | ||||
|                 return item === $(this).children(".red-ui-editableList-item-content").data('data'); | ||||
|             }); | ||||
|             if (items.length > 0) { | ||||
|                 this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top) | ||||
|             } | ||||
|         }, | ||||
|         getItem: function(li) { | ||||
|             var el = li.find(".red-ui-editableList-item-content"); | ||||
|             var el = li.children(".red-ui-editableList-item-content"); | ||||
|             if (el.length) { | ||||
|                 return el.data('data'); | ||||
|             } else { | ||||
|   | ||||
| @@ -29,6 +29,10 @@ RED.panels = (function() { | ||||
|         if (!vertical) { | ||||
|             container.addClass("red-ui-panels-horizontal"); | ||||
|         } | ||||
|  | ||||
|         $(children[0]).addClass("red-ui-panel"); | ||||
|         $(children[1]).addClass("red-ui-panel"); | ||||
|  | ||||
|         var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]); | ||||
|         var startPosition; | ||||
|         var panelSizes = []; | ||||
| @@ -52,11 +56,11 @@ RED.panels = (function() { | ||||
|                     var newSizes = [panelSizes[0]+delta,panelSizes[1]-delta]; | ||||
|                     if (vertical) { | ||||
|                         $(children[0]).height(newSizes[0]); | ||||
|                         $(children[1]).height(newSizes[1]); | ||||
|                         // $(children[1]).height(newSizes[1]); | ||||
|                         ui.position.top -= delta; | ||||
|                     } else { | ||||
|                         $(children[0]).width(newSizes[0]); | ||||
|                         $(children[1]).width(newSizes[1]); | ||||
|                         // $(children[1]).width(newSizes[1]); | ||||
|                         ui.position.left -= delta; | ||||
|                     } | ||||
|                     if (options.resize) { | ||||
| @@ -71,6 +75,9 @@ RED.panels = (function() { | ||||
|  | ||||
|         var panel = { | ||||
|             ratio: function(ratio) { | ||||
|                 if (ratio === undefined) { | ||||
|                     return panelRatio; | ||||
|                 } | ||||
|                 panelRatio = ratio; | ||||
|                 modifiedSizes = true; | ||||
|                 if (ratio === 0 || ratio === 1) { | ||||
| @@ -99,10 +106,10 @@ RED.panels = (function() { | ||||
|                     panelSizes = [topPanelSize,bottomPanelSize]; | ||||
|                     if (vertical) { | ||||
|                         $(children[0]).outerHeight(panelSizes[0]); | ||||
|                         $(children[1]).outerHeight(panelSizes[1]); | ||||
|                         // $(children[1]).outerHeight(panelSizes[1]); | ||||
|                     } else { | ||||
|                         $(children[0]).outerWidth(panelSizes[0]); | ||||
|                         $(children[1]).outerWidth(panelSizes[1]); | ||||
|                         // $(children[1]).outerWidth(panelSizes[1]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (options.resize) { | ||||
|   | ||||
| @@ -136,6 +136,23 @@ RED.popover = (function() { | ||||
|                         closePopup(true); | ||||
|                     }); | ||||
|                 } | ||||
|                 if (trigger === 'hover' && options.interactive) { | ||||
|                     div.on('mouseenter', function(e) { | ||||
|                         clearTimeout(timer); | ||||
|                         active = true; | ||||
|                     }) | ||||
|                     div.on('mouseleave', function(e) { | ||||
|                         if (timer) { | ||||
|                             clearTimeout(timer); | ||||
|                         } | ||||
|                         if (active) { | ||||
|                             timer = setTimeout(function() { | ||||
|                                 active = false; | ||||
|                                 closePopup(); | ||||
|                             },delay.hide); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|                 if (instant) { | ||||
|                     div.show(); | ||||
|                 } else { | ||||
| @@ -163,8 +180,10 @@ RED.popover = (function() { | ||||
|         if (trigger === 'hover') { | ||||
|             target.on('mouseenter',function(e) { | ||||
|                 clearTimeout(timer); | ||||
|                 active = true; | ||||
|                 timer = setTimeout(openPopup,delay.show); | ||||
|                 if (!active) { | ||||
|                     active = true; | ||||
|                     timer = setTimeout(openPopup,delay.show); | ||||
|                 } | ||||
|             }); | ||||
|             target.on('mouseleave disabled', function(e) { | ||||
|                 if (timer) { | ||||
| @@ -278,6 +297,7 @@ RED.popover = (function() { | ||||
|                 var closeCallback = options.onclose; | ||||
|                 var target = options.target; | ||||
|                 var align = options.align || "left"; | ||||
|                 var offset = options.offset || [0,0]; | ||||
|  | ||||
|                 var pos = target.offset(); | ||||
|                 var targetWidth = target.width(); | ||||
| @@ -285,7 +305,7 @@ RED.popover = (function() { | ||||
|                 var panelHeight = panel.height(); | ||||
|                 var panelWidth = panel.width(); | ||||
|  | ||||
|                 var top = (targetHeight+pos.top); | ||||
|                 var top = (targetHeight+pos.top) + offset[1]; | ||||
|                 if (top+panelHeight > $(window).height()) { | ||||
|                     top -= (top+panelHeight)-$(window).height() + 5; | ||||
|                 } | ||||
| @@ -296,12 +316,12 @@ RED.popover = (function() { | ||||
|                 if (align === "left") { | ||||
|                     panel.css({ | ||||
|                         top: top+"px", | ||||
|                         left: (pos.left)+"px", | ||||
|                         left: (pos.left+offset[0])+"px", | ||||
|                     }); | ||||
|                 } else if(align === "right") { | ||||
|                     panel.css({ | ||||
|                         top: top+"px", | ||||
|                         left: (pos.left-panelWidth)+"px", | ||||
|                         left: (pos.left-panelWidth+offset[0])+"px", | ||||
|                     }); | ||||
|                 } | ||||
|                 panel.slideDown(100); | ||||
|   | ||||
| @@ -27,7 +27,8 @@ | ||||
|  * | ||||
|  * methods: | ||||
|  *   - data(items) - clears existing items and replaces with new data | ||||
|  * | ||||
|  *   - clearSelection - clears the selected items | ||||
|  *   - filter(filterFunc) - filters the tree using the provided function | ||||
|  * events: | ||||
|  *   - treelistselect : function(event, item) {} | ||||
|  *   - treelistconfirm : function(event,item) {} | ||||
| @@ -39,7 +40,8 @@ | ||||
|  *         label: 'Local', // label for the item | ||||
|  *         sublabel: 'Local', // a sub-label for the item | ||||
|  *         icon: 'fa fa-rocket', // (optional) icon for the item | ||||
|  *         selected: true/false, // (optional) if present, display checkbox accordingly | ||||
|  *         checkbox: true/false, // (optional) if present, display checkbox accordingly | ||||
|  *         selected: true/false, // (optional) whether the item is selected or not | ||||
|  *         children: [] | function(done,item) // (optional) an array of child items, or a function | ||||
|  *                                       // that will call the `done` callback with an array | ||||
|  *                                       // of child items | ||||
| @@ -59,9 +61,9 @@ | ||||
|  * properties and functions: | ||||
|  * | ||||
|  *   item.parent - set to the parent item | ||||
|  *   item.depth - the depth in the tree (0 == root) | ||||
|  *   item.treeList.container | ||||
|  *   item.treeList.label - the label element for the item | ||||
|  *   item.treeList.depth - the depth in the tree (0 == root) | ||||
|  *   item.treeList.parentList  - the editableList instance this item is in | ||||
|  *   item.treeList.remove() - removes the item from the tree | ||||
|  *   item.treeList.makeLeaf(detachChildElements) - turns an element with children into a leaf node, | ||||
| @@ -78,8 +80,8 @@ | ||||
|  *                                            Optionally selects the item after adding. | ||||
|  *   item.treeList.expand(done) - expands the parent item to show children. Optional 'done' callback. | ||||
|  *   item.treeList.collapse() - collapse the parent item to hide children. | ||||
|  * | ||||
|  * | ||||
|  *   item.treeList.sortChildren(sortFunction) - does a one-time sort of the children using sortFunction | ||||
|  *   item.treeList.replaceElement(element) - replace the custom element for the item | ||||
|  * | ||||
|  * | ||||
|  */ | ||||
| @@ -100,6 +102,8 @@ | ||||
|                 var target; | ||||
|                 switch(evt.keyCode) { | ||||
|                     case 13: // ENTER | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children) { | ||||
|                             if (selected.treeList.container.hasClass("expanded")) { | ||||
|                                 selected.treeList.collapse() | ||||
| @@ -112,6 +116,8 @@ | ||||
|  | ||||
|                     break; | ||||
|                     case 37: // LEFT | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children&& selected.treeList.container.hasClass("expanded")) { | ||||
|                             selected.treeList.collapse() | ||||
|                         } else if (selected.parent) { | ||||
| @@ -119,6 +125,8 @@ | ||||
|                         } | ||||
|                     break; | ||||
|                     case 38: // UP | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         target = that._getPreviousSibling(selected); | ||||
|                         if (target) { | ||||
|                             target = that._getLastDescendant(target); | ||||
| @@ -128,6 +136,8 @@ | ||||
|                         } | ||||
|                     break; | ||||
|                     case 39: // RIGHT | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children) { | ||||
|                             if (!selected.treeList.container.hasClass("expanded")) { | ||||
|                                 selected.treeList.expand() | ||||
| @@ -135,6 +145,8 @@ | ||||
|                         } | ||||
|                     break | ||||
|                     case 40: //DOWN | ||||
|                         evt.preventDefault(); | ||||
|                         evt.stopPropagation(); | ||||
|                         if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) { | ||||
|                             target = selected.children[0]; | ||||
|                         } else { | ||||
| @@ -151,7 +163,8 @@ | ||||
|                 } | ||||
|             }); | ||||
|             this._data = []; | ||||
|  | ||||
|             this._items = {}; | ||||
|             this._selected = new Set(); | ||||
|             this._topList = $('<ol class="red-ui-treeList-list">').css({ | ||||
|                 position:'absolute', | ||||
|                 top: 0, | ||||
| @@ -244,7 +257,8 @@ | ||||
|                         that._trigger("changeparent",null,evt); | ||||
|                     }); | ||||
|                     that._trigger("sort",null,parent); | ||||
|                 } | ||||
|                 }, | ||||
|                 filter: parent.treeList.childFilter | ||||
|             }); | ||||
|             if (!!that.options.sortable) { | ||||
|                 subtree.addClass('red-ui-treeList-sortable'); | ||||
| @@ -289,21 +303,171 @@ | ||||
|             } | ||||
|             return reparentedEvent; | ||||
|         }, | ||||
|         _addSubtree: function(parentList, container, item, depth) { | ||||
|         _initItem: function(item,depth) { | ||||
|             if (item.treeList) { | ||||
|                 return; | ||||
|             } | ||||
|             var that = this; | ||||
|             this._items[item.id] = item; | ||||
|             item.treeList = {}; | ||||
|             item.treeList.depth = depth; | ||||
|             item.treeList.container = container; | ||||
|  | ||||
|             item.treeList.parentList = parentList; | ||||
|             item.depth = depth; | ||||
|             item.treeList.remove = function() { | ||||
|                 parentList.editableList('removeItem',item); | ||||
|                 if (item.treeList.parentList) { | ||||
|                     item.treeList.parentList.editableList('removeItem',item); | ||||
|                 } | ||||
|                 if (item.parent) { | ||||
|                     var index = item.parent.children.indexOf(item); | ||||
|                     item.parent.children.splice(index,1) | ||||
|                     that._trigger("sort",null,item.parent); | ||||
|                 } | ||||
|                 that._selected.delete(item); | ||||
|                 delete item.treeList; | ||||
|                 delete(that._items[item.id]); | ||||
|             } | ||||
|             item.treeList.insertChildAt = function(newItem,position,select) { | ||||
|                 newItem.parent = item; | ||||
|                 item.children.splice(position,0,newItem); | ||||
|                 var processChildren = function(parent,i) { | ||||
|                     that._initItem(i,parent.depth+1) | ||||
|                     i.parent = parent; | ||||
|                     if (i.children && typeof i.children !== 'function') { | ||||
|                         i.children.forEach(function(item) { | ||||
|                             processChildren(i, item, parent.depth+2) | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|                 processChildren(item,newItem); | ||||
|  | ||||
|                 if (!item.deferBuild) { | ||||
|                     item.treeList.childList.editableList('insertItemAt',newItem,position) | ||||
|                     if (select) { | ||||
|                         setTimeout(function() { | ||||
|                             that.select(newItem) | ||||
|                         },100); | ||||
|                     } | ||||
|                     that._trigger("sort",null,item); | ||||
|                 } | ||||
|             } | ||||
|             item.treeList.addChild = function(newItem,select) { | ||||
|                 item.treeList.insertChildAt(newItem,item.children.length,select); | ||||
|             } | ||||
|             item.treeList.expand = function(done) { | ||||
|                 if (!item.children) { | ||||
|                     if (done) { done(false) } | ||||
|                     return; | ||||
|                 } | ||||
|                 if (!item.treeList.container) { | ||||
|                     item.expanded = true; | ||||
|                     if (done) { done(false) } | ||||
|                     return; | ||||
|                 } | ||||
|                 var container = item.treeList.container; | ||||
|                 if (container.hasClass("expanded")) { | ||||
|                     if (done) { done(false) } | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) { | ||||
|                     container.addClass('built'); | ||||
|                     var childrenAdded = false; | ||||
|                     var spinner; | ||||
|                     var startTime = 0; | ||||
|                     var completeBuild = function(children) { | ||||
|                         childrenAdded = true; | ||||
|                         item.treeList.childList = that._addChildren(container,item,children,depth).hide(); | ||||
|                         var delta = Date.now() - startTime; | ||||
|                         if (delta < 400) { | ||||
|                             setTimeout(function() { | ||||
|                                 item.treeList.childList.slideDown('fast'); | ||||
|                                 if (spinner) { | ||||
|                                     spinner.remove(); | ||||
|                                 } | ||||
|                             },400-delta); | ||||
|                         } else { | ||||
|                             item.treeList.childList.slideDown('fast'); | ||||
|                             if (spinner) { | ||||
|                                 spinner.remove(); | ||||
|                             } | ||||
|                         } | ||||
|                         item.expanded = true; | ||||
|                         if (done) { done(true) } | ||||
|                         that._trigger("childrenloaded",null,item) | ||||
|                     } | ||||
|                     if (typeof item.children === 'function') { | ||||
|                         item.children(completeBuild,item); | ||||
|                     } else { | ||||
|                         delete item.deferBuild; | ||||
|                         completeBuild(item.children); | ||||
|                     } | ||||
|                     if (!childrenAdded) { | ||||
|                         startTime = Date.now(); | ||||
|                         spinner = $('<div class="red-ui-treeList-spinner">').css({ | ||||
|                             "background-position": (35+depth*20)+'px 50%' | ||||
|                         }).appendTo(container); | ||||
|                     } | ||||
|  | ||||
|                 } else { | ||||
|                     if (that._loadingData) { | ||||
|                         item.treeList.childList.show(); | ||||
|                     } else { | ||||
|                         item.treeList.childList.slideDown('fast'); | ||||
|                     } | ||||
|                     item.expanded = true; | ||||
|                     if (done) { done(!that._loadingData) } | ||||
|                 } | ||||
|                 container.addClass("expanded"); | ||||
|             } | ||||
|             item.treeList.collapse = function() { | ||||
|                 if (!item.children) { | ||||
|                     return; | ||||
|                 } | ||||
|                 item.expanded = false; | ||||
|                 if (item.treeList.container) { | ||||
|                     item.treeList.childList.slideUp('fast'); | ||||
|                     item.treeList.container.removeClass("expanded"); | ||||
|                 } | ||||
|             } | ||||
|             item.treeList.sortChildren = function(sortFunc) { | ||||
|                 if (!item.children) { | ||||
|                     return; | ||||
|                 } | ||||
|                 item.children.sort(sortFunc); | ||||
|                 if (item.treeList.childList) { | ||||
|                     // Do a one-off sort of the list, which means calling sort twice: | ||||
|                     // 1. first with the desired sort function | ||||
|                     item.treeList.childList.editableList('sort',sortFunc); | ||||
|                     // 2. and then with null to remove it | ||||
|                     item.treeList.childList.editableList('sort',null); | ||||
|                 } | ||||
|             } | ||||
|             item.treeList.replaceElement = function (element) { | ||||
|                 if (item.element) { | ||||
|                     if (item.treeList.container) { | ||||
|                         $(item.element).remove(); | ||||
|                         $(element).appendTo(item.treeList.label); | ||||
|                         var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(item.depth*20); | ||||
|                         $(element).css({ | ||||
|                             width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)" | ||||
|                         }) | ||||
|                     } | ||||
|                     item.element = element; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (item.children && typeof item.children !== "function") { | ||||
|                 item.children.forEach(function(i) { | ||||
|                     that._initItem(i,depth+1); | ||||
|                 }) | ||||
|             } | ||||
|         }, | ||||
|         _addSubtree: function(parentList, container, item, depth) { | ||||
|             var that = this; | ||||
|             this._initItem(item,depth); | ||||
|             // item.treeList = {}; | ||||
|             // item.treeList.depth = depth; | ||||
|             item.treeList.container = container; | ||||
|  | ||||
|             item.treeList.parentList = parentList; | ||||
|  | ||||
|             var label = $("<div>",{class:"red-ui-treeList-label"}).appendTo(container); | ||||
|             item.treeList.label = label; | ||||
| @@ -357,6 +521,7 @@ | ||||
|                 treeListIcon.off("click.red-ui-treeList-expand"); | ||||
|                 delete item.children; | ||||
|                 container.removeClass("expanded"); | ||||
|                 delete item.expanded; | ||||
|             } | ||||
|             item.treeList.makeParent = function(children) { | ||||
|                 if (treeListIcon.children().length) { | ||||
| @@ -388,101 +553,25 @@ | ||||
|                     item.treeList.childList = that._addChildren(container,item,item.children,depth).hide(); | ||||
|                 } | ||||
|             } | ||||
|             item.treeList.insertChildAt = function(newItem,position,select) { | ||||
|                 newItem.parent = item; | ||||
|                 item.children.splice(position,0,newItem); | ||||
|  | ||||
|                 if (!item.deferBuild) { | ||||
|                     item.treeList.childList.editableList('insertItemAt',newItem,position) | ||||
|                     if (select) { | ||||
|                         setTimeout(function() { | ||||
|                             that.select(newItem) | ||||
|                         },100); | ||||
|                     } | ||||
|                     that._trigger("sort",null,item); | ||||
|                 } | ||||
|             } | ||||
|             item.treeList.addChild = function(newItem,select) { | ||||
|                 item.treeList.insertChildAt(newItem,item.children.length,select); | ||||
|             } | ||||
|             item.treeList.expand = function(done) { | ||||
|                 if (!item.children) { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (container.hasClass("expanded")) { | ||||
|                     if (done) { done() } | ||||
|                     return; | ||||
|                 } | ||||
|                 if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) { | ||||
|                     container.addClass('built'); | ||||
|                     var childrenAdded = false; | ||||
|                     var spinner; | ||||
|                     var startTime = 0; | ||||
|                     var completeBuild = function(children) { | ||||
|                         childrenAdded = true; | ||||
|                         item.treeList.childList = that._addChildren(container,item,children,depth).hide(); | ||||
|                         var delta = Date.now() - startTime; | ||||
|                         if (delta < 400) { | ||||
|                             setTimeout(function() { | ||||
|                                 item.treeList.childList.slideDown('fast'); | ||||
|                                 if (spinner) { | ||||
|                                     spinner.remove(); | ||||
|                                 } | ||||
|                             },400-delta); | ||||
|                         } else { | ||||
|                             item.treeList.childList.slideDown('fast'); | ||||
|                             if (spinner) { | ||||
|                                 spinner.remove(); | ||||
|                             } | ||||
|                         } | ||||
|                         if (done) { done() } | ||||
|                         that._trigger("childrenloaded",null,item) | ||||
|                     } | ||||
|                     if (typeof item.children === 'function') { | ||||
|                         item.children(completeBuild,item); | ||||
|                     } else { | ||||
|                         delete item.deferBuild; | ||||
|                         completeBuild(item.children); | ||||
|                     } | ||||
|                     if (!childrenAdded) { | ||||
|                         startTime = Date.now(); | ||||
|                         spinner = $('<div class="red-ui-treeList-spinner">').css({ | ||||
|                             "background-position": (35+depth*20)+'px 50%' | ||||
|                         }).appendTo(container); | ||||
|                     } | ||||
|  | ||||
|                 } else { | ||||
|                     if (that._loadingData) { | ||||
|                         item.treeList.childList.show(); | ||||
|                     } else { | ||||
|                         item.treeList.childList.slideDown('fast'); | ||||
|                     } | ||||
|                     if (done) { done() } | ||||
|                 } | ||||
|                 container.addClass("expanded"); | ||||
|             } | ||||
|             item.treeList.collapse = function() { | ||||
|                 if (!item.children) { | ||||
|                     return; | ||||
|                 } | ||||
|                 item.treeList.childList.slideUp('fast'); | ||||
|                 container.removeClass("expanded"); | ||||
|             } | ||||
|  | ||||
|             var treeListIcon = $('<span class="red-ui-treeList-icon"></span>').appendTo(label); | ||||
|             if (item.children) { | ||||
|                 item.treeList.makeParent(); | ||||
|             } | ||||
|  | ||||
|             if (item.hasOwnProperty('selected')) { | ||||
|             if (item.checkbox) { | ||||
|                 var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label); | ||||
|                 var cb = $('<input class="red-ui-treeList-checkbox" type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper); | ||||
|                 label.toggleClass("selected",item.selected); | ||||
|                 cb.on('click', function(e) { | ||||
|                     e.stopPropagation(); | ||||
|                 }); | ||||
|                 cb.on('change', function(e) { | ||||
|                     item.selected = this.checked; | ||||
|                     if (item.selected) { | ||||
|                         that._selected.add(item); | ||||
|                     } else { | ||||
|                         that._selected.delete(item); | ||||
|                     } | ||||
|                     label.toggleClass("selected",this.checked); | ||||
|                     that._trigger("select",e,item); | ||||
|                 }) | ||||
| @@ -499,8 +588,12 @@ | ||||
|                 } | ||||
|             } else { | ||||
|                 label.on("click", function(e) { | ||||
|                     that._topList.find(".selected").removeClass("selected"); | ||||
|                     if (!that.options.multi) { | ||||
|                         that.clearSelection(); | ||||
|                     } | ||||
|                     label.addClass("selected"); | ||||
|                     that._selected.add(item); | ||||
|  | ||||
|                     that._trigger("select",e,item) | ||||
|                 }) | ||||
|                 label.on("dblclick", function(e) { | ||||
| @@ -508,9 +601,30 @@ | ||||
|                         that._trigger("confirm",e,item); | ||||
|                     } | ||||
|                 }) | ||||
|                 item.treeList.select = function(v) { | ||||
|                     if (!that.options.multi) { | ||||
|                         that.clearSelection(); | ||||
|                     } | ||||
|                     label.toggleClass("selected",v); | ||||
|                     if (v) { | ||||
|                         that._selected.add(item); | ||||
|                         that._trigger("select",null,item) | ||||
|                     } else { | ||||
|                         that._selected.delete(item); | ||||
|                     } | ||||
|                     that.reveal(item); | ||||
|                 } | ||||
|             } | ||||
|             label.toggleClass("selected",!!item.selected); | ||||
|             if (item.selected) { | ||||
|                 that._selected.add(item); | ||||
|             } | ||||
|             if (item.icon) { | ||||
|                 $('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label); | ||||
|                 if (typeof item.icon === "string") { | ||||
|                     $('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label); | ||||
|                 } else { | ||||
|                     $('<span class="red-ui-treeList-icon">').appendTo(label).append(item.icon); | ||||
|                 } | ||||
|             } | ||||
|             if (item.hasOwnProperty('label') || item.hasOwnProperty('sublabel')) { | ||||
|                 if (item.hasOwnProperty('label')) { | ||||
| @@ -542,6 +656,7 @@ | ||||
|             var that = this; | ||||
|             if (items !== undefined) { | ||||
|                 this._data = items; | ||||
|                 this._items = {}; | ||||
|                 this._topList.editableList('empty'); | ||||
|                 this._loadingData = true; | ||||
|                 for (var i=0; i<items.length;i++) { | ||||
| @@ -556,33 +671,142 @@ | ||||
|                 return this._data; | ||||
|             } | ||||
|         }, | ||||
|         show: function(id) { | ||||
|             for (var i=0;i<this._data.length;i++) { | ||||
|                 if (this._data[i].id === id) { | ||||
|                     this._topList.editableList('show',this._data[i]); | ||||
|         show: function(item, done) { | ||||
|             if (typeof item === "string") { | ||||
|                 item = this._items[item] | ||||
|             } | ||||
|             if (!item) { | ||||
|                 return; | ||||
|             } | ||||
|             var that = this; | ||||
|             var stack = []; | ||||
|             var i = item; | ||||
|             while(i) { | ||||
|                 stack.unshift(i); | ||||
|                 i = i.parent; | ||||
|             } | ||||
|             var isOpening = false; | ||||
|             var handleStack = function(opening) { | ||||
|                 isOpening = isOpening ||opening | ||||
|                 var item = stack.shift(); | ||||
|                 if (stack.length === 0) { | ||||
|                     setTimeout(function() { | ||||
|                         that.reveal(item); | ||||
|                         if (done) { done(); } | ||||
|                     },isOpening?200:0); | ||||
|                 } else { | ||||
|                     item.treeList.expand(handleStack) | ||||
|                 } | ||||
|             } | ||||
|             handleStack(); | ||||
|         }, | ||||
|         select: function(item) { | ||||
|             this._topList.find(".selected").removeClass("selected"); | ||||
|             item.treeList.label.addClass("selected"); | ||||
|             this._trigger("select",null,item) | ||||
|         reveal: function(item) { | ||||
|             if (typeof item === "string") { | ||||
|                 item = this._items[item] | ||||
|             } | ||||
|             if (!item) { | ||||
|                 return; | ||||
|             } | ||||
|             var listOffset = this._topList.offset().top; | ||||
|             var itemOffset = item.treeList.label.offset().top; | ||||
|             var scrollTop = this._topList.parent().scrollTop(); | ||||
|             itemOffset -= listOffset+scrollTop; | ||||
|             var treeHeight = this._topList.parent().height(); | ||||
|             var itemHeight = item.treeList.label.outerHeight(); | ||||
|             if (itemOffset < itemHeight/2) { | ||||
|                 this._topList.parent().scrollTop(scrollTop+itemOffset-itemHeight/2-itemHeight) | ||||
|             } else if (itemOffset+itemHeight > treeHeight) { | ||||
|                 this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight)); | ||||
|             } | ||||
|         }, | ||||
|         select: function(item, triggerEvent, deselectExisting) { | ||||
|             var that = this; | ||||
|             if (!this.options.multi && deselectExisting !== false) { | ||||
|                 this.clearSelection(); | ||||
|             } | ||||
|             if (Array.isArray(item)) { | ||||
|                 item.forEach(function(i) { | ||||
|                     that.select(i,triggerEvent,false); | ||||
|                 }) | ||||
|                 return; | ||||
|             } | ||||
|             if (typeof item === "string") { | ||||
|                 item = this._items[item] | ||||
|             } | ||||
|             if (!item) { | ||||
|                 return; | ||||
|             } | ||||
|             // this.show(item.id); | ||||
|             item.selected = true; | ||||
|             this._selected.add(item); | ||||
|  | ||||
|             if (item.treeList.label) { | ||||
|                 item.treeList.label.addClass("selected"); | ||||
|             } | ||||
|             if (triggerEvent !== false) { | ||||
|                 this._trigger("select",null,item) | ||||
|             } | ||||
|         }, | ||||
|         clearSelection: function() { | ||||
|             this._selected.forEach(function(item) { | ||||
|                 item.selected = false; | ||||
|                 if (item.treeList.label) { | ||||
|                     item.treeList.label.removeClass("selected") | ||||
|                 } | ||||
|             }); | ||||
|             this._selected.clear(); | ||||
|         }, | ||||
|         selected: function() { | ||||
|             var s = this._topList.find(".selected"); | ||||
|             var selected = []; | ||||
|             this._selected.forEach(function(item) { | ||||
|                 selected.push(item); | ||||
|             }) | ||||
|             if (this.options.multi) { | ||||
|                 var res = []; | ||||
|                 s.each(function() { | ||||
|                     res.push($(this).parent().data('data')); | ||||
|                 }) | ||||
|                 return res; | ||||
|                 return selected; | ||||
|             } | ||||
|             if (s.length) { | ||||
|                 return s.parent().data('data'); | ||||
|             if (selected.length) { | ||||
|                 return selected[0] | ||||
|             } else { | ||||
|                 // TODO: This may be a bug.. it causes the call to return itself | ||||
|                 // not undefined. | ||||
|                 return undefined; | ||||
|             } | ||||
|         }, | ||||
|         filter: function(filterFunc,expandResults) { | ||||
|             var filter = function(item) { | ||||
|                 var matchCount = 0; | ||||
|                 if (filterFunc && filterFunc(item)) { | ||||
|                     matchCount++; | ||||
|                 } | ||||
|                 var childCount = 0; | ||||
|                 if (item.children && typeof item.children !== "function") { | ||||
|                     if (item.treeList.childList) { | ||||
|                         childCount = item.treeList.childList.editableList('filter', filter); | ||||
|                     } else { | ||||
|                         item.treeList.childFilter = filter; | ||||
|                         if (filterFunc) { | ||||
|                             item.children.forEach(function(i) { | ||||
|                                 if (filter(i)) { | ||||
|                                     childCount++; | ||||
|                                 } | ||||
|                             }) | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                     matchCount += childCount; | ||||
|                     if (childCount > 0) { | ||||
|                         item.treeList.expand(); | ||||
|                     } | ||||
|                 } | ||||
|                 if (!filterFunc) { | ||||
|                     return true | ||||
|                 } | ||||
|                 return matchCount > 0 | ||||
|             } | ||||
|             return this._topList.editableList('filter', filter); | ||||
|         }, | ||||
|         get: function(id) { | ||||
|             return this._items[id] || null; | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -164,6 +164,84 @@ | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|         }, | ||||
|         cred:{ | ||||
|             value:"cred", | ||||
|             label:"credential", | ||||
|             icon:"fa fa-lock", | ||||
|             inputType: "password", | ||||
|             valueLabel: function(container,value) { | ||||
|                 var that = this; | ||||
|                 container.css("pointer-events","none"); | ||||
|                 this.elementDiv.hide(); | ||||
|                 var buttons = $('<div>').css({ | ||||
|                     position: "absolute", | ||||
|                     right:"6px", | ||||
|                     top: "6px", | ||||
|                     "pointer-events":"all" | ||||
|                 }).appendTo(container); | ||||
|                 var eyeButton = $('<button type="button" class="red-ui-button red-ui-button-small"></button>').css({ | ||||
|                     width:"20px" | ||||
|                 }).appendTo(buttons).on("click", function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     var currentType = that.input.attr("type"); | ||||
|                     if (currentType === "text") { | ||||
|                         that.input.attr("type","password"); | ||||
|                         eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); | ||||
|                         setTimeout(function() { | ||||
|                             that.input.focus(); | ||||
|                         },50); | ||||
|                     } else { | ||||
|                         that.input.attr("type","text"); | ||||
|                         eyeCon.removeClass("fa-eye").addClass("fa-eye-slash"); | ||||
|                         setTimeout(function() { | ||||
|                             that.input.focus(); | ||||
|                         },50); | ||||
|                     } | ||||
|                 }).hide(); | ||||
|                 var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-1px").appendTo(eyeButton); | ||||
|  | ||||
|                 if (value === "__PWRD__") { | ||||
|                     var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({ | ||||
|                         padding:"6px 6px", | ||||
|                         borderRadius:"4px" | ||||
|                     }).addClass("red-ui-typedInput-value-label-inactive").appendTo(container); | ||||
|                     var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         innerContainer.hide(); | ||||
|                         container.css("background","none"); | ||||
|                         container.css("pointer-events","none"); | ||||
|                         that.input.val(""); | ||||
|                         that.element.val(""); | ||||
|                         that.elementDiv.show(); | ||||
|                         editButton.hide(); | ||||
|                         cancelButton.show(); | ||||
|                         eyeButton.show(); | ||||
|                         setTimeout(function() { | ||||
|                             that.input.focus(); | ||||
|                         },50); | ||||
|                     }); | ||||
|                     var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left","3px").appendTo(buttons).on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         innerContainer.show(); | ||||
|                         container.css("background",""); | ||||
|                         that.input.val("__PWRD__"); | ||||
|                         that.element.val("__PWRD__"); | ||||
|                         that.elementDiv.hide(); | ||||
|                         editButton.show(); | ||||
|                         cancelButton.hide(); | ||||
|                         eyeButton.hide(); | ||||
|                         that.input.attr("type","password"); | ||||
|                         eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); | ||||
|  | ||||
|                     }).hide(); | ||||
|                 } else { | ||||
|                     container.css("background","none"); | ||||
|                     container.css("pointer-events","none"); | ||||
|                     this.elementDiv.show(); | ||||
|                     eyeButton.show(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     var nlsd = false; | ||||
| @@ -220,6 +298,8 @@ | ||||
|                 that.input.attr(d,m); | ||||
|             }); | ||||
|  | ||||
|             this.defaultInputType = this.input.attr('type'); | ||||
|  | ||||
|             this.uiSelect.addClass("red-ui-typedInput-container"); | ||||
|  | ||||
|             this.element.attr('type','hidden'); | ||||
| @@ -635,7 +715,7 @@ | ||||
|                             $('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel); | ||||
|                         } | ||||
|                         else { | ||||
|                             $('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel); | ||||
|                             $('<i>',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel); | ||||
|                         } | ||||
|                     } | ||||
|                     if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) { | ||||
| @@ -778,6 +858,11 @@ | ||||
|                         if (this.optionSelectTrigger) { | ||||
|                             this.optionSelectTrigger.hide(); | ||||
|                         } | ||||
|                         if (opt.inputType) { | ||||
|                             this.input.attr('type',opt.inputType) | ||||
|                         } else { | ||||
|                             this.input.attr('type',this.defaultInputType) | ||||
|                         } | ||||
|                         if (opt.hasValue === false) { | ||||
|                             this.oldValue = this.input.val(); | ||||
|                             this.input.val(""); | ||||
| @@ -786,8 +871,8 @@ | ||||
|                         } else if (opt.valueLabel) { | ||||
|                             this.valueLabelContainer.show(); | ||||
|                             this.valueLabelContainer.empty(); | ||||
|                             opt.valueLabel.call(this,this.valueLabelContainer,this.input.val()); | ||||
|                             this.elementDiv.hide(); | ||||
|                             opt.valueLabel.call(this,this.valueLabelContainer,this.input.val()); | ||||
|                         } else { | ||||
|                             if (this.oldValue !== undefined) { | ||||
|                                 this.input.val(this.oldValue); | ||||
|   | ||||
| @@ -108,7 +108,7 @@ RED.deploy = (function() { | ||||
|  | ||||
|  | ||||
|  | ||||
|         RED.events.on('nodes:change',function(state) { | ||||
|         RED.events.on('workspace:dirty',function(state) { | ||||
|             if (state.dirty) { | ||||
|                 window.onbeforeunload = function() { | ||||
|                     return RED._("deploy.confirm.undeployedChanges"); | ||||
|   | ||||
| @@ -490,8 +490,7 @@ RED.editor = (function() { | ||||
|                 done(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (definition.credentials) { | ||||
|         if (definition.credentials || /^subflow:/.test(definition.type)) { | ||||
|             if (node.credentials) { | ||||
|                 populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); | ||||
|                 completePrepare(); | ||||
| @@ -499,7 +498,9 @@ RED.editor = (function() { | ||||
|                 $.getJSON(getCredentialsURL(node.type, node.id), function (data) { | ||||
|                     node.credentials = data; | ||||
|                     node.credentials._ = $.extend(true,{},data); | ||||
|                     populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); | ||||
|                     if (!/^subflow:/.test(definition.type)) { | ||||
|                         populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); | ||||
|                     } | ||||
|                     completePrepare(); | ||||
|                 }); | ||||
|             } | ||||
| @@ -513,7 +514,9 @@ RED.editor = (function() { | ||||
|         for (var i=editStack.length-1;i<editStack.length;i++) { | ||||
|             var node = editStack[i]; | ||||
|             label = node.type; | ||||
|             if (node.type === '_expression') { | ||||
|             if (node.type === 'group') { | ||||
|                 label = RED._("group.editGroup",{name:RED.utils.sanitize(node.name||node.id)}); | ||||
|             } else if (node.type === '_expression') { | ||||
|                 label = RED._("expressionEditor.title"); | ||||
|             } else if (node.type === '_js') { | ||||
|                 label = RED._("jsEditor.title"); | ||||
| @@ -576,8 +579,11 @@ RED.editor = (function() { | ||||
|             $(this).attr("data-i18n",keys.join(";")); | ||||
|         }); | ||||
|  | ||||
|         if (type === "subflow-template" || type === "subflow") { | ||||
|             RED.subflow.buildEditForm(dialogForm,type,node); | ||||
|         if (type === "subflow-template") { | ||||
|             // This is the 'edit properties' dialog for a subflow template | ||||
|             // TODO: this needs to happen later in the dialog open sequence | ||||
|             //       so that credentials can be loaded prior to building the form | ||||
|             RED.subflow.buildEditForm(type,node); | ||||
|         } | ||||
|  | ||||
|         // Add dummy fields to prevent 'Enter' submitting the form in some | ||||
| @@ -586,6 +592,7 @@ RED.editor = (function() { | ||||
|         //  - the elements need to have id's that imply password/username | ||||
|         $('<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); | ||||
|         $('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-user"  type="text"/></span>').prependTo(dialogForm); | ||||
|         dialogForm.on("submit", function(e) { e.preventDefault();}); | ||||
|         dialogForm.find('input').attr("autocomplete","off"); | ||||
|         return dialogForm; | ||||
| @@ -784,6 +791,11 @@ RED.editor = (function() { | ||||
|                             nodeDiv.css({ | ||||
|                                 'backgroundColor': backgroundColor | ||||
|                             }); | ||||
|                             var borderColor = RED.utils.getDarkerColor(backgroundColor); | ||||
|                             if (borderColor !== backgroundColor) { | ||||
|                                 nodeDiv.css('border-color',borderColor) | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|                         RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
| @@ -819,99 +831,6 @@ RED.editor = (function() { | ||||
|         searchInput.trigger("focus"); | ||||
|     } | ||||
|  | ||||
|     function createColorPicker(colorRow, color) { | ||||
|  | ||||
|         var colorButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(colorRow); | ||||
|         $('<i class="fa fa-caret-down"></i>').appendTo(colorButton); | ||||
|  | ||||
|         var colorDisp = $('<div>',{class:"red-ui-search-result-node"}).appendTo(colorButton); | ||||
|  | ||||
|         var selector = $("<input/>", { | ||||
|             id: "red-ui-editor-node-color", | ||||
|             type: "text", | ||||
|             value: color | ||||
|         }).css({ | ||||
|             marginLeft: "10px", | ||||
|             width: "150px", | ||||
|         }).appendTo(colorRow); | ||||
|  | ||||
|         selector.on("change", function (e) { | ||||
|             var color = selector.val(); | ||||
|             $(".red-ui-editor-node-appearance-button .red-ui-search-result-node").css({ | ||||
|                 "background-color": color | ||||
|             }); | ||||
|         }); | ||||
|         selector.trigger("change"); | ||||
|         colorButton.on("click", function (e) { | ||||
|             var recommendedColors = [ | ||||
|                 "#DDAA99", | ||||
|                 "#3FADB5", "#87A980", "#A6BBCF", | ||||
|                 "#AAAA66", "#C0C0C0", "#C0DEED", | ||||
|                 "#C7E9C0", "#D7D7A0", "#D8BFD8", | ||||
|                 "#DAC4B4", "#DEB887", "#DEBD5C", | ||||
|                 "#E2D96E", "#E6E0F8", "#E7E7AE", | ||||
|                 "#E9967A", "#F3B567", "#FDD0A2", | ||||
|                 "#FDF0C2", "#FFAAAA", "#FFCC66", | ||||
|                 "#FFF0F0", "#FFFFFF" | ||||
|             ].map(function(c) { | ||||
|                 var r = parseInt(c.substring(1, 3), 16) / 255; | ||||
|                 var g = parseInt(c.substring(3, 5), 16) / 255; | ||||
|                 var b = parseInt(c.substring(5, 7), 16) / 255; | ||||
|                 return { | ||||
|                     hex: c, | ||||
|                     r: r, | ||||
|                     g: g, | ||||
|                     b: b, | ||||
|                     l: 0.3 * r + 0.59 * g + 0.11 * b | ||||
|                 } | ||||
|             }); | ||||
|             // Sort by luminosity. | ||||
|             recommendedColors.sort(function (a, b) { | ||||
|                 return a.l - b.l; | ||||
|             }); | ||||
|  | ||||
|             var numColors = recommendedColors.length; | ||||
|             var width = 30; | ||||
|             var height = 30; | ||||
|             var margin = 2; | ||||
|             var perRow = 6; | ||||
|             var picker = $("<div/>", { | ||||
|                 class: "red-ui-color-picker" | ||||
|             }).css({ | ||||
|                 width: ((width+margin+margin)*perRow)+"px", | ||||
|                 height: Math.ceil(numColors/perRow)*(height+margin+margin)+"+px" | ||||
|             }); | ||||
|             var count = 0; | ||||
|             var row = null; | ||||
|             recommendedColors.forEach(function (col) { | ||||
|                 if ((count % perRow) == 0) { | ||||
|                     row = $("<div/>").appendTo(picker); | ||||
|                 } | ||||
|                 var button = $("<button/>", { | ||||
|                 }).css({ | ||||
|                     width: width+"px", | ||||
|                     height: height+"px", | ||||
|                     margin: margin+"px", | ||||
|                     backgroundColor: col.hex, | ||||
|                     "border-style": "solid", | ||||
|                     "border-width": "1px", | ||||
|                     "border-color": col.luma<0.92?col.hex:'#ccc' | ||||
|                 }).appendTo(row); | ||||
|                 button.on("click",  function (e) { | ||||
|                     e.preventDefault(); | ||||
|                     colorPanel.hide(); | ||||
|                     selector.val(col.hex); | ||||
|                     selector.trigger("change"); | ||||
|                 }); | ||||
|                 count++; | ||||
|             }); | ||||
|             var colorPanel = RED.popover.panel(picker); | ||||
|             colorPanel.show({ | ||||
|                 target: colorButton | ||||
|             }) | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function buildAppearanceForm(container,node) { | ||||
|         var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container); | ||||
|  | ||||
| @@ -997,7 +916,35 @@ RED.editor = (function() { | ||||
|                 class: "form-row" | ||||
|             }).appendTo(dialogForm); | ||||
|             $("<label/>").text(RED._("editor.color")).appendTo(colorRow); | ||||
|             createColorPicker(colorRow, color); | ||||
|  | ||||
|             var recommendedColors = [ | ||||
|                 "#DDAA99", | ||||
|                 "#3FADB5", "#87A980", "#A6BBCF", | ||||
|                 "#AAAA66", "#C0C0C0", "#C0DEED", | ||||
|                 "#C7E9C0", "#D7D7A0", "#D8BFD8", | ||||
|                 "#DAC4B4", "#DEB887", "#DEBD5C", | ||||
|                 "#E2D96E", "#E6E0F8", "#E7E7AE", | ||||
|                 "#E9967A", "#F3B567", "#FDD0A2", | ||||
|                 "#FDF0C2", "#FFAAAA", "#FFCC66", | ||||
|                 "#FFF0F0", "#FFFFFF" | ||||
|             ] | ||||
|  | ||||
|             RED.colorPicker.create({ | ||||
|                 id: "red-ui-editor-node-color", | ||||
|                 value: color, | ||||
|                 palette: recommendedColors, | ||||
|                 sortPalette: function (a, b) {return a.l - b.l;} | ||||
|             }).appendTo(colorRow); | ||||
|  | ||||
|             $("#red-ui-editor-node-color").on('change', function(ev) { | ||||
|                 // Horribly out of scope... | ||||
|                 var colour = $(this).val(); | ||||
|                 nodeDiv.css('backgroundColor',colour); | ||||
|                 var borderColor = RED.utils.getDarkerColor(colour); | ||||
|                 if (borderColor !== colour) { | ||||
|                     nodeDiv.css('border-color',borderColor) | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|  | ||||
| @@ -1012,6 +959,11 @@ RED.editor = (function() { | ||||
|             var colour = RED.utils.getNodeColor(node.type, node._def); | ||||
|             var icon_url = RED.utils.getNodeIcon(node._def,node); | ||||
|             nodeDiv.css('backgroundColor',colour); | ||||
|             var borderColor = RED.utils.getDarkerColor(colour); | ||||
|             if (borderColor !== colour) { | ||||
|                 nodeDiv.css('border-color',borderColor) | ||||
|             } | ||||
|  | ||||
|             var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|             RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|  | ||||
| @@ -1132,15 +1084,17 @@ RED.editor = (function() { | ||||
|         if (node.info) { | ||||
|             nodeInfoEditor.getSession().setValue(node.info, -1); | ||||
|         } | ||||
|         node.infoEditor = nodeInfoEditor; | ||||
|         return nodeInfoEditor; | ||||
|     } | ||||
|  | ||||
|     function showEditDialog(node) { | ||||
|     function showEditDialog(node, defaultTab) { | ||||
|         var editing_node = node; | ||||
|         var isDefaultIcon; | ||||
|         var defaultIcon; | ||||
|         var nodeInfoEditor; | ||||
|         var finishedBuilding = false; | ||||
|         var skipInfoRefreshOnClose = false; | ||||
|  | ||||
|         editStack.push(node); | ||||
|         RED.view.state(RED.state.EDITING); | ||||
| @@ -1471,6 +1425,19 @@ RED.editor = (function() { | ||||
|                         if (type === "subflow") { | ||||
|                             var old_env = editing_node.env; | ||||
|                             var new_env = RED.subflow.exportSubflowInstanceEnv(editing_node); | ||||
|                             if (new_env && new_env.length > 0) { | ||||
|                                 new_env.forEach(function(prop) { | ||||
|                                     if (prop.type === "cred") { | ||||
|                                         editing_node.credentials = editing_node.credentials || {_:{}}; | ||||
|                                         editing_node.credentials[prop.name] = prop.value; | ||||
|                                         editing_node.credentials['has_'+prop.name] = (prop.value !== ""); | ||||
|                                         if (prop.value !== '__PWRD__') { | ||||
|                                             changed = true; | ||||
|                                         } | ||||
|                                         delete prop.value; | ||||
|                                     } | ||||
|                                 }); | ||||
|                             } | ||||
|                             if (!isSameObj(old_env, new_env)) { | ||||
|                                 editing_node.env = new_env; | ||||
|                                 changes.env = editing_node.env; | ||||
| @@ -1520,6 +1487,7 @@ RED.editor = (function() { | ||||
|                         editing_node.dirty = true; | ||||
|                         validateNode(editing_node); | ||||
|                         RED.events.emit("editor:save",editing_node); | ||||
|                         RED.events.emit("nodes:change",editing_node); | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
|                 } | ||||
| @@ -1567,9 +1535,6 @@ RED.editor = (function() { | ||||
|                     collapsible: true, | ||||
|                     menu: false | ||||
|                 }); | ||||
|                 if (editing_node) { | ||||
|                     RED.sidebar.info.refresh(editing_node); | ||||
|                 } | ||||
|                 var ns; | ||||
|                 if (node._def.set.module === "node-red") { | ||||
|                     ns = "node-red"; | ||||
| @@ -1599,12 +1564,13 @@ RED.editor = (function() { | ||||
|                         id: "editor-subflow-envProperties", | ||||
|                         label: RED._("editor-tab.envProperties"), | ||||
|                         name: RED._("editor-tab.envProperties"), | ||||
|                         content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), | ||||
|                         content: $('<div>', {id:"editor-subflow-envProperties-content",class:"red-ui-tray-content"}).appendTo(editorContent).hide(), | ||||
|                         iconClass: "fa fa-list" | ||||
|                     }; | ||||
|  | ||||
|                     RED.subflow.buildPropertiesForm(subflowPropertiesTab.content,node); | ||||
|                     editorTabs.addTab(subflowPropertiesTab); | ||||
|                     // This tab is populated by the oneditprepare function of this | ||||
|                     // subflow. That ensures it is done *after* any credentials | ||||
|                     // have been loaded for the instance. | ||||
|                 } | ||||
|  | ||||
|                 if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info'))  { | ||||
| @@ -1638,6 +1604,9 @@ RED.editor = (function() { | ||||
|                 prepareEditDialog(node,node._def,"node-input", function() { | ||||
|                     trayBody.i18n(); | ||||
|                     finishedBuilding = true; | ||||
|                     if (defaultTab) { | ||||
|                         editorTabs.activateTab(defaultTab); | ||||
|                     } | ||||
|                     done(); | ||||
|                 }); | ||||
|             }, | ||||
| @@ -1645,7 +1614,7 @@ RED.editor = (function() { | ||||
|                 if (RED.view.state() != RED.state.IMPORT_DRAGGING) { | ||||
|                     RED.view.state(RED.state.DEFAULT); | ||||
|                 } | ||||
|                 if (editing_node) { | ||||
|                 if (editing_node && !skipInfoRefreshOnClose) { | ||||
|                     RED.sidebar.info.refresh(editing_node); | ||||
|                 } | ||||
|                 RED.workspaces.refresh(); | ||||
| @@ -1673,6 +1642,7 @@ RED.editor = (function() { | ||||
|                 text: RED._("subflow.edit"), | ||||
|                 click: function() { | ||||
|                     RED.workspaces.show(id); | ||||
|                     skipInfoRefreshOnClose = true; | ||||
|                     $("#node-dialog-ok").trigger("click"); | ||||
|                 } | ||||
|             }); | ||||
| @@ -2039,6 +2009,7 @@ RED.editor = (function() { | ||||
|                     RED.view.redraw(true); | ||||
|                     if (!configAdding) { | ||||
|                         RED.events.emit("editor:save",editing_config_node); | ||||
|                         RED.events.emit("nodes:change",editing_config_node); | ||||
|                     } | ||||
|                     RED.tray.close(function() { | ||||
|                         updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix); | ||||
| @@ -2252,12 +2223,26 @@ RED.editor = (function() { | ||||
|  | ||||
|                         var old_env = editing_node.env; | ||||
|                         var new_env = RED.subflow.exportSubflowTemplateEnv($("#node-input-env-container").editableList("items")); | ||||
|  | ||||
|                         if (new_env && new_env.length > 0) { | ||||
|                             new_env.forEach(function(prop) { | ||||
|                                 if (prop.type === "cred") { | ||||
|                                     editing_node.credentials = editing_node.credentials || {_:{}}; | ||||
|                                     editing_node.credentials[prop.name] = prop.value; | ||||
|                                     editing_node.credentials['has_'+prop.name] = (prop.value !== ""); | ||||
|                                     if (prop.value !== '__PWRD__') { | ||||
|                                         changed = true; | ||||
|                                     } | ||||
|                                     delete prop.value; | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|  | ||||
|                         if (!isSameObj(old_env, new_env)) { | ||||
|                             editing_node.env = new_env; | ||||
|                             changes.env = editing_node.env; | ||||
|                             changed = true; | ||||
|                         } | ||||
|                         RED.palette.refresh(); | ||||
|  | ||||
|                         if (changed) { | ||||
|                             var wasChanged = editing_node.changed; | ||||
| @@ -2277,6 +2262,7 @@ RED.editor = (function() { | ||||
|                                     validateNode(n); | ||||
|                                 } | ||||
|                             }); | ||||
|                             RED.events.emit("subflows:change",editing_node); | ||||
|                             RED.nodes.dirty(true); | ||||
|                             var historyEvent = { | ||||
|                                 t:'edit', | ||||
| @@ -2311,7 +2297,7 @@ RED.editor = (function() { | ||||
|                     $("#node-input-env-container").editableList('height',height-95); | ||||
|                 } | ||||
|             }, | ||||
|             open: function(tray) { | ||||
|             open: function(tray, done) { | ||||
|                 var trayFooter = tray.find(".red-ui-tray-footer"); | ||||
|                 var trayFooterLeft = $("<div/>", { | ||||
|                     class: "red-ui-tray-footer-left" | ||||
| @@ -2362,7 +2348,6 @@ RED.editor = (function() { | ||||
|                     content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), | ||||
|                     iconClass: "fa fa-cog" | ||||
|                 }; | ||||
|                 buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node); | ||||
|                 editorTabs.addTab(nodePropertiesTab); | ||||
|  | ||||
|                 var descriptionTab = { | ||||
| @@ -2391,11 +2376,19 @@ RED.editor = (function() { | ||||
|                 buildAppearanceForm(appearanceTab.content,editing_node); | ||||
|                 editorTabs.addTab(appearanceTab); | ||||
|  | ||||
|                 $("#subflow-input-name").val(subflow.name); | ||||
|                 RED.text.bidi.prepareInput($("#subflow-input-name")); | ||||
|  | ||||
|                 buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node); | ||||
|                 trayBody.i18n(); | ||||
|                 finishedBuilding = true; | ||||
|  | ||||
|                 $.getJSON(getCredentialsURL("subflow", subflow.id), function (data) { | ||||
|                     subflow.credentials = data; | ||||
|                     subflow.credentials._ = $.extend(true,{},data); | ||||
|  | ||||
|                     $("#subflow-input-name").val(subflow.name); | ||||
|                     RED.text.bidi.prepareInput($("#subflow-input-name")); | ||||
|  | ||||
|                     finishedBuilding = true; | ||||
|                     done(); | ||||
|                 }); | ||||
|             }, | ||||
|             close: function() { | ||||
|                 if (RED.view.state() != RED.state.IMPORT_DRAGGING) { | ||||
| @@ -2414,6 +2407,251 @@ RED.editor = (function() { | ||||
|         RED.tray.show(trayOptions); | ||||
|     } | ||||
|  | ||||
|     function showEditGroupDialog(group) { | ||||
|         var editing_node = group; | ||||
|         editStack.push(group); | ||||
|         RED.view.state(RED.state.EDITING); | ||||
|         var nodeInfoEditor; | ||||
|         var finishedBuilding = false; | ||||
|         var trayOptions = { | ||||
|             title: getEditStackTitle(), | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     id: "node-dialog-cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     id: "node-dialog-ok", | ||||
|                     class: "primary", | ||||
|                     text: RED._("common.label.done"), | ||||
|                     click: function() { | ||||
|                         var changes = {}; | ||||
|                         var changed = false; | ||||
|                         var wasDirty = RED.nodes.dirty(); | ||||
|                         var d; | ||||
|                         var outputMap; | ||||
|  | ||||
|                         if (editing_node._def.oneditsave) { | ||||
|                             var oldValues = {}; | ||||
|                             for (d in editing_node._def.defaults) { | ||||
|                                 if (editing_node._def.defaults.hasOwnProperty(d)) { | ||||
|                                     if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") { | ||||
|                                         oldValues[d] = editing_node[d]; | ||||
|                                     } else { | ||||
|                                         oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             try { | ||||
|                                 var rc = editing_node._def.oneditsave.call(editing_node); | ||||
|                                 if (rc === true) { | ||||
|                                     changed = true; | ||||
|                                 } | ||||
|                             } catch(err) { | ||||
|                                 console.log("oneditsave",editing_node.id,editing_node.type,err.toString()); | ||||
|                             } | ||||
|  | ||||
|                             for (d in editing_node._def.defaults) { | ||||
|                                 if (editing_node._def.defaults.hasOwnProperty(d)) { | ||||
|                                     if (oldValues[d] === null || typeof oldValues[d] === "string" || typeof oldValues[d] === "number") { | ||||
|                                         if (oldValues[d] !== editing_node[d]) { | ||||
|                                             changes[d] = oldValues[d]; | ||||
|                                             changed = true; | ||||
|                                         } | ||||
|                                     } else { | ||||
|                                         if (JSON.stringify(oldValues[d]) !== JSON.stringify(editing_node[d])) { | ||||
|                                             changes[d] = oldValues[d]; | ||||
|                                             changed = true; | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         var newValue; | ||||
|                         if (editing_node._def.defaults) { | ||||
|                             for (d in editing_node._def.defaults) { | ||||
|                                 if (editing_node._def.defaults.hasOwnProperty(d)) { | ||||
|                                     var input = $("#node-input-"+d); | ||||
|                                     if (input.attr('type') === "checkbox") { | ||||
|                                         newValue = input.prop('checked'); | ||||
|                                     } else if (input.prop("nodeName") === "select" && input.attr("multiple") === "multiple") { | ||||
|                                         // An empty select-multiple box returns null. | ||||
|                                         // Need to treat that as an empty array. | ||||
|                                         newValue = input.val(); | ||||
|                                         if (newValue == null) { | ||||
|                                             newValue = []; | ||||
|                                         } | ||||
|                                     } else if ("format" in editing_node._def.defaults[d] && editing_node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") { | ||||
|                                         newValue = input.text(); | ||||
|                                     } else { | ||||
|                                         newValue = input.val(); | ||||
|                                     } | ||||
|                                     if (newValue != null) { | ||||
|                                         if (editing_node._def.defaults[d].type) { | ||||
|                                             if (newValue == "_ADD_") { | ||||
|                                                 newValue = ""; | ||||
|                                             } | ||||
|                                         } | ||||
|                                         if (editing_node[d] != newValue) { | ||||
|                                             if (editing_node._def.defaults[d].type) { | ||||
|                                                 // Change to a related config node | ||||
|                                                 var configNode = RED.nodes.node(editing_node[d]); | ||||
|                                                 if (configNode) { | ||||
|                                                     var users = configNode.users; | ||||
|                                                     users.splice(users.indexOf(editing_node),1); | ||||
|                                                 } | ||||
|                                                 configNode = RED.nodes.node(newValue); | ||||
|                                                 if (configNode) { | ||||
|                                                     configNode.users.push(editing_node); | ||||
|                                                 } | ||||
|                                             } | ||||
|                                             changes[d] = editing_node[d]; | ||||
|                                             editing_node[d] = newValue; | ||||
|                                             changed = true; | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         var oldInfo = editing_node.info; | ||||
|                         if (nodeInfoEditor) { | ||||
|                             var newInfo = nodeInfoEditor.getValue(); | ||||
|                             if (!!oldInfo) { | ||||
|                                 // Has existing info property | ||||
|                                 if (newInfo.trim() === "") { | ||||
|                                     // New value is blank - remove the property | ||||
|                                     changed = true; | ||||
|                                     changes.info = oldInfo; | ||||
|                                     delete editing_node.info; | ||||
|                                 } else if (newInfo !== oldInfo) { | ||||
|                                     // New value is different | ||||
|                                     changed = true; | ||||
|                                     changes.info = oldInfo; | ||||
|                                     editing_node.info = newInfo; | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 // No existing info | ||||
|                                 if (newInfo.trim() !== "") { | ||||
|                                     // New value is not blank | ||||
|                                     changed = true; | ||||
|                                     changes.info = undefined; | ||||
|                                     editing_node.info = newInfo; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         if (changed) { | ||||
|                             var wasChanged = editing_node.changed; | ||||
|                             editing_node.changed = true; | ||||
|                             RED.nodes.dirty(true); | ||||
|                             var historyEvent = { | ||||
|                                 t:'edit', | ||||
|                                 node:editing_node, | ||||
|                                 changes:changes, | ||||
|                                 dirty:wasDirty, | ||||
|                                 changed:wasChanged | ||||
|                             }; | ||||
|                             RED.history.push(historyEvent); | ||||
|                             RED.events.emit("groups:change",editing_node); | ||||
|                         } | ||||
|                         editing_node.dirty = true; | ||||
|                         RED.tray.close(); | ||||
|                         RED.view.redraw(true); | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             resize: function(size) { | ||||
|                 editTrayWidthCache['group'] = size.width; | ||||
|                 $(".red-ui-tray-content").height(size.height - 50); | ||||
|                 // var form = $(".red-ui-tray-content form").height(dimensions.height - 50 - 40); | ||||
|                 // if (editing_node && editing_node._def.oneditresize) { | ||||
|                 //     try { | ||||
|                 //         editing_node._def.oneditresize.call(editing_node,{width:form.width(),height:form.height()}); | ||||
|                 //     } catch(err) { | ||||
|                 //         console.log("oneditresize",editing_node.id,editing_node.type,err.toString()); | ||||
|                 //     } | ||||
|                 // } | ||||
|             }, | ||||
|             open: function(tray, done) { | ||||
|                 var trayFooter = tray.find(".red-ui-tray-footer"); | ||||
|                 var trayFooterLeft = $("<div/>", { | ||||
|                     class: "red-ui-tray-footer-left" | ||||
|                 }).appendTo(trayFooter) | ||||
|                 var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                 trayBody.parent().css('overflow','hidden'); | ||||
|  | ||||
|                 var editorTabEl = $('<ul></ul>').appendTo(trayBody); | ||||
|                 var editorContent = $('<div></div>').appendTo(trayBody); | ||||
|  | ||||
|                 var editorTabs = RED.tabs.create({ | ||||
|                     element:editorTabEl, | ||||
|                     onchange:function(tab) { | ||||
|                         editorContent.children().hide(); | ||||
|                         if (tab.onchange) { | ||||
|                             tab.onchange.call(tab); | ||||
|                         } | ||||
|                         tab.content.show(); | ||||
|                         if (finishedBuilding) { | ||||
|                             RED.tray.resize(); | ||||
|                         } | ||||
|                     }, | ||||
|                     collapsible: true, | ||||
|                     menu: false | ||||
|                 }); | ||||
|  | ||||
|                 var nodePropertiesTab = { | ||||
|                     id: "editor-tab-properties", | ||||
|                     label: RED._("editor-tab.properties"), | ||||
|                     name: RED._("editor-tab.properties"), | ||||
|                     content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), | ||||
|                     iconClass: "fa fa-cog" | ||||
|                 }; | ||||
|                 buildEditForm(nodePropertiesTab.content,"dialog-form","group","node-red",group); | ||||
|  | ||||
|                 editorTabs.addTab(nodePropertiesTab); | ||||
|  | ||||
|                 var descriptionTab = { | ||||
|                     id: "editor-tab-description", | ||||
|                     label: RED._("editor-tab.description"), | ||||
|                     name: RED._("editor-tab.description"), | ||||
|                     content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), | ||||
|                     iconClass: "fa fa-file-text-o", | ||||
|                     onchange: function() { | ||||
|                         nodeInfoEditor.focus(); | ||||
|                     } | ||||
|                 }; | ||||
|                 editorTabs.addTab(descriptionTab); | ||||
|                 nodeInfoEditor = buildDescriptionForm(descriptionTab.content,editing_node); | ||||
|                 prepareEditDialog(group,group._def,"node-input", function() { | ||||
|                     trayBody.i18n(); | ||||
|                     finishedBuilding = true; | ||||
|                     done(); | ||||
|                 }); | ||||
|             }, | ||||
|             close: function() { | ||||
|                 if (RED.view.state() != RED.state.IMPORT_DRAGGING) { | ||||
|                     RED.view.state(RED.state.DEFAULT); | ||||
|                 } | ||||
|                 RED.sidebar.info.refresh(editing_node); | ||||
|                 nodeInfoEditor.destroy(); | ||||
|                 nodeInfoEditor = null; | ||||
|                 editStack.pop(); | ||||
|                 editing_node = null; | ||||
|             }, | ||||
|             show: function() { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (editTrayWidthCache.hasOwnProperty('group')) { | ||||
|             trayOptions.width = editTrayWidthCache['group']; | ||||
|         } | ||||
|         RED.tray.show(trayOptions); | ||||
|     } | ||||
|  | ||||
|     function showTypeEditor(type, options) { | ||||
|         if (customEditTypes.hasOwnProperty(type)) { | ||||
|             if (editStack.length > 0) { | ||||
| @@ -2537,6 +2775,7 @@ RED.editor = (function() { | ||||
|         edit: showEditDialog, | ||||
|         editConfig: showEditConfigNodeDialog, | ||||
|         editSubflow: showEditSubflowDialog, | ||||
|         editGroup: showEditGroupDialog, | ||||
|         editJavaScript: function(options) { showTypeEditor("_js",options) }, | ||||
|         editExpression: function(options) { showTypeEditor("_expression", options) }, | ||||
|         editJSON: function(options) { showTypeEditor("_json", options) }, | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
|  **/ | ||||
| (function() { | ||||
|  | ||||
|     var template = '<script type="text/x-red" data-template-name="_buffer"><div id="red-ui-editor-type-buffer-panels"><div id="red-ui-editor-type-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button class="red-ui-editor-type-buffer-type red-ui-button red-ui-button-small"><i class="fa fa-exclamation-circle"></i> <span id="red-ui-editor-type-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="red-ui-editor-type-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></button></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="red-ui-editor-type-buffer-str"></div></div></div><div id="red-ui-editor-type-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px"><div class="node-text-editor" id="red-ui-editor-type-buffer-bin"></div></div></div></div></script>'; | ||||
|     var template = '<script type="text/x-red" data-template-name="_buffer"><div id="red-ui-editor-type-buffer-panels"><div id="red-ui-editor-type-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button class="red-ui-editor-type-buffer-type red-ui-button red-ui-button-small"><i class="fa fa-exclamation-circle"></i> <span id="red-ui-editor-type-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="red-ui-editor-type-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></button></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="red-ui-editor-type-buffer-str"></div></div></div><div id="red-ui-editor-type-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px; margin-bottom:0;"><div class="node-text-editor" id="red-ui-editor-type-buffer-bin"></div></div></div></div></script>'; | ||||
|  | ||||
|     function stringToUTF8Array(str) { | ||||
|         var data = []; | ||||
| @@ -187,8 +187,7 @@ | ||||
|  | ||||
|                     $(".red-ui-editor-type-buffer-type").on("click", function(e) { | ||||
|                         e.preventDefault(); | ||||
|                         RED.sidebar.info.set(RED._("bufferEditor.modeDesc")); | ||||
|                         RED.sidebar.info.show(); | ||||
|                         RED.sidebar.help.set(RED._("bufferEditor.modeDesc")); | ||||
|                     }) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -237,8 +237,7 @@ | ||||
|                     var changeTimer; | ||||
|                     $(".red-ui-editor-type-expression-legacy").on("click", function(e) { | ||||
|                         e.preventDefault(); | ||||
|                         RED.sidebar.info.set(RED._("expressionEditor.compatModeDesc")); | ||||
|                         RED.sidebar.info.show(); | ||||
|                         RED.sidebar.help.set(RED._("expressionEditor.compatModeDesc")); | ||||
|                     }) | ||||
|                     var testExpression = function() { | ||||
|                         var value = testDataEditor.getValue(); | ||||
| @@ -318,9 +317,9 @@ | ||||
|                             var p2 = $("#red-ui-editor-type-expression-panel-info > .form-row > div:first-child"); | ||||
|                             p2Height -= p2.outerHeight(true) + 20; | ||||
|                             $(".red-ui-editor-type-expression-tab-content").height(p2Height); | ||||
|                             $("#red-ui-editor-type-expression-test-data").css("height",(p2Height-5)+"px"); | ||||
|                             $("#red-ui-editor-type-expression-test-data").css("height",(p2Height-25)+"px"); | ||||
|                             testDataEditor.resize(); | ||||
|                             $("#red-ui-editor-type-expression-test-result").css("height",(p2Height-5)+"px"); | ||||
|                             $("#red-ui-editor-type-expression-test-result").css("height",(p2Height-25)+"px"); | ||||
|                             testResultEditor.resize(); | ||||
|                         } | ||||
|                     }); | ||||
|   | ||||
| @@ -87,6 +87,9 @@ | ||||
|                         expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); | ||||
|                     } | ||||
|                     dialogForm.i18n(); | ||||
|                     setTimeout(function() { | ||||
|                         expressionEditor.focus(); | ||||
|                     },300); | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     expressionEditor.destroy(); | ||||
|   | ||||
							
								
								
									
										653
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/group.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										653
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/group.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,653 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| RED.group = (function() { | ||||
|  | ||||
|     var _groupEditTemplate = '<script type="text/x-red" data-template-name="group">'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+ | ||||
|             '<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+ | ||||
|         '</div>'+ | ||||
|  | ||||
|         // '<div class="node-input-group-style-tools"><span class="button-group"><button class="red-ui-button red-ui-button-small">Use default style</button><button class="red-ui-button red-ui-button-small">Set as default style</button></span></div>'+ | ||||
|  | ||||
|         '<div class="form-row" id="node-input-row-style-stroke">'+ | ||||
|             '<label data-i18n="editor:common.label.style"></label>'+ | ||||
|             '<label style="width: 70px;margin-right:10px" for="node-input-style-stroke" data-i18n="editor:common.label.line"></label>'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row" style="padding-left: 100px;" id="node-input-row-style-fill">'+ | ||||
|             '<label style="width: 70px;margin-right: 10px "  for="node-input-style-fill" data-i18n="editor:common.label.fill"></label>'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row">'+ | ||||
|             '<label for="node-input-style-label" data-i18n="editor:common.label.label"></label>'+ | ||||
|             '<input type="checkbox" id="node-input-style-label"/>'+ | ||||
|         '</div>'+ | ||||
|         '<div class="form-row" id="node-input-row-style-label-options">'+ | ||||
|             '<div style="margin-left: 100px; display: inline-block">'+ | ||||
|                 '<div class="form-row">'+ | ||||
|                     '<span style="display: inline-block; min-width: 140px"  id="node-input-row-style-label-color">'+ | ||||
|                         '<label style="width: 70px;margin-right: 10px" for="node-input-style-fill" data-i18n="editor:common.label.color"></label>'+ | ||||
|                     '</span>'+ | ||||
|                 '</div>'+ | ||||
|                 '<div class="form-row">'+ | ||||
|                     '<span style="display: inline-block; min-width: 140px;" id="node-input-row-style-label-position">'+ | ||||
|                         '<label style="width: 70px;margin-right: 10px " for="node-input-style-label-position" data-i18n="editor:common.label.position"></label>'+ | ||||
|                     '</span>'+ | ||||
|                 '</div>'+ | ||||
|             '</div>'+ | ||||
|         '</div>'+ | ||||
|  | ||||
|         '</script>'; | ||||
|  | ||||
|     var colorPalette = [ | ||||
|         "#ff0000", | ||||
|         "#ffC000", | ||||
|         "#ffff00", | ||||
|         "#92d04f", | ||||
|         "#0070c0", | ||||
|         "#001f60", | ||||
|         "#6f2fa0", | ||||
|         "#000000", | ||||
|         "#777777" | ||||
|     ] | ||||
|     var colorSteps = 3; | ||||
|     var colorCount = colorPalette.length; | ||||
|     for (var i=0,len=colorPalette.length*colorSteps;i<len;i++) { | ||||
|         var ci = i%colorCount; | ||||
|         var j = Math.floor(i/colorCount)+1; | ||||
|         var c = colorPalette[ci]; | ||||
|         var r = parseInt(c.substring(1, 3), 16); | ||||
|         var g = parseInt(c.substring(3, 5), 16); | ||||
|         var b = parseInt(c.substring(5, 7), 16); | ||||
|         var dr = (255-r)/(colorSteps+((ci===colorCount-1) ?0:1)); | ||||
|         var dg = (255-g)/(colorSteps+((ci===colorCount-1) ?0:1)); | ||||
|         var db = (255-b)/(colorSteps+((ci===colorCount-1) ?0:1)); | ||||
|         r = Math.min(255,Math.floor(r+j*dr)); | ||||
|         g = Math.min(255,Math.floor(g+j*dg)); | ||||
|         b = Math.min(255,Math.floor(b+j*db)); | ||||
|         var s = ((r<<16) + (g<<8) + b).toString(16); | ||||
|         colorPalette.push('#'+'000000'.slice(0, 6-s.length)+s); | ||||
|     } | ||||
|  | ||||
|     var defaultGroupStyle = {}; | ||||
|  | ||||
|     var groupDef = { | ||||
|         defaults:{ | ||||
|             name:{value:""}, | ||||
|             style:{value:{}}, | ||||
|             nodes:{value:[]} | ||||
|         }, | ||||
|         category: "config", | ||||
|         oneditprepare: function() { | ||||
|             var style = this.style || {}; | ||||
|             RED.colorPicker.create({ | ||||
|                 id:"node-input-style-stroke", | ||||
|                 value: style.stroke || "#a4a4a4", | ||||
|                 palette: colorPalette, | ||||
|                 cellPerRow: colorCount, | ||||
|                 cellWidth: 16, | ||||
|                 cellHeight: 16, | ||||
|                 cellMargin: 3, | ||||
|                 none: true, | ||||
|                 opacity: style['stroke-opacity'] || 1.0 | ||||
|             }).appendTo("#node-input-row-style-stroke"); | ||||
|             RED.colorPicker.create({ | ||||
|                 id:"node-input-style-fill", | ||||
|                 value: style.fill || "none", | ||||
|                 palette: colorPalette, | ||||
|                 cellPerRow: colorCount, | ||||
|                 cellWidth: 16, | ||||
|                 cellHeight: 16, | ||||
|                 cellMargin: 3, | ||||
|                 none: true, | ||||
|                 opacity: style['fill-opacity'] || 1.0 | ||||
|             }).appendTo("#node-input-row-style-fill"); | ||||
|  | ||||
|             createLayoutPicker({ | ||||
|                 id:"node-input-style-label-position", | ||||
|                 value:style["label-position"] || "nw" | ||||
|             }).appendTo("#node-input-row-style-label-position"); | ||||
|  | ||||
|             RED.colorPicker.create({ | ||||
|                 id:"node-input-style-color", | ||||
|                 value: style.color || "#a4a4a4", | ||||
|                 palette: colorPalette, | ||||
|                 cellPerRow: colorCount, | ||||
|                 cellWidth: 16, | ||||
|                 cellHeight: 16, | ||||
|                 cellMargin: 3 | ||||
|             }).appendTo("#node-input-row-style-label-color"); | ||||
|  | ||||
|             $("#node-input-style-label").toggleButton({ | ||||
|                 enabledLabel: RED._("editor.show"), | ||||
|                 disabledLabel: RED._("editor.hide") | ||||
|             }) | ||||
|  | ||||
|  | ||||
|             $("#node-input-style-label").on("change", function(evt) { | ||||
|                 $("#node-input-row-style-label-options").toggle($(this).prop("checked")); | ||||
|             }) | ||||
|             $("#node-input-style-label").prop("checked", this.style.label) | ||||
|             $("#node-input-style-label").trigger("change"); | ||||
|  | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             this.style.stroke = $("#node-input-style-stroke").val(); | ||||
|             this.style.fill = $("#node-input-style-fill").val(); | ||||
|             this.style["stroke-opacity"] = $("#node-input-style-stroke-opacity").val(); | ||||
|             this.style["fill-opacity"] = $("#node-input-style-fill-opacity").val(); | ||||
|             this.style.label = $("#node-input-style-label").prop("checked"); | ||||
|             if (this.style.label) { | ||||
|                 this.style["label-position"] = $("#node-input-style-label-position").val(); | ||||
|                 this.style.color = $("#node-input-style-color").val(); | ||||
|             } else { | ||||
|                 delete this.style["label-position"]; | ||||
|                 delete this.style.color; | ||||
|             } | ||||
|  | ||||
|             if (this.style["stroke-opacity"] === "1") { | ||||
|                 delete this.style["stroke-opacity"] | ||||
|             } | ||||
|             if (this.style["fill-opacity"] === "1") { | ||||
|                 delete this.style["fill-opacity"] | ||||
|             } | ||||
|             this.resize = true; | ||||
|         }, | ||||
|         set:{ | ||||
|             module: "node-red" | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|  | ||||
|         RED.events.on("view:selection-changed",function(selection) { | ||||
|             RED.menu.setDisabled("menu-item-group-group",!!!selection.nodes); | ||||
|             RED.menu.setDisabled("menu-item-group-ungroup",!!!selection.nodes || selection.nodes.filter(function(n) { return n.type==='group'}).length === 0); | ||||
|             RED.menu.setDisabled("menu-item-group-merge",!!!selection.nodes); | ||||
|             RED.menu.setDisabled("menu-item-group-remove",!!!selection.nodes || selection.nodes.filter(function(n) { return !!n.g }).length === 0); | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:group-selection", function() { groupSelection() }) | ||||
|         RED.actions.add("core:ungroup-selection", function() { ungroupSelection() }) | ||||
|         RED.actions.add("core:merge-selection-to-group", function() { mergeSelection() }) | ||||
|         RED.actions.add("core:remove-selection-from-group", function() { removeSelection() }) | ||||
|         RED.actions.add("core:copy-group-style", function() { copyGroupStyle() }); | ||||
|         RED.actions.add("core:paste-group-style", function() { pasteGroupStyle() }); | ||||
|  | ||||
|         $(_groupEditTemplate).appendTo("#red-ui-editor-node-configs"); | ||||
|  | ||||
|         var groupStyleDiv = $("<div>",{ | ||||
|             class:"red-ui-flow-group-body", | ||||
|             style: "position: absolute; top: -1000px;" | ||||
|         }).appendTo(document.body); | ||||
|         var groupStyle = getComputedStyle(groupStyleDiv[0]); | ||||
|         defaultGroupStyle = { | ||||
|             stroke: convertColorToHex(groupStyle.stroke), | ||||
|             "stroke-opacity": groupStyle.strokeOpacity, | ||||
|             fill: convertColorToHex(groupStyle.fill), | ||||
|             "fill-opacity": groupStyle.fillOpacity | ||||
|         } | ||||
|         groupStyleDiv.remove(); | ||||
|     } | ||||
|  | ||||
|     function convertColorToHex(c) { | ||||
|         var m = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(c); | ||||
|         if (m) { | ||||
|             var s = ((parseInt(m[1])<<16) + (parseInt(m[2])<<8) + parseInt(m[3])).toString(16) | ||||
|             return '#'+'000000'.slice(0, 6-s.length)+s; | ||||
|         } | ||||
|         return c; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     var groupStyleClipboard; | ||||
|  | ||||
|     function copyGroupStyle() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') { | ||||
|             groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style)); | ||||
|             RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"}) | ||||
|         } | ||||
|     } | ||||
|     function pasteGroupStyle() { | ||||
|         if (groupStyleClipboard) { | ||||
|             var selection = RED.view.selection(); | ||||
|             if (selection.nodes) { | ||||
|                 var historyEvent = { | ||||
|                     t:'multi', | ||||
|                     events:[], | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 } | ||||
|                 selection.nodes.forEach(function(n) { | ||||
|                     if (n.type === 'group') { | ||||
|                         historyEvent.events.push({ | ||||
|                             t: "edit", | ||||
|                             node: n, | ||||
|                             changes: { | ||||
|                                 style: JSON.parse(JSON.stringify(n.style)) | ||||
|                             }, | ||||
|                             dirty: RED.nodes.dirty() | ||||
|                         }); | ||||
|                         n.style = JSON.parse(JSON.stringify(groupStyleClipboard)); | ||||
|                         n.dirty = true; | ||||
|  | ||||
|                     } | ||||
|                 }) | ||||
|                 if (historyEvent.events.length > 0) { | ||||
|                     RED.history.push(historyEvent); | ||||
|                     RED.nodes.dirty(true); | ||||
|                     RED.view.redraw(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function groupSelection() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes) { | ||||
|             var group = createGroup(selection.nodes); | ||||
|             if (group) { | ||||
|                 var historyEvent = { | ||||
|                     t:"createGroup", | ||||
|                     groups: [ group ], | ||||
|                     dirty: RED.nodes.dirty() | ||||
|                 } | ||||
|                 RED.history.push(historyEvent); | ||||
|                 RED.view.select({nodes:[group]}); | ||||
|                 RED.nodes.dirty(true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function ungroupSelection() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes) { | ||||
|             var newSelection = []; | ||||
|             groups = selection.nodes.filter(function(n) { return n.type === "group" }); | ||||
|  | ||||
|             var historyEvent = { | ||||
|                 t:"ungroup", | ||||
|                 groups: [ ], | ||||
|                 dirty: RED.nodes.dirty() | ||||
|             } | ||||
|             RED.history.push(historyEvent); | ||||
|  | ||||
|  | ||||
|             groups.forEach(function(g) { | ||||
|                 newSelection = newSelection.concat(ungroup(g)) | ||||
|                 historyEvent.groups.push(g); | ||||
|             }) | ||||
|             RED.history.push(historyEvent); | ||||
|             RED.view.select({nodes:newSelection}) | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function ungroup(g) { | ||||
|         var nodes = []; | ||||
|         var parentGroup = RED.nodes.group(g.g); | ||||
|         g.nodes.forEach(function(n) { | ||||
|             nodes.push(n); | ||||
|             if (parentGroup) { | ||||
|                 // Move nodes to parent group | ||||
|                 n.g = parentGroup.id; | ||||
|                 parentGroup.nodes.push(n); | ||||
|                 parentGroup.dirty = true; | ||||
|                 n.dirty = true; | ||||
|             } else { | ||||
|                 delete n.g; | ||||
|             } | ||||
|             if (n.type === 'group') { | ||||
|                 RED.events.emit("groups:change",n) | ||||
|             } else { | ||||
|                 RED.events.emit("nodes:change",n) | ||||
|             } | ||||
|         }) | ||||
|         RED.nodes.removeGroup(g); | ||||
|         return nodes; | ||||
|     } | ||||
|  | ||||
|     function mergeSelection() { | ||||
|         // TODO: this currently creates an entirely new group. Need to merge properties | ||||
|         //       of any existing group | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes) { | ||||
|             var nodes = []; | ||||
|  | ||||
|             var historyEvent = { | ||||
|                 t: "multi", | ||||
|                 events: [] | ||||
|             } | ||||
|             var ungroupHistoryEvent = { | ||||
|                 t: "ungroup", | ||||
|                 groups: [] | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var n; | ||||
|             var parentGroup; | ||||
|             // First pass, check they are all in the same parent | ||||
|             // TODO: DRY mergeSelection,removeSelection,... | ||||
|             for (var i=0; i<selection.nodes.length; i++) { | ||||
|                 n = selection.nodes[i]; | ||||
|                 if (i === 0) { | ||||
|                     parentGroup = n.g; | ||||
|                 } else if (n.g !== parentGroup) { | ||||
|                     RED.notify(RED._("group.errors.cannotCreateDiffGroups"),"error"); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             // Second pass, ungroup any groups in the selection and add their contents | ||||
|             // to the selection | ||||
|             for (var i=0; i<selection.nodes.length; i++) { | ||||
|                 n = selection.nodes[i]; | ||||
|                 if (n.type === "group") { | ||||
|                     ungroupHistoryEvent.groups.push(n); | ||||
|                     nodes = nodes.concat(ungroup(n)); | ||||
|                 } else { | ||||
|                     nodes.push(n); | ||||
|                 } | ||||
|                 n.dirty = true; | ||||
|             } | ||||
|             if (ungroupHistoryEvent.groups.length > 0) { | ||||
|                 historyEvent.events.push(ungroupHistoryEvent); | ||||
|             } | ||||
|             // Finally, create the new group | ||||
|             var group = createGroup(nodes); | ||||
|             if (group) { | ||||
|                 RED.view.select({nodes:[group]}) | ||||
|             } | ||||
|             historyEvent.events.push({ | ||||
|                 t:"createGroup", | ||||
|                 groups: [ group ], | ||||
|                 dirty: RED.nodes.dirty() | ||||
|             }); | ||||
|             RED.history.push(historyEvent); | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function removeSelection() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes) { | ||||
|             var nodes = []; | ||||
|             var n; | ||||
|             var parentGroup = RED.nodes.group(selection.nodes[0].g); | ||||
|             if (parentGroup) { | ||||
|                 try { | ||||
|                     removeFromGroup(parentGroup,selection.nodes,true); | ||||
|                     var historyEvent = { | ||||
|                         t: "removeFromGroup", | ||||
|                         dirty: RED.nodes.dirty(), | ||||
|                         group: parentGroup, | ||||
|                         nodes: selection.nodes | ||||
|                     } | ||||
|                     RED.history.push(historyEvent); | ||||
|                     RED.nodes.dirty(true); | ||||
|                 } catch(err) { | ||||
|                     RED.notify(err,"error"); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             RED.view.select({nodes:selection.nodes}) | ||||
|         } | ||||
|     } | ||||
|     function createGroup(nodes) { | ||||
|         if (nodes.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|         if (nodes.filter(function(n) { return n.type === "subflow" }).length > 0) { | ||||
|             RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error"); | ||||
|             return; | ||||
|         } | ||||
|         // nodes is an array | ||||
|         // each node must be on the same tab (z) | ||||
|         var group = { | ||||
|             id: RED.nodes.id(), | ||||
|             type: 'group', | ||||
|             nodes: [], | ||||
|             style: JSON.parse(JSON.stringify(defaultGroupStyle)), | ||||
|             x: Number.POSITIVE_INFINITY, | ||||
|             y: Number.POSITIVE_INFINITY, | ||||
|             w: 0, | ||||
|             h: 0, | ||||
|             _def: RED.group.def | ||||
|         } | ||||
|  | ||||
|         group.z = nodes[0].z; | ||||
|         RED.nodes.addGroup(group); | ||||
|  | ||||
|         try { | ||||
|             addToGroup(group,nodes); | ||||
|         } catch(err) { | ||||
|             RED.notify(err,"error"); | ||||
|             return; | ||||
|         } | ||||
|         return group; | ||||
|     } | ||||
|     function addToGroup(group,nodes) { | ||||
|         if (!Array.isArray(nodes)) { | ||||
|             nodes = [nodes]; | ||||
|         } | ||||
|         var i,n,z; | ||||
|         var g; | ||||
|         // First pass - validate we can safely add these nodes to the group | ||||
|         for (i=0;i<nodes.length;i++) { | ||||
|             n = nodes[i] | ||||
|             if (!n.z) { | ||||
|                 throw new Error("Cannot add node without a z property to a group") | ||||
|             } | ||||
|             if (!z) { | ||||
|                 z = n.z; | ||||
|             } else if (z !== n.z) { | ||||
|                 throw new Error("Cannot add nooes with different z properties") | ||||
|             } | ||||
|             if (n.g) { | ||||
|                 // This is already in a group. | ||||
|                 //  - check they are all in the same group | ||||
|                 if (!g) { | ||||
|                     if (i!==0) { | ||||
|                         // TODO: this might be ok when merging groups | ||||
|                         throw new Error(RED._("group.errors.cannotCreateDiffGroups")) | ||||
|                     } | ||||
|                     g = n.g | ||||
|                 } | ||||
|             } | ||||
|             if (g !== n.g) { | ||||
|                 throw new Error(RED._("group.errors.cannotCreateDiffGroups")) | ||||
|             } | ||||
|         } | ||||
|         // The nodes are already in a group. The assumption is they should be | ||||
|         // wrapped in the newly provided group, and that group added to in their | ||||
|         // place to the existing containing group. | ||||
|         if (g) { | ||||
|             g = RED.nodes.group(g); | ||||
|             g.nodes.push(group); | ||||
|             g.dirty = true; | ||||
|             group.g = g.id; | ||||
|         } | ||||
|         // Second pass - add them to the group | ||||
|         for (i=0;i<nodes.length;i++) { | ||||
|             n = nodes[i]; | ||||
|             if (n.type !== "subflow") { | ||||
|                 if (g && n.g === g.id) { | ||||
|                     var ni = g.nodes.indexOf(n); | ||||
|                     if (ni > -1) { | ||||
|                         g.nodes.splice(ni,1) | ||||
|                     } | ||||
|                 } | ||||
|                 n.g = group.id; | ||||
|                 n.dirty = true; | ||||
|                 group.nodes.push(n); | ||||
|                 group.x = Math.min(group.x,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0)); | ||||
|                 group.y = Math.min(group.y,n.y-n.h/2-25); | ||||
|                 group.w = Math.max(group.w,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0) - group.x); | ||||
|                 group.h = Math.max(group.h,n.y+n.h/2+25-group.y); | ||||
|                 if (n.type === 'group') { | ||||
|                     RED.events.emit("groups:change",n) | ||||
|                 } else { | ||||
|                     RED.events.emit("nodes:change",n) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (g) { | ||||
|             RED.events.emit("groups:change",group) | ||||
|         } | ||||
|         markDirty(group); | ||||
|     } | ||||
|     function removeFromGroup(group, nodes, reparent) { | ||||
|         if (!Array.isArray(nodes)) { | ||||
|             nodes = [nodes]; | ||||
|         } | ||||
|         var n; | ||||
|         // First pass, check they are all in the same parent | ||||
|         // TODO: DRY mergeSelection,removeSelection,... | ||||
|         for (var i=0; i<nodes.length; i++) { | ||||
|             if (nodes[i].g !== group.id) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         var parentGroup = RED.nodes.group(group.g); | ||||
|         for (var i=0; i<nodes.length; i++) { | ||||
|             n = nodes[i]; | ||||
|             n.dirty = true; | ||||
|             var index = group.nodes.indexOf(n); | ||||
|             group.nodes.splice(index,1); | ||||
|             if (reparent && group.g) { | ||||
|                 n.g = group.g | ||||
|                 parentGroup.nodes.push(n); | ||||
|             } else { | ||||
|                 delete n.g; | ||||
|             } | ||||
|             if (n.type === 'group') { | ||||
|                 RED.events.emit("groups:change",n) | ||||
|             } else { | ||||
|                 RED.events.emit("nodes:change",n) | ||||
|             } | ||||
|         } | ||||
|         markDirty(group); | ||||
|     } | ||||
|  | ||||
|     function getNodes(group,recursive) { | ||||
|         var nodes = []; | ||||
|         group.nodes.forEach(function(n) { | ||||
|             nodes.push(n); | ||||
|             if (recursive && n.type === 'group') { | ||||
|                 nodes = nodes.concat(getNodes(n,recursive)) | ||||
|             } | ||||
|         }) | ||||
|         return nodes; | ||||
|     } | ||||
|  | ||||
|     function groupContains(group,item) { | ||||
|         if (item.g === group.id) { | ||||
|             return true; | ||||
|         } | ||||
|         for (var i=0;i<group.nodes.length;i++) { | ||||
|             if (group.nodes[i].type === "group") { | ||||
|                 if (groupContains(group.nodes[i],item)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|     function getRootGroup(group) { | ||||
|         if (!group.g) { | ||||
|             return group; | ||||
|         } | ||||
|         return getRootGroup(RED.nodes.group(group.g)) | ||||
|     } | ||||
|  | ||||
|     function createLayoutPicker(options) { | ||||
|  | ||||
|         var container = $("<div>",{style:"display:inline-block"}); | ||||
|         var layoutHiddenInput = $("<input/>", { id: options.id, type: "hidden", value: options.value }).appendTo(container); | ||||
|  | ||||
|         var layoutButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(container); | ||||
|         $('<i class="fa fa-caret-down"></i>').appendTo(layoutButton); | ||||
|  | ||||
|         var layoutDispContainer = $('<div>',{class:"red-ui-search-result-node"}).appendTo(layoutButton); | ||||
|         var layoutDisp = $('<div>',{class:"red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"}).appendTo(layoutDispContainer); | ||||
|  | ||||
|         var refreshDisplay = function() { | ||||
|             var val = layoutHiddenInput.val(); | ||||
|             layoutDisp.removeClass().addClass("red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"+val) | ||||
|         } | ||||
|         layoutButton.on("click", function(e) { | ||||
|             var picker = $("<div/>", { | ||||
|                 class: "red-ui-group-layout-picker" | ||||
|             }).css({ | ||||
|                 width: "126px" | ||||
|             }); | ||||
|  | ||||
|             var row = null; | ||||
|  | ||||
|             row = $("<div/>").appendTo(picker); | ||||
|  | ||||
|             for (var y=0;y<2;y++) { //red-ui-group-layout-text-pos | ||||
|                 var yComponent= "ns"[y]; | ||||
|                 row = $("<div/>").appendTo(picker); | ||||
|                 for (var x=0;x<3;x++) { | ||||
|                     var xComponent = ["w","","e"][x]; | ||||
|                     var val = yComponent+xComponent; | ||||
|                     var button = $("<button/>", { class:"red-ui-search-result-node","data-pos":val }).appendTo(row); | ||||
|                     button.on("click",  function (e) { | ||||
|                         e.preventDefault(); | ||||
|                         layoutHiddenInput.val($(this).data("pos")); | ||||
|                         layoutPanel.hide() | ||||
|                         refreshDisplay(); | ||||
|                     }); | ||||
|                     $('<div>',{class:"red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"+val}).appendTo(button); | ||||
|  | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             refreshDisplay(); | ||||
|             var layoutPanel = RED.popover.panel(picker); | ||||
|             layoutPanel.show({ | ||||
|                 target: layoutButton | ||||
|             }) | ||||
|         }) | ||||
|  | ||||
|         refreshDisplay(); | ||||
|  | ||||
|         return container; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function markDirty(group) { | ||||
|         group.dirty = true; | ||||
|         while(group) { | ||||
|             group.dirty = true; | ||||
|             group = RED.nodes.group(group.g); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         def: groupDef, | ||||
|         init: init, | ||||
|         createGroup: createGroup, | ||||
|         ungroup: ungroup, | ||||
|         addToGroup: addToGroup, | ||||
|         removeFromGroup: removeFromGroup, | ||||
|         getNodes: getNodes, | ||||
|         contains: groupContains, | ||||
|         markDirty: markDirty | ||||
|     } | ||||
| })(); | ||||
| @@ -22,7 +22,7 @@ RED.library = (function() { | ||||
|  | ||||
|     var _libraryLookup = '<div id="red-ui-library-dialog-load" class="hide">'+ | ||||
|         '<form class="form-horizontal">'+ | ||||
|             '<div style="height: 400px; position:relative; ">'+ | ||||
|             '<div class="red-ui-library-dialog-box" style="height: 400px; position:relative; ">'+ | ||||
|                 '<div id="red-ui-library-dialog-load-panes">'+ | ||||
|                     '<div class="red-ui-panel" id="red-ui-library-dialog-load-browser"></div>'+ | ||||
|                     '<div class="red-ui-panel">'+ | ||||
| @@ -41,7 +41,7 @@ RED.library = (function() { | ||||
|  | ||||
|     var _librarySave = '<div id="red-ui-library-dialog-save" class="hide">'+ | ||||
|         '<form class="form-horizontal">'+ | ||||
|         '<div style="height: 400px; position:relative; ">'+ | ||||
|         '<div class="red-ui-library-dialog-box" style="height: 400px; position:relative; ">'+ | ||||
|             '<div id="red-ui-library-dialog-save-browser"></div>'+ | ||||
|             '<div class="form-row">'+ | ||||
|                 '<label data-i18n="clipboard.export.exportAs"></label><input id="red-ui-library-dialog-save-filename" type="text">'+ | ||||
| @@ -64,12 +64,14 @@ RED.library = (function() { | ||||
|  | ||||
|         var queryArgs = []; | ||||
|         var data = {}; | ||||
|         for (var i=0; i<activeLibrary.fields.length; i++) { | ||||
|         for (var i=0; i < activeLibrary.fields.length; i++) { | ||||
|             var field = activeLibrary.fields[i]; | ||||
|             if (field == "name") { | ||||
|             if (field === "name") { | ||||
|                 data.name = name; | ||||
|             } else if (typeof(field) === 'object') { | ||||
|                 data[field.name] = field.get(); | ||||
|             } else { | ||||
|                 data[field] = $("#"+elementPrefix+field).val(); | ||||
|                 data[field] = $("#" + elementPrefix + field).val(); | ||||
|             } | ||||
|         } | ||||
|         data.text = activeLibrary.editor.getValue(); | ||||
| @@ -254,6 +256,13 @@ RED.library = (function() { | ||||
|                     libraryEditor.renderer.$cursorLayer.element.style.opacity=0; | ||||
|                     libraryEditor.$blockScrolling = Infinity; | ||||
|  | ||||
|                     var dialogHeight = 400; | ||||
|                     var winHeight = $(window).height(); | ||||
|                     if (winHeight < 570) { | ||||
|                         dialogHeight = 400 - (570 - winHeight); | ||||
|                     } | ||||
|                     $("#red-ui-library-dialog-load .red-ui-library-dialog-box").height(dialogHeight); | ||||
|  | ||||
|                     $( "#red-ui-library-dialog-load" ).dialog("option","title",RED._("library.typeLibrary", {type:options.type})).dialog( "open" ); | ||||
|                 } | ||||
|             }, | ||||
| @@ -293,6 +302,15 @@ RED.library = (function() { | ||||
|                             saveLibraryBrowser.select(listing[0].children[0]); | ||||
|                         },200); | ||||
|                     }); | ||||
|  | ||||
|                     var dialogHeight = 400; | ||||
|                     var winHeight = $(window).height(); | ||||
|                     if (winHeight < 570) { | ||||
|                         dialogHeight = 400 - (570 - winHeight); | ||||
|                     } | ||||
|                     $("#red-ui-library-dialog-save .red-ui-library-dialog-box").height(dialogHeight); | ||||
|  | ||||
|  | ||||
|                     $( "#red-ui-library-dialog-save" ).dialog( "open" ); | ||||
|                 } | ||||
|             } | ||||
| @@ -518,14 +536,20 @@ RED.library = (function() { | ||||
|                     { | ||||
|                         text: RED._("common.label.load"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
|                         click: function () { | ||||
|                             if (selectedLibraryItem) { | ||||
|                                 var elementPrefix = activeLibrary.elementPrefix || "node-input-"; | ||||
|                                 for (var i=0; i<activeLibrary.fields.length; i++) { | ||||
|                                 for (var i = 0; i < activeLibrary.fields.length; i++) { | ||||
|                                     var field = activeLibrary.fields[i]; | ||||
|                                     $("#"+elementPrefix+field).val(selectedLibraryItem[field]); | ||||
|                                     if (typeof(field) === 'object') { | ||||
|                                         var val = selectedLibraryItem[field.name]; | ||||
|                                         field.set(val); | ||||
|                                     } | ||||
|                                     else { | ||||
|                                         $("#"+elementPrefix+field).val(selectedLibraryItem[field]); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 activeLibrary.editor.setValue(libraryEditor.getValue(),-1); | ||||
|                                 activeLibrary.editor.setValue(libraryEditor.getValue(), -1); | ||||
|                             } | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|   | ||||
| @@ -75,13 +75,16 @@ RED.palette.editor = (function() { | ||||
|             }); | ||||
|         }) | ||||
|     } | ||||
|     function installNodeModule(id,version,callback) { | ||||
|     function installNodeModule(id,version,url,callback) { | ||||
|         var requestBody = { | ||||
|             module: id | ||||
|         }; | ||||
|         if (version) { | ||||
|             requestBody.version = version; | ||||
|         } | ||||
|         if (url) { | ||||
|             requestBody.url = url; | ||||
|         } | ||||
|         $.ajax({ | ||||
|             url:"nodes", | ||||
|             type: "POST", | ||||
| @@ -627,7 +630,7 @@ RED.palette.editor = (function() { | ||||
|                         if ($(this).hasClass('disabled')) { | ||||
|                             return; | ||||
|                         } | ||||
|                         update(entry,loadedIndex[entry.name].version,container,function(err){}); | ||||
|                         update(entry,loadedIndex[entry.name].version,loadedIndex[entry.name].pkg_url,container,function(err){}); | ||||
|                     }) | ||||
|  | ||||
|  | ||||
| @@ -877,7 +880,7 @@ RED.palette.editor = (function() { | ||||
|  | ||||
|         $('<div id="red-ui-palette-module-install-shade" class="red-ui-palette-module-shade hide"><div class="red-ui-palette-module-shade-status"></div><img src="red/images/spin.svg" class="red-ui-palette-spinner"/></div>').appendTo(installTab); | ||||
|     } | ||||
|     function update(entry,version,container,done) { | ||||
|     function update(entry,version,url,container,done) { | ||||
|         if (RED.settings.theme('palette.editable') === false) { | ||||
|             done(new Error('Palette not editable')); | ||||
|             return; | ||||
| @@ -903,7 +906,7 @@ RED.palette.editor = (function() { | ||||
|                             RED.actions.invoke("core:show-event-log"); | ||||
|                         }); | ||||
|                         RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.name+" "+version); | ||||
|                         installNodeModule(entry.name,version,function(xhr) { | ||||
|                         installNodeModule(entry.name,version,url,function(xhr) { | ||||
|                             spinner.remove(); | ||||
|                             if (xhr) { | ||||
|                                 if (xhr.responseJSON) { | ||||
| @@ -1028,7 +1031,7 @@ RED.palette.editor = (function() { | ||||
|                     RED.actions.invoke("core:show-event-log"); | ||||
|                 }); | ||||
|                 RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.id+" "+entry.version); | ||||
|                 installNodeModule(entry.id,entry.version,function(xhr) { | ||||
|                 installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr) { | ||||
|                     spinner.remove(); | ||||
|                      if (xhr) { | ||||
|                          if (xhr.responseJSON) { | ||||
|   | ||||
| @@ -165,6 +165,7 @@ RED.palette = (function() { | ||||
|                     metaData = typeInfo.set.module+" : "; | ||||
|                 } | ||||
|                 metaData += type; | ||||
|                 $('<button type="button" onclick="RED.sidebar.help.show(\''+type+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-book"></i></button>').appendTo(popOverContent) | ||||
|                 $('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent); | ||||
|             } | ||||
|         } catch(err) { | ||||
| @@ -181,7 +182,11 @@ RED.palette = (function() { | ||||
|     function setIcon(element,sf) { | ||||
|         var icon_url = RED.utils.getNodeIcon(sf._def); | ||||
|         var iconContainer = element.find(".red-ui-palette-icon-container"); | ||||
|         RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|         var currentIcon = iconContainer.attr("data-palette-icon"); | ||||
|         if (currentIcon !== icon_url) { | ||||
|             iconContainer.attr("data-palette-icon", icon_url); | ||||
|             RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function getPaletteNode(type) { | ||||
| @@ -224,6 +229,7 @@ RED.palette = (function() { | ||||
|                 var iconContainer = $('<div/>', { | ||||
|                     class: "red-ui-palette-icon-container"+(((!def.align && def.inputs !== 0 && def.outputs === 0) || "right" === def.align) ? " red-ui-palette-icon-container-right" : "") | ||||
|                 }).appendTo(d); | ||||
|                 iconContainer.attr("data-palette-icon", icon_url); | ||||
|                 RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|             } | ||||
|  | ||||
| @@ -250,6 +256,7 @@ RED.palette = (function() { | ||||
|             var popover = RED.popover.create({ | ||||
|                 target:d, | ||||
|                 trigger: "hover", | ||||
|                 interactive: true, | ||||
|                 width: "300px", | ||||
|                 content: "hi", | ||||
|                 delay: { show: 750, hide: 50 } | ||||
| @@ -265,25 +272,28 @@ RED.palette = (function() { | ||||
|             //     html: true, | ||||
|             //     container:'body' | ||||
|             // }); | ||||
|             d.on("click", function() { | ||||
|                 RED.view.focus(); | ||||
|                 var helpText; | ||||
|                 if (nt.indexOf("subflow:") === 0) { | ||||
|                     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>'); | ||||
|                 } | ||||
|                 // Don't look too closely. RED.sidebar.info.set will set the 'Description' | ||||
|                 // section of the sidebar. Pass in the title of the Help section so it looks | ||||
|                 // right. | ||||
|                 RED.sidebar.info.set(helpText,RED._("sidebar.info.nodeHelp")); | ||||
|             }); | ||||
|             // d.on("click", function() { | ||||
|             //     RED.view.focus(); | ||||
|             //     var helpText; | ||||
|             //     if (nt.indexOf("subflow:") === 0) { | ||||
|             //         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>'); | ||||
|             //     } | ||||
|             //     // Don't look too closely. RED.sidebar.info.set will set the 'Description' | ||||
|             //     // section of the sidebar. Pass in the title of the Help section so it looks | ||||
|             //     // right. | ||||
|             //     RED.sidebar.type.show(helpText,RED._("sidebar.info.nodeHelp")); | ||||
|             // }); | ||||
|             var chart = $("#red-ui-workspace-chart"); | ||||
|             var chartSVG = $("#red-ui-workspace-chart>svg").get(0); | ||||
|             var activeSpliceLink; | ||||
|             var mouseX; | ||||
|             var mouseY; | ||||
|             var spliceTimer; | ||||
|             var groupTimer; | ||||
|             var activeGroup; | ||||
|             var hoverGroup; | ||||
|             var paletteWidth; | ||||
|             var paletteTop; | ||||
|             $(d).draggable({ | ||||
| @@ -295,16 +305,53 @@ RED.palette = (function() { | ||||
|                 start: function() { | ||||
|                     paletteWidth = $("#red-ui-palette").width(); | ||||
|                     paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top; | ||||
|                     hoverGroup = null; | ||||
|                     activeGroup = RED.view.getActiveGroup(); | ||||
|                     if (activeGroup) { | ||||
|                         document.getElementById("group_select_"+activeGroup.id).classList.add("red-ui-flow-group-active-hovered"); | ||||
|                     } | ||||
|                     RED.view.focus(); | ||||
|                 }, | ||||
|                 stop: function() { d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}}, | ||||
|                 stop: function() { | ||||
|                     d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false); | ||||
|                     if (hoverGroup) { | ||||
|                         document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); | ||||
|                     } | ||||
|                     if (activeGroup) { | ||||
|                         document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered"); | ||||
|                     } | ||||
|                     if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; } | ||||
|                     if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; } | ||||
|                 }, | ||||
|                 drag: function(e,ui) { | ||||
|                     var paletteNode = getPaletteNode(nt); | ||||
|                     ui.originalPosition.left = paletteNode.offset().left; | ||||
|                     mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft(); | ||||
|                     mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop(); | ||||
|                     if (!groupTimer) { | ||||
|                         groupTimer = setTimeout(function() { | ||||
|                             mouseX /= RED.view.scale(); | ||||
|                             mouseY /= RED.view.scale(); | ||||
|                             var group = RED.view.getGroupAtPoint(mouseX,mouseY); | ||||
|                             if (group !== hoverGroup) { | ||||
|                                 if (hoverGroup) { | ||||
|                                     document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered"); | ||||
|                                 } | ||||
|                                 if (group) { | ||||
|                                     document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered"); | ||||
|                                 } | ||||
|                                 hoverGroup = group; | ||||
|                                 if (hoverGroup) { | ||||
|                                     $(ui.helper).data('group',hoverGroup); | ||||
|                                 } else { | ||||
|                                     $(ui.helper).removeData('group'); | ||||
|                                 } | ||||
|                             } | ||||
|                             groupTimer = null; | ||||
|  | ||||
|                         },200) | ||||
|                     } | ||||
|                     if (def.inputs > 0 && def.outputs > 0) { | ||||
|                         mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft(); | ||||
|                         mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop(); | ||||
|                         if (!spliceTimer) { | ||||
|                             spliceTimer = setTimeout(function() { | ||||
|                                 var nodes = []; | ||||
| @@ -412,61 +459,67 @@ RED.palette = (function() { | ||||
|         categoryNode.show(); | ||||
|         paletteNode.show(); | ||||
|     } | ||||
|  | ||||
|     function refreshNodeTypes() { | ||||
|         RED.nodes.eachSubflow(function(sf) { | ||||
|             var paletteNode = getPaletteNode('subflow:'+sf.id); | ||||
|             var portInput = paletteNode.find(".red-ui-palette-port-input"); | ||||
|             var portOutput = paletteNode.find(".red-ui-palette-port-output"); | ||||
|         RED.nodes.eachSubflow(refreshSubflow) | ||||
|     } | ||||
|     function refreshSubflow(sf) { | ||||
|         var paletteNode = getPaletteNode('subflow:'+sf.id); | ||||
|         var portInput = paletteNode.find(".red-ui-palette-port-input"); | ||||
|         var portOutput = paletteNode.find(".red-ui-palette-port-output"); | ||||
|  | ||||
|             var paletteLabel = paletteNode.find(".red-ui-palette-label"); | ||||
|             paletteLabel.attr("class","red-ui-palette-label" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-label-right" : "")); | ||||
|         var paletteLabel = paletteNode.find(".red-ui-palette-label"); | ||||
|         paletteLabel.attr("class","red-ui-palette-label" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-label-right" : "")); | ||||
|  | ||||
|             var paletteIconContainer = paletteNode.find(".red-ui-palette-icon-container"); | ||||
|             paletteIconContainer.attr("class","red-ui-palette-icon-container" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-icon-container-right" : "")); | ||||
|         var paletteIconContainer = paletteNode.find(".red-ui-palette-icon-container"); | ||||
|         paletteIconContainer.attr("class","red-ui-palette-icon-container" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-icon-container-right" : "")); | ||||
|  | ||||
|             if (portInput.length === 0 && sf.in.length > 0) { | ||||
|                 var portIn = document.createElement("div"); | ||||
|                 portIn.className = "red-ui-palette-port red-ui-palette-port-input"; | ||||
|                 paletteNode.append(portIn); | ||||
|             } else if (portInput.length !== 0 && sf.in.length === 0) { | ||||
|                 portInput.remove(); | ||||
|             } | ||||
|         if (portInput.length === 0 && sf.in.length > 0) { | ||||
|             var portIn = document.createElement("div"); | ||||
|             portIn.className = "red-ui-palette-port red-ui-palette-port-input"; | ||||
|             paletteNode.append(portIn); | ||||
|         } else if (portInput.length !== 0 && sf.in.length === 0) { | ||||
|             portInput.remove(); | ||||
|         } | ||||
|  | ||||
|             if (portOutput.length === 0 && sf.out.length > 0) { | ||||
|                 var portOut = document.createElement("div"); | ||||
|                 portOut.className = "red-ui-palette-port red-ui-palette-port-output"; | ||||
|                 paletteNode.append(portOut); | ||||
|             } else if (portOutput.length !== 0 && sf.out.length === 0) { | ||||
|                 portOutput.remove(); | ||||
|             } | ||||
|         if (portOutput.length === 0 && sf.out.length > 0) { | ||||
|             var portOut = document.createElement("div"); | ||||
|             portOut.className = "red-ui-palette-port red-ui-palette-port-output"; | ||||
|             paletteNode.append(portOut); | ||||
|         } else if (portOutput.length !== 0 && sf.out.length === 0) { | ||||
|             portOutput.remove(); | ||||
|         } | ||||
|         var currentLabel = paletteNode.attr("data-palette-label"); | ||||
|         var currentInfo = paletteNode.attr("data-palette-info"); | ||||
|  | ||||
|         if (currentLabel !== sf.name || currentInfo !== sf.info) { | ||||
|             paletteNode.attr("data-palette-info",sf.info); | ||||
|             setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||"")); | ||||
|             setIcon(paletteNode,sf); | ||||
|         } | ||||
|         setIcon(paletteNode,sf); | ||||
|  | ||||
|             var currentCategory = paletteNode.data('category'); | ||||
|             var newCategory = (sf.category||"subflows"); | ||||
|             if (currentCategory !== newCategory) { | ||||
|                 var category = escapeCategory(newCategory); | ||||
|                 createCategory(newCategory,category,category,"node-red"); | ||||
|         var currentCategory = paletteNode.data('category'); | ||||
|         var newCategory = (sf.category||"subflows"); | ||||
|         if (currentCategory !== newCategory) { | ||||
|             var category = escapeCategory(newCategory); | ||||
|             createCategory(newCategory,category,category,"node-red"); | ||||
|  | ||||
|                 var currentCategoryNode = paletteNode.closest(".red-ui-palette-category"); | ||||
|                 var newCategoryNode = $("#red-ui-palette-"+category); | ||||
|                 newCategoryNode.append(paletteNode); | ||||
|                 if (newCategoryNode.find(".red-ui-palette-node").length === 1) { | ||||
|                     categoryContainers[category].open(); | ||||
|                 } | ||||
|  | ||||
|                 paletteNode.data('category',newCategory); | ||||
|                 if (currentCategoryNode.find(".red-ui-palette-node").length === 0) { | ||||
|                     if (currentCategoryNode.find("i").hasClass("expanded")) { | ||||
|                         currentCategoryNode.find(".red-ui-palette-content").slideToggle(); | ||||
|                         currentCategoryNode.find("i").toggleClass("expanded"); | ||||
|                     } | ||||
|                 } | ||||
|             var currentCategoryNode = paletteNode.closest(".red-ui-palette-category"); | ||||
|             var newCategoryNode = $("#red-ui-palette-"+category); | ||||
|             newCategoryNode.append(paletteNode); | ||||
|             if (newCategoryNode.find(".red-ui-palette-node").length === 1) { | ||||
|                 categoryContainers[category].open(); | ||||
|             } | ||||
|  | ||||
|             paletteNode.css("backgroundColor", sf.color); | ||||
|         }); | ||||
|             paletteNode.data('category',newCategory); | ||||
|             if (currentCategoryNode.find(".red-ui-palette-node").length === 0) { | ||||
|                 if (currentCategoryNode.find("i").hasClass("expanded")) { | ||||
|                     currentCategoryNode.find(".red-ui-palette-content").slideToggle(); | ||||
|                     currentCategoryNode.find("i").toggleClass("expanded"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         paletteNode.css("backgroundColor", sf.color); | ||||
|     } | ||||
|  | ||||
|     function filterChange(val) { | ||||
| @@ -504,6 +557,8 @@ RED.palette = (function() { | ||||
|         $('<div class="red-ui-component-footer"></div>').appendTo("#red-ui-palette"); | ||||
|         $('<div id="red-ui-palette-shade" class="hide"></div>').appendTo("#red-ui-palette"); | ||||
|  | ||||
|         $("#red-ui-palette > .red-ui-palette-spinner").show(); | ||||
|  | ||||
|  | ||||
|         RED.events.on('registry:node-type-added', function(nodeType) { | ||||
|             var def = RED.nodes.getType(nodeType); | ||||
| @@ -545,7 +600,8 @@ RED.palette = (function() { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         $("#red-ui-palette > .red-ui-palette-spinner").show(); | ||||
|         RED.events.on("subflows:change",refreshSubflow); | ||||
|  | ||||
|  | ||||
|  | ||||
|         $("#red-ui-palette-search input").searchBox({ | ||||
|   | ||||
| @@ -685,6 +685,8 @@ RED.projects = (function() { | ||||
|                                             } | ||||
|                                         } | ||||
|                                     },projectData).then(function() { | ||||
|                                         RED.menu.setDisabled('menu-item-projects-open',false); | ||||
|                                         RED.menu.setDisabled('menu-item-projects-settings',false); | ||||
|                                         RED.events.emit("project:change", {name:name}); | ||||
|                                     }).always(function() { | ||||
|                                         setTimeout(function() { | ||||
| @@ -1495,7 +1497,6 @@ RED.projects = (function() { | ||||
|                                         } | ||||
|                                     } else if (projectType === 'open') { | ||||
|                                         return switchProject(selectedProject.name,function(err,data) { | ||||
|                                             dialog.dialog( "close" ); | ||||
|                                             if (err) { | ||||
|                                                 if (err.code !== 'credentials_load_failed') { | ||||
|                                                     console.log(RED._("projects.create.unexpected_error"),err) | ||||
| @@ -1604,6 +1605,7 @@ RED.projects = (function() { | ||||
|                 }, | ||||
|             } | ||||
|         },{active:true}).then(function() { | ||||
|             dialog.dialog( "close" ); | ||||
|             RED.events.emit("project:change", {name:name}); | ||||
|         }).always(function() { | ||||
|             setTimeout(function() { | ||||
| @@ -1671,16 +1673,27 @@ RED.projects = (function() { | ||||
|         if (typeof buttons === 'function') { | ||||
|             buttons = buttons(options||{}); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         dialog.dialog('option','buttons',buttons); | ||||
|         dialogBody.append(container); | ||||
|  | ||||
|  | ||||
|         var dialogHeight = 590; | ||||
|         var winHeight = $(window).height(); | ||||
|         if (winHeight < 750) { | ||||
|             dialogHeight = 590 - (750 - winHeight); | ||||
|         } | ||||
|         $(".red-ui-projects-dialog-box").height(dialogHeight); | ||||
|         $(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 180); | ||||
|         dialog.dialog('option','title',screen.title||""); | ||||
|         dialog.dialog("open"); | ||||
|         dialog.dialog({position: { 'my': 'center top', 'at': 'center top+20', 'of': window }}); | ||||
|     } | ||||
|  | ||||
|     function createProjectList(options) { | ||||
|         options = options||{}; | ||||
|         var height = options.height || "300px"; | ||||
|         var height = options.height || "200px"; | ||||
|         var container = $('<div></div>',{class:"red-ui-projects-dialog-project-list-container" }); | ||||
|         var filterTerm = ""; | ||||
|  | ||||
| @@ -2252,7 +2265,7 @@ RED.projects = (function() { | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         dialog = $('<div id="red-ui-projects-dialog" class="hide red-ui-projects-edit-form"><form class="form-horizontal"></form><div class="red-ui-component-spinner hide"><img src="red/images/spin.svg"/></div></div>') | ||||
|         dialog = $('<div id="red-ui-projects-dialog" class="hide red-ui-projects-edit-form"><div class="red-ui-projects-dialog-box"><form class="form-horizontal"></form><div class="red-ui-component-spinner hide"><img src="red/images/spin.svg"/></div></div></div>') | ||||
|             .appendTo("#red-ui-editor") | ||||
|             .dialog({ | ||||
|                 modal: true, | ||||
| @@ -2339,6 +2352,7 @@ RED.projects = (function() { | ||||
|             if (data.active) { | ||||
|                 $.getJSON("projects/"+data.active, function(project) { | ||||
|                     activeProject = project; | ||||
|                     RED.events.emit("projects:load",activeProject); | ||||
|                     RED.sidebar.versionControl.refresh(true); | ||||
|                     if (done) { | ||||
|                         done(activeProject); | ||||
|   | ||||
| @@ -23,8 +23,7 @@ RED.search = (function() { | ||||
|     var visible = false; | ||||
|  | ||||
|     var index = {}; | ||||
|     var keys = []; | ||||
|     var results = []; | ||||
|     var currentResults = []; | ||||
|     var previousActiveElement; | ||||
|  | ||||
|  | ||||
| @@ -66,23 +65,9 @@ RED.search = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function indexWorkspace() { | ||||
|         index = {}; | ||||
|         RED.nodes.eachWorkspace(indexNode); | ||||
|         RED.nodes.eachSubflow(indexNode); | ||||
|         RED.nodes.eachConfig(indexNode); | ||||
|         RED.nodes.eachNode(indexNode); | ||||
|         keys = Object.keys(index); | ||||
|         keys.sort(); | ||||
|         keys.forEach(function(key) { | ||||
|             index[key] = Object.keys(index[key]).map(function(id) { | ||||
|                 return index[key][id]; | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     function search(val) { | ||||
|         searchResults.editableList('empty'); | ||||
|         var results = []; | ||||
|         var keys = Object.keys(index); | ||||
|         var typeFilter; | ||||
|         var m = /(?:^| )type:([^ ]+)/.exec(val); | ||||
|         if (m) { | ||||
| @@ -92,8 +77,7 @@ RED.search = (function() { | ||||
|  | ||||
|         val = val.trim(); | ||||
|  | ||||
|         selected = -1; | ||||
|         results = []; | ||||
|  | ||||
|         if (val.length > 0 || typeFilter) { | ||||
|             val = val.toLowerCase(); | ||||
|             var i; | ||||
| @@ -104,10 +88,14 @@ RED.search = (function() { | ||||
|                 var key = keys[i]; | ||||
|                 var kpos = keys[i].indexOf(val); | ||||
|                 if (kpos > -1) { | ||||
|                     for (j=0;j<index[key].length;j++) { | ||||
|                         var node = index[key][j]; | ||||
|                     var ids = Object.keys(index[key]); | ||||
|                     for (j=0;j<ids.length;j++) { | ||||
|                         var node = index[key][ids[j]]; | ||||
|                         if (!typeFilter || node.node.type === typeFilter) { | ||||
|                             nodes[node.node.id] = nodes[node.node.id] = node; | ||||
|                             nodes[node.node.id] = nodes[node.node.id] = { | ||||
|                                 node: node.node, | ||||
|                                 label: node.label | ||||
|                             }; | ||||
|                             nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos); | ||||
|                         } | ||||
|                     } | ||||
| @@ -121,22 +109,8 @@ RED.search = (function() { | ||||
|             for (i=0;i<list.length;i++) { | ||||
|                 results.push(nodes[list[i]]); | ||||
|             } | ||||
|             if (results.length > 0) { | ||||
|                 for (i=0;i<Math.min(results.length,25);i++) { | ||||
|                     searchResults.editableList('addItem',results[i]) | ||||
|                 } | ||||
|                 if (results.length > 25) { | ||||
|                     searchResults.editableList('addItem', { | ||||
|                         more: { | ||||
|                             results: results, | ||||
|                             start: 25 | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } else { | ||||
|                 searchResults.editableList('addItem',{}); | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     function ensureSelectedIsVisible() { | ||||
| @@ -161,13 +135,37 @@ RED.search = (function() { | ||||
|         searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.searchInput">').appendTo(searchDiv).searchBox({ | ||||
|             delay: 200, | ||||
|             change: function() { | ||||
|                 search($(this).val()); | ||||
|                 searchResults.editableList('empty'); | ||||
|                 selected = -1; | ||||
|                 currentResults = search($(this).val()); | ||||
|                 if (currentResults.length > 0) { | ||||
|                     for (i=0;i<Math.min(currentResults.length,25);i++) { | ||||
|                         searchResults.editableList('addItem',currentResults[i]) | ||||
|                     } | ||||
|                     if (currentResults.length > 25) { | ||||
|                         searchResults.editableList('addItem', { | ||||
|                             more: { | ||||
|                                 results: currentResults, | ||||
|                                 start: 25 | ||||
|                             } | ||||
|                         }) | ||||
|                     } | ||||
|                 } else { | ||||
|                     searchResults.editableList('addItem',{}); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|         }); | ||||
|         var copySearchContainer = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-caret-right"></button>').appendTo(searchDiv).on('click', function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             RED.sidebar.info.outliner.search(searchInput.val()) | ||||
|             hide(); | ||||
|         }); | ||||
|  | ||||
|         searchInput.on('keydown',function(evt) { | ||||
|             var children; | ||||
|             if (results.length > 0) { | ||||
|             if (currentResults.length > 0) { | ||||
|                 if (evt.keyCode === 40) { | ||||
|                     // Down | ||||
|                     children = searchResults.children(); | ||||
| @@ -199,21 +197,21 @@ RED.search = (function() { | ||||
|                         var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data'); | ||||
|                         if (object) { | ||||
|                             searchResults.editableList('removeItem',object); | ||||
|                             for (i=object.more.start;i<Math.min(results.length,object.more.start+25);i++) { | ||||
|                                 searchResults.editableList('addItem',results[i]) | ||||
|                             for (i=object.more.start;i<Math.min(currentResults.length,object.more.start+25);i++) { | ||||
|                                 searchResults.editableList('addItem',currentResults[i]) | ||||
|                             } | ||||
|                             if (results.length > object.more.start+25) { | ||||
|                             if (currentResults.length > object.more.start+25) { | ||||
|                                 searchResults.editableList('addItem', { | ||||
|                                     more: { | ||||
|                                         results: results, | ||||
|                                         results: currentResults, | ||||
|                                         start: object.more.start+25 | ||||
|                                     } | ||||
|                                 }) | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (results.length > 0) { | ||||
|                             reveal(results[Math.max(0,selected)].node); | ||||
|                         if (currentResults.length > 0) { | ||||
|                             reveal(currentResults[Math.max(0,selected)].node); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -234,13 +232,13 @@ RED.search = (function() { | ||||
|                     div.on("click", function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         searchResults.editableList('removeItem',object); | ||||
|                         for (i=object.more.start;i<Math.min(results.length,object.more.start+25);i++) { | ||||
|                             searchResults.editableList('addItem',results[i]) | ||||
|                         for (i=object.more.start;i<Math.min(currentResults.length,object.more.start+25);i++) { | ||||
|                             searchResults.editableList('addItem',currentResults[i]) | ||||
|                         } | ||||
|                         if (results.length > object.more.start+25) { | ||||
|                         if (currentResults.length > object.more.start+25) { | ||||
|                             searchResults.editableList('addItem', { | ||||
|                                 more: { | ||||
|                                     results: results, | ||||
|                                     results: currentResults, | ||||
|                                     start: object.more.start+25 | ||||
|                                 } | ||||
|                             }) | ||||
| @@ -253,17 +251,7 @@ RED.search = (function() { | ||||
|                     var def = node._def; | ||||
|                     div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container); | ||||
|  | ||||
|                     var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div); | ||||
|                     var colour = RED.utils.getNodeColor(node.type,def); | ||||
|                     var icon_url = RED.utils.getNodeIcon(def,node); | ||||
|                     if (node.type === 'tab') { | ||||
|                         colour = "#C0DEED"; | ||||
|                     } | ||||
|                     nodeDiv.css('backgroundColor',colour); | ||||
|  | ||||
|                     var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|                     RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|  | ||||
|                     RED.utils.createNodeIcon(node).appendTo(div); | ||||
|                     var contentDiv = $('<div>',{class:"red-ui-search-result-node-description"}).appendTo(div); | ||||
|                     if (node.z) { | ||||
|                         var workspace = RED.nodes.workspace(node.z); | ||||
| @@ -308,7 +296,7 @@ RED.search = (function() { | ||||
|             $("#red-ui-palette-shade").show(); | ||||
|             $("#red-ui-sidebar-shade").show(); | ||||
|             $("#red-ui-sidebar-separator").hide(); | ||||
|             indexWorkspace(); | ||||
|  | ||||
|             if (dialog === null) { | ||||
|                 createDialog(); | ||||
|             } | ||||
| @@ -342,6 +330,28 @@ RED.search = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function clearIndex() { | ||||
|         index = {}; | ||||
|     } | ||||
|  | ||||
|     function addItemToIndex(item) { | ||||
|         indexNode(item); | ||||
|     } | ||||
|     function removeItemFromIndex(item) { | ||||
|         var keys = Object.keys(index); | ||||
|         for (var i=0,l=keys.length;i<l;i++) { | ||||
|             delete index[keys[i]][item.id]; | ||||
|             if (Object.keys(index[keys[i]]).length === 0) { | ||||
|                 delete index[keys[i]]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function updateItemOnIndex(item) { | ||||
|         removeItemFromIndex(item); | ||||
|         addItemToIndex(item); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function init() { | ||||
|         RED.actions.add("core:search",show); | ||||
|  | ||||
| @@ -358,12 +368,33 @@ RED.search = (function() { | ||||
|         $("#red-ui-editor-shade").on('mousedown',hide); | ||||
|         $("#red-ui-palette-shade").on('mousedown',hide); | ||||
|         $("#red-ui-sidebar-shade").on('mousedown',hide); | ||||
|  | ||||
|  | ||||
|         RED.events.on("workspace:clear", clearIndex) | ||||
|  | ||||
|         RED.events.on("flows:add", addItemToIndex) | ||||
|         RED.events.on("flows:remove", removeItemFromIndex) | ||||
|         RED.events.on("flows:change", updateItemOnIndex) | ||||
|  | ||||
|         RED.events.on("subflows:add", addItemToIndex) | ||||
|         RED.events.on("subflows:remove", removeItemFromIndex) | ||||
|         RED.events.on("subflows:change", updateItemOnIndex) | ||||
|  | ||||
|         RED.events.on("nodes:add",addItemToIndex); | ||||
|         RED.events.on("nodes:remove",removeItemFromIndex); | ||||
|         RED.events.on("nodes:change",updateItemOnIndex); | ||||
|  | ||||
|         RED.events.on("groups:add",addItemToIndex); | ||||
|         RED.events.on("groups:remove",removeItemFromIndex); | ||||
|         RED.events.on("groups:change",updateItemOnIndex); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         show: show, | ||||
|         hide: hide | ||||
|         hide: hide, | ||||
|         search: search | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -250,6 +250,7 @@ RED.sidebar = (function() { | ||||
|         RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar"); | ||||
|         showSidebar(); | ||||
|         RED.sidebar.info.init(); | ||||
|         RED.sidebar.help.init(); | ||||
|         RED.sidebar.config.init(); | ||||
|         RED.sidebar.context.init(); | ||||
|         // hide info bar at start if screen rather narrow... | ||||
|   | ||||
| @@ -25,5 +25,7 @@ RED.state = { | ||||
|     IMPORT_DRAGGING: 8, | ||||
|     QUICK_JOINING: 9, | ||||
|     PANNING: 10, | ||||
|     SELECTING_NODE: 11 | ||||
|     SELECTING_NODE: 11, | ||||
|     GROUP_DRAGGING: 12, | ||||
|     GROUP_RESIZE: 13 | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										332
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,332 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
| RED.sidebar.help = (function() { | ||||
|  | ||||
|     var content; | ||||
|     var toolbar; | ||||
|     var helpSection; | ||||
|     var panels; | ||||
|     var panelRatio; | ||||
|     var helpTopics = []; | ||||
|     var treeList; | ||||
|     var tocPanel; | ||||
|     var helpIndex = {}; | ||||
|  | ||||
|  | ||||
|     function resizeStack() { | ||||
|         var h = $(content).parent().height() - toolbar.outerHeight(); | ||||
|         panels.resize(h) | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|  | ||||
|         content = document.createElement("div"); | ||||
|         content.className = "red-ui-sidebar-info" | ||||
|  | ||||
|         toolbar = $("<div>", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(content); | ||||
|         $('<span class="button-group"><a id="red-ui-sidebar-help-show-toc" class="red-ui-button red-ui-button-small selected" href="#"><i class="fa fa-list-ul"></i></a></span>').appendTo(toolbar) | ||||
|         var showTOCButton = toolbar.find('#red-ui-sidebar-help-show-toc') | ||||
|         RED.popover.tooltip(showTOCButton,RED._("sidebar.help.showTopics")); | ||||
|         showTOCButton.on("click",function(e) { | ||||
|             e.preventDefault(); | ||||
|             if ($(this).hasClass('selected')) { | ||||
|                 hideTOC(); | ||||
|             } else { | ||||
|                 showTOC(); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         var stackContainer = $("<div>",{class:"red-ui-sidebar-help-stack"}).appendTo(content); | ||||
|  | ||||
|         tocPanel = $("<div>", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer); | ||||
|         var helpPanel = $("<div>").css({ | ||||
|             "overflow-y": "scroll" | ||||
|         }).appendTo(stackContainer); | ||||
|  | ||||
|         panels = RED.panels.create({ | ||||
|             container: stackContainer | ||||
|         }) | ||||
|         panels.ratio(0.3); | ||||
|  | ||||
|         helpSearch = $('<input type="text" data-i18n="[placeholder]sidebar.help.search">').appendTo(toolbar).searchBox({ | ||||
|             delay: 100, | ||||
|             change: function() { | ||||
|                 var val = $(this).val().toLowerCase(); | ||||
|                 if (val) { | ||||
|                     showTOC(); | ||||
|                     var c = treeList.treeList('filter',function(item) { | ||||
|                         if (item.depth === 0) { | ||||
|                             return true; | ||||
|                         } | ||||
|                         return (item.nodeType &&  item.nodeType.indexOf(val) > -1) || | ||||
|                                (item.subflowLabel && item.subflowLabel.indexOf(val) > -1) | ||||
|                     },true) | ||||
|                 } else { | ||||
|                     treeList.treeList('filter',null); | ||||
|                     var selected = treeList.treeList('selected'); | ||||
|                     if (selected.id) { | ||||
|                         treeList.treeList('show',selected.id); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         helpSection = $("<div>",{class:"red-ui-help"}).css({ | ||||
|             "padding":"6px", | ||||
|         }).appendTo(helpPanel) | ||||
|  | ||||
|         $('<span class="red-ui-help-info-none">'+RED._("sidebar.help.noHelp")+'</span>').appendTo(helpSection); | ||||
|  | ||||
|         treeList = $("<div>").css({width: "100%"}).appendTo(tocPanel).treeList({data: []}) | ||||
|         treeList.on('treelistselect', function(e,item) { | ||||
|             if (item.nodeType) { | ||||
|                 showHelp(item.nodeType); | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         RED.sidebar.addTab({ | ||||
|             id: "help", | ||||
|             label: RED._("sidebar.help.label"), | ||||
|             name: RED._("sidebar.help.name"), | ||||
|             iconClass: "fa fa-book", | ||||
|             action:"core:show-help-tab", | ||||
|             content: content, | ||||
|             pinned: true, | ||||
|             enableOnEdit: true, | ||||
|             onchange: function() { | ||||
|                 resizeStack() | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         $(window).on("resize", resizeStack); | ||||
|         $(window).on("focus", resizeStack); | ||||
|  | ||||
|         RED.events.on('registry:node-type-added', queueRefresh); | ||||
|         RED.events.on('registry:node-type-removed', queueRefresh); | ||||
|         RED.events.on('subflows:change', refreshSubflow); | ||||
|  | ||||
|         RED.actions.add("core:show-help-tab",show); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     var refreshTimer; | ||||
|     function queueRefresh() { | ||||
|         if (!refreshTimer) { | ||||
|             refreshTimer = setTimeout(function() { | ||||
|                 refreshTimer = null; | ||||
|                 refreshHelpIndex(); | ||||
|             },500); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function refreshSubflow(sf) { | ||||
|         var item = treeList.treeList('get',"node-type:subflow:"+sf.id); | ||||
|         item.subflowLabel = sf._def.label().toLowerCase(); | ||||
|         item.treeList.replaceElement(getNodeLabel({_def:sf._def,type:sf._def.label()})); | ||||
|     } | ||||
|  | ||||
|     function hideTOC() { | ||||
|         var tocButton = $('#red-ui-sidebar-help-show-toc') | ||||
|         if (tocButton.hasClass('selected')) { | ||||
|             tocButton.removeClass('selected'); | ||||
|             panelRatio = panels.ratio(); | ||||
|             tocPanel.css({"transition":"height 0.2s"}) | ||||
|             panels.ratio(0) | ||||
|             setTimeout(function() { | ||||
|                 tocPanel.css({"transition":""}) | ||||
|             },250); | ||||
|         } | ||||
|     } | ||||
|     function showTOC() { | ||||
|         var tocButton = $('#red-ui-sidebar-help-show-toc') | ||||
|         if (!tocButton.hasClass('selected')) { | ||||
|             tocButton.addClass('selected'); | ||||
|             tocPanel.css({"transition":"height 0.2s"}) | ||||
|             panels.ratio(Math.max(0.3,Math.min(panelRatio,0.7))); | ||||
|             setTimeout(function() { | ||||
|                 tocPanel.css({"transition":""}) | ||||
|                 var selected = treeList.treeList('selected'); | ||||
|                 if (selected.id) { | ||||
|                     treeList.treeList('show',selected); | ||||
|                 } | ||||
|             },250); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function refreshHelpIndex() { | ||||
|         helpTopics = []; | ||||
|         var modules = RED.nodes.registry.getModuleList(); | ||||
|         var moduleNames = Object.keys(modules); | ||||
|         moduleNames.sort(); | ||||
|  | ||||
|         var helpData = [{ | ||||
|             label: RED._("sidebar.help.nodeHelp"), | ||||
|             children: [], | ||||
|             expanded: true | ||||
|         }] | ||||
|  | ||||
|         var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)}); | ||||
|         if (subflows.length > 0) { | ||||
|             helpData[0].children.push({ | ||||
|                 label: RED._("menu.label.subflows"), | ||||
|                 children: [] | ||||
|             }) | ||||
|             subflows.forEach(function(nodeType) { | ||||
|                 var sf = RED.nodes.getType(nodeType); | ||||
|                 helpData[0].children[0].children.push({ | ||||
|                     id:"node-type:"+nodeType, | ||||
|                     nodeType: nodeType, | ||||
|                     subflowLabel: sf.label().toLowerCase(), | ||||
|                     element: getNodeLabel({_def:sf,type:sf.label()}) | ||||
|                 }) | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|  | ||||
|         moduleNames.forEach(function(moduleName) { | ||||
|             var module = modules[moduleName]; | ||||
|             var nodeTypes = []; | ||||
|  | ||||
|             var setNames = Object.keys(module.sets); | ||||
|             setNames.forEach(function(setName) { | ||||
|                 module.sets[setName].types.forEach(function(nodeType) { | ||||
|                     if ($("script[data-help-name='"+nodeType+"']").length) { | ||||
|                         nodeTypes.push({ | ||||
|                             id: "node-type:"+nodeType, | ||||
|                             nodeType: nodeType, | ||||
|                             element:getNodeLabel({_def:RED.nodes.getType(nodeType),type:nodeType}) | ||||
|                         }) | ||||
|                     } | ||||
|                 }) | ||||
|             }) | ||||
|             if (nodeTypes.length > 0) { | ||||
|                 nodeTypes.sort(function(A,B) { | ||||
|                     return A.nodeType.localeCompare(B.nodeType) | ||||
|                 }) | ||||
|                 helpData[0].children.push({ | ||||
|                     id: moduleName, | ||||
|                     icon: "fa fa-cube", | ||||
|                     label: moduleName, | ||||
|                     children: nodeTypes | ||||
|                 }) | ||||
|             } | ||||
|         }); | ||||
|         treeList.treeList("data",helpData); | ||||
|     } | ||||
|  | ||||
|     function getNodeLabel(n) { | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item"}); | ||||
|         RED.utils.createNodeIcon(n).appendTo(div); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); | ||||
|         $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.name||n.type).appendTo(contentDiv); | ||||
|         return div; | ||||
|     } | ||||
|  | ||||
|     function showHelp(nodeType) { | ||||
|         helpSection.empty(); | ||||
|         var helpText; | ||||
|         var title; | ||||
|         var m = /^subflow(:(.+))?$/.exec(nodeType); | ||||
|         if (m && m[2]) { | ||||
|             var subflowNode = RED.nodes.subflow(m[2]); | ||||
|             helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>')); | ||||
|             title = subflowNode.name || nodeType; | ||||
|         } else { | ||||
|             helpText = $("script[data-help-name='"+nodeType+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'); | ||||
|             title = nodeType; | ||||
|         } | ||||
|         setInfoText(title, helpText, helpSection); | ||||
|  | ||||
|         var ratio = panels.ratio(); | ||||
|         if (ratio > 0.7) { | ||||
|             panels.ratio(0.7) | ||||
|         } | ||||
|         treeList.treeList("show","node-type:"+nodeType) | ||||
|         treeList.treeList("select","node-type:"+nodeType, false); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function show(type) { | ||||
|         RED.sidebar.show("help"); | ||||
|         if (type) { | ||||
|             hideTOC(); | ||||
|             showHelp(type); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO: DRY - projects.js | ||||
|     function addTargetToExternalLinks(el) { | ||||
|         $(el).find("a").each(function(el) { | ||||
|             var href = $(this).attr('href'); | ||||
|             if (/^https?:/.test(href)) { | ||||
|                 $(this).attr('target','_blank'); | ||||
|             } | ||||
|         }); | ||||
|         return el; | ||||
|     } | ||||
|  | ||||
|     function setInfoText(title, infoText,target) { | ||||
|         if (title) { | ||||
|             $("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(target); | ||||
|         } | ||||
|         var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(target); | ||||
|         info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" ); | ||||
|         var foldingHeader = "H3"; | ||||
|         info.find(foldingHeader).wrapInner('<a class="red-ui-help-info-header expanded" href="#"></a>') | ||||
|             .find("a").prepend('<i class="fa fa-angle-right">').on("click", function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 var isExpanded = $(this).hasClass('expanded'); | ||||
|                 var el = $(this).parent().next(); | ||||
|                 while(el.length === 1 && el[0].nodeName !== foldingHeader) { | ||||
|                     el.toggle(!isExpanded); | ||||
|                     el = el.next(); | ||||
|                 } | ||||
|                 $(this).toggleClass('expanded',!isExpanded); | ||||
|             }) | ||||
|         target.parent().scrollTop(0); | ||||
|     } | ||||
|  | ||||
|     function set(html,title) { | ||||
|         $(helpSection).empty(); | ||||
|         setInfoText(title,html,helpSection); | ||||
|         hideTOC(); | ||||
|         show(); | ||||
|     } | ||||
|  | ||||
|     function refreshSelection(selection) { | ||||
|         if (selection === undefined) { | ||||
|             selection = RED.view.selection(); | ||||
|         } | ||||
|         if (selection.nodes) { | ||||
|             if (selection.nodes.length == 1) { | ||||
|                 var node = selection.nodes[0]; | ||||
|                 if (node.type === "subflow" && node.direction) { | ||||
|                     // ignore subflow virtual ports | ||||
|                 } else if (node.type !== 'group'){ | ||||
|                     showHelp(node.type); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     RED.events.on("view:selection-changed",refreshSelection); | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         show: show, | ||||
|         set: set | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										475
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,475 @@ | ||||
| RED.sidebar.info.outliner = (function() { | ||||
|  | ||||
|     var treeList; | ||||
|     var searchInput; | ||||
|     var projectInfo; | ||||
|     var projectInfoLabel; | ||||
|     var flowList; | ||||
|     var subflowList; | ||||
|     var globalConfigNodes; | ||||
|  | ||||
|     var objects = {}; | ||||
|     var missingParents = {}; | ||||
|  | ||||
|     function getFlowData() { | ||||
|         var flowData = [ | ||||
|             { | ||||
|                 label: RED._("menu.label.flows"), | ||||
|                 expanded: true, | ||||
|                 children: [] | ||||
|             }, | ||||
|             { | ||||
|                 label: RED._("menu.label.subflows"), | ||||
|                 children: [] | ||||
|             }, | ||||
|             { | ||||
|                 id: "__global__", | ||||
|                 label: RED._("sidebar.info.globalConfig"), | ||||
|                 children: [] | ||||
|             } | ||||
|         ] | ||||
|         flowList = flowData[0]; | ||||
|         subflowList = flowData[1]; | ||||
|         globalConfigNodes = flowData[2]; | ||||
|  | ||||
|         return flowData; | ||||
|     } | ||||
|  | ||||
|     function getProjectLabel(p) { | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); | ||||
|         div.css("width", "calc(100% - 40px)"); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); | ||||
|         contentDiv.text(p.name); | ||||
|         var controls = $('<div>',{class:"red-ui-info-outline-item-controls"}).appendTo(div); | ||||
|         var editProjectButton = $('<button class="red-ui-button red-ui-button-small" style="position:absolute;right:5px;top: 3px;"><i class="fa fa-ellipsis-h"></i></button>') | ||||
|             .appendTo(controls) | ||||
|             .on("click", function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 RED.projects.editProject(); | ||||
|             }); | ||||
|         RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings')); | ||||
|         return div; | ||||
|     } | ||||
|  | ||||
|     var empties = {}; | ||||
|     function getEmptyItem(id) { | ||||
|         var item = { | ||||
|             empty: true, | ||||
|             element: $('<div class="red-ui-info-outline-item red-ui-info-outline-item-empty">').text(RED._("sidebar.info.empty")), | ||||
|         } | ||||
|         empties[id] = item; | ||||
|         return item; | ||||
|     } | ||||
|  | ||||
|     function getNodeLabelText(n) { | ||||
|         var label = n.name || n.type+": "+n.id; | ||||
|         if (n._def.label) { | ||||
|             try { | ||||
|                 label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||""; | ||||
|             } catch(err) { | ||||
|                 console.log("Definition error: "+type+".label",err); | ||||
|             } | ||||
|         } | ||||
|         var newlineIndex = label.indexOf("\\n"); | ||||
|         if (newlineIndex > -1) { | ||||
|             label = label.substring(0,newlineIndex)+"..."; | ||||
|         } | ||||
|         return label; | ||||
|     } | ||||
|  | ||||
|     function getNodeLabel(n) { | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item"}); | ||||
|         RED.utils.createNodeIcon(n).appendTo(div); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); | ||||
|         var labelText = getNodeLabelText(n); | ||||
|         var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); | ||||
|         if (labelText) { | ||||
|             label.text(labelText) | ||||
|         } else { | ||||
|             label.html(" ") | ||||
|         } | ||||
|  | ||||
|         addControls(n, div); | ||||
|  | ||||
|         return div; | ||||
|     } | ||||
|  | ||||
|     function getFlowLabel(n) { | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); | ||||
|         var label = (typeof n === "string")? n : n.label; | ||||
|         var newlineIndex = label.indexOf("\\n"); | ||||
|         if (newlineIndex > -1) { | ||||
|             label = label.substring(0,newlineIndex)+"..."; | ||||
|         } | ||||
|         contentDiv.text(label); | ||||
|         addControls(n, div); | ||||
|         return div; | ||||
|     } | ||||
|  | ||||
|     function getSubflowLabel(n) { | ||||
|  | ||||
|         var div = $('<div>',{class:"red-ui-info-outline-item"}); | ||||
|         RED.utils.createNodeIcon(n).appendTo(div); | ||||
|         var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); | ||||
|         var labelText = getNodeLabelText(n); | ||||
|         var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); | ||||
|         if (labelText) { | ||||
|             label.text(labelText) | ||||
|         } else { | ||||
|             label.html(" ") | ||||
|         } | ||||
|  | ||||
|         addControls(n, div); | ||||
|  | ||||
|         return div; | ||||
|  | ||||
|  | ||||
|         // var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); | ||||
|         // var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); | ||||
|         // contentDiv.text(n.name || n.id); | ||||
|         // addControls(n, div); | ||||
|         // return div; | ||||
|     } | ||||
|  | ||||
|     function addControls(n,div) { | ||||
|         var controls = $('<div>',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div); | ||||
|         if (n._def.button) { | ||||
|             $('<button type="button" class="red-ui-info-outline-item-control-action red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').appendTo(controls).on("click",function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 RED.view.clickNodeButton(n); | ||||
|             }) | ||||
|         } | ||||
|         // $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-eye"></i></button>').appendTo(controls).on("click",function(evt) { | ||||
|         //     evt.preventDefault(); | ||||
|         //     evt.stopPropagation(); | ||||
|         //     RED.view.reveal(n.id); | ||||
|         // }) | ||||
|         if (n.type !== 'group' && n.type !== 'subflow') { | ||||
|             $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 if (n.type === 'tab') { | ||||
|                     if (n.disabled) { | ||||
|                         RED.workspaces.enable(n.id) | ||||
|                     } else { | ||||
|                         RED.workspaces.disable(n.id) | ||||
|                     } | ||||
|                 } else { | ||||
|                     // TODO: this ought to be a utility function in RED.nodes | ||||
|                     var historyEvent = { | ||||
|                         t: "edit", | ||||
|                         node: n, | ||||
|                         changed: n.changed, | ||||
|                         changes: { | ||||
|                             d: n.d | ||||
|                         }, | ||||
|                         dirty:RED.nodes.dirty() | ||||
|                     } | ||||
|                     if (n.d) { | ||||
|                         delete n.d; | ||||
|                     } else { | ||||
|                         n.d = true; | ||||
|                     } | ||||
|                     n.dirty = true; | ||||
|                     n.changed = true; | ||||
|                     RED.events.emit("nodes:change",n); | ||||
|                     RED.nodes.dirty(true) | ||||
|                     RED.view.redraw(); | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             $('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls) | ||||
|         } | ||||
|         controls.find("button").on("dblclick", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     function onProjectLoad(activeProject) { | ||||
|         objects = {}; | ||||
|         var newFlowData = getFlowData(); | ||||
|         projectInfoLabel.empty(); | ||||
|         getProjectLabel(activeProject).appendTo(projectInfoLabel); | ||||
|         projectInfo.show(); | ||||
|         treeList.treeList('data',newFlowData); | ||||
|     } | ||||
|  | ||||
|     function build() { | ||||
|         var container = $("<div>", {class:"red-ui-info-outline"}).css({'height': '100%'}); | ||||
|         var toolbar = $("<div>", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(container); | ||||
|  | ||||
|         searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.search">').appendTo(toolbar).searchBox({ | ||||
|             delay: 300, | ||||
|             change: function() { | ||||
|                 var val = $(this).val(); | ||||
|                 var searchResults = RED.search.search(val); | ||||
|                 if (val) { | ||||
|                     var resultMap = {}; | ||||
|                     for (var i=0,l=searchResults.length;i<l;i++) { | ||||
|                         resultMap[searchResults[i].node.id] = true; | ||||
|                     } | ||||
|                     var c = treeList.treeList('filter',function(item) { | ||||
|                         if (item.depth === 0) { | ||||
|                             return true; | ||||
|                         } | ||||
|                         return item.id &&  objects[item.id] && resultMap[item.id] | ||||
|                     },true) | ||||
|                 } else { | ||||
|                     treeList.treeList('filter',null); | ||||
|                     var selected = treeList.treeList('selected'); | ||||
|                     if (selected.id) { | ||||
|                         treeList.treeList('show',selected.id); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         projectInfo = $('<div class="red-ui-treeList-label red-ui-info-outline-project"><span class="red-ui-treeList-icon"><i class="fa fa-archive"></i></span></div>').hide().appendTo(container) | ||||
|         projectInfoLabel = $('<span>').appendTo(projectInfo); | ||||
|  | ||||
|         // <div class="red-ui-info-outline-item red-ui-info-outline-item-flow" style=";"><div class="red-ui-search-result-description red-ui-info-outline-item-label">Space Monkey</div><div class="red-ui-info-outline-item-controls"><button class="red-ui-button red-ui-button-small" style="position:absolute;right:5px;"><i class="fa fa-ellipsis-h"></i></button></div></div></div>').appendTo(container) | ||||
|  | ||||
|         treeList = $("<div>").css({width: "100%"}).appendTo(container).treeList({ | ||||
|             data:getFlowData() | ||||
|         }) | ||||
|         treeList.on('treelistselect', function(e,item) { | ||||
|             var node = RED.nodes.node(item.id) || RED.nodes.group(item.id); | ||||
|             if (node) { | ||||
|                 if (node.type === 'group' || node._def.category !== "config") { | ||||
|                     RED.view.select({nodes:[node]}) | ||||
|                 } else { | ||||
|                     RED.view.select({nodes:[]}) | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         treeList.on('treelistconfirm', function(e,item) { | ||||
|             var node = RED.nodes.node(item.id); | ||||
|             if (node) { | ||||
|                 if (node._def.category === "config") { | ||||
|                     RED.editor.editConfig("", node.type, node.id); | ||||
|                 } else { | ||||
|                     RED.editor.edit(node); | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         RED.events.on("projects:load", onProjectLoad) | ||||
|  | ||||
|         RED.events.on("flows:add", onFlowAdd) | ||||
|         RED.events.on("flows:remove", onObjectRemove) | ||||
|         RED.events.on("flows:change", onFlowChange) | ||||
|         RED.events.on("flows:reorder", onFlowsReorder) | ||||
|  | ||||
|         RED.events.on("subflows:add", onSubflowAdd) | ||||
|         RED.events.on("subflows:remove", onObjectRemove) | ||||
|         RED.events.on("subflows:change", onSubflowChange) | ||||
|  | ||||
|         RED.events.on("nodes:add",onNodeAdd); | ||||
|         RED.events.on("nodes:remove",onObjectRemove); | ||||
|         RED.events.on("nodes:change",onNodeChange); | ||||
|  | ||||
|         RED.events.on("groups:add",onNodeAdd); | ||||
|         RED.events.on("groups:remove",onObjectRemove); | ||||
|         RED.events.on("groups:change",onNodeChange); | ||||
|  | ||||
|         RED.events.on("view:selection-changed", onSelectionChanged); | ||||
|  | ||||
|         RED.events.on("workspace:clear", onWorkspaceClear) | ||||
|  | ||||
|         return container; | ||||
|     } | ||||
|     function onWorkspaceClear() { | ||||
|         treeList.treeList('data',getFlowData()); | ||||
|     } | ||||
|     function onFlowAdd(ws) { | ||||
|         objects[ws.id] = { | ||||
|             id: ws.id, | ||||
|             element: getFlowLabel(ws), | ||||
|             children:[], | ||||
|             deferBuild: true, | ||||
|             icon: "red-ui-icons red-ui-icons-flow", | ||||
|             gutter: getGutter(ws) | ||||
|         } | ||||
|         if (missingParents[ws.id]) { | ||||
|             objects[ws.id].children = missingParents[ws.id]; | ||||
|             delete missingParents[ws.id] | ||||
|         } else { | ||||
|             objects[ws.id].children.push(getEmptyItem(ws.id)); | ||||
|         } | ||||
|         flowList.treeList.addChild(objects[ws.id]) | ||||
|         objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) | ||||
|         objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled) | ||||
|  | ||||
|  | ||||
|     } | ||||
|     function onFlowChange(n) { | ||||
|         var existingObject = objects[n.id]; | ||||
|  | ||||
|         var label = n.label || n.id; | ||||
|         var newlineIndex = label.indexOf("\\n"); | ||||
|         if (newlineIndex > -1) { | ||||
|             label = label.substring(0,newlineIndex)+"..."; | ||||
|         } | ||||
|         existingObject.element.find(".red-ui-info-outline-item-label").text(label); | ||||
|         existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) | ||||
|         existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled) | ||||
|     } | ||||
|     function onFlowsReorder(order) { | ||||
|         var indexMap = {}; | ||||
|         order.forEach(function(id,index) { | ||||
|             indexMap[id] = index; | ||||
|         }) | ||||
|  | ||||
|         flowList.treeList.sortChildren(function(A,B) { | ||||
|             if (A.id === "__global__") { return -1 } | ||||
|             if (B.id === "__global__") { return 1 } | ||||
|             return indexMap[A.id] - indexMap[B.id] | ||||
|         }) | ||||
|     } | ||||
|     function onSubflowAdd(sf) { | ||||
|         objects[sf.id] = { | ||||
|             id: sf.id, | ||||
|             element: getNodeLabel(sf), | ||||
|             children:[], | ||||
|             deferBuild: true, | ||||
|             gutter: getGutter(sf) | ||||
|         } | ||||
|         if (missingParents[sf.id]) { | ||||
|             objects[sf.id].children = missingParents[sf.id]; | ||||
|             delete missingParents[sf.id] | ||||
|         } else { | ||||
|             objects[sf.id].children.push(getEmptyItem(sf.id)); | ||||
|         } | ||||
|         subflowList.treeList.addChild(objects[sf.id]) | ||||
|     } | ||||
|     function onSubflowChange(sf) { | ||||
|         var existingObject = objects[sf.id]; | ||||
|         existingObject.treeList.replaceElement(getNodeLabel(sf)); | ||||
|         // existingObject.element.find(".red-ui-info-outline-item-label").text(n.name || n.id); | ||||
|         RED.nodes.eachNode(function(n) { | ||||
|             if (n.type == "subflow:"+sf.id) { | ||||
|                 var sfInstance = objects[n.id]; | ||||
|                 sfInstance.treeList.replaceElement(getNodeLabel(n)); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function onNodeChange(n) { | ||||
|         var existingObject = objects[n.id]; | ||||
|         var parent = n.g||n.z; | ||||
|  | ||||
|         var nodeLabelText = getNodeLabelText(n); | ||||
|         if (nodeLabelText) { | ||||
|             existingObject.element.find(".red-ui-info-outline-item-label").text(nodeLabelText); | ||||
|         } else { | ||||
|             existingObject.element.find(".red-ui-info-outline-item-label").html(" "); | ||||
|         } | ||||
|  | ||||
|         if (parent !== existingObject.parent.id) { | ||||
|             existingObject.treeList.remove(); | ||||
|             if (!parent) { | ||||
|                 globalConfigNodes.treeList.addChild(existingObject); | ||||
|             } else { | ||||
|                 if (empties[parent]) { | ||||
|                     empties[parent].treeList.remove(); | ||||
|                     delete empties[parent]; | ||||
|                 } | ||||
|                 objects[parent].treeList.addChild(existingObject) | ||||
|             } | ||||
|         } | ||||
|         existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) | ||||
|     } | ||||
|     function onObjectRemove(n) { | ||||
|         var existingObject = objects[n.id]; | ||||
|         existingObject.treeList.remove(); | ||||
|         delete objects[n.id] | ||||
|         var parent = existingObject.parent; | ||||
|         if (parent.children.length === 0) { | ||||
|             parent.treeList.addChild(getEmptyItem(parent.id)); | ||||
|         } | ||||
|         if (existingObject.children && (existingObject.children.length > 0)) { | ||||
|             existingObject.children.forEach(function (nc) { | ||||
|                 if (!nc.empty) { | ||||
|                     var childObject = objects[nc.id]; | ||||
|                     nc.parent = parent; | ||||
|                     objects[parent.id].treeList.addChild(childObject); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|     function getGutter(n) { | ||||
|         var span = $("<span>",{class:"red-ui-info-outline-gutter"}); | ||||
|         $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-search"></i></button>').appendTo(span).on("click",function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             RED.view.reveal(n.id); | ||||
|         }) | ||||
|         return span; | ||||
|     } | ||||
|     function onNodeAdd(n) { | ||||
|         objects[n.id] = { | ||||
|             id: n.id, | ||||
|             element: getNodeLabel(n), | ||||
|             gutter: getGutter(n) | ||||
|         } | ||||
|         if (n.type === "group") { | ||||
|             objects[n.id].children = []; | ||||
|             objects[n.id].deferBuild = true; | ||||
|             if (missingParents[n.id]) { | ||||
|                 objects[n.id].children = missingParents[n.id]; | ||||
|                 delete missingParents[n.id] | ||||
|             } | ||||
|         } | ||||
|         var parent = n.g||n.z; | ||||
|         if (parent) { | ||||
|             if (objects[parent]) { | ||||
|                 if (empties[parent]) { | ||||
|                     empties[parent].treeList.remove(); | ||||
|                     delete empties[parent]; | ||||
|                 } | ||||
|                 if (objects[parent].treeList) { | ||||
|                     objects[parent].treeList.addChild(objects[n.id]); | ||||
|                 } else { | ||||
|                     objects[parent].children.push(objects[n.id]) | ||||
|                 } | ||||
|             } else { | ||||
|                 missingParents[parent] = missingParents[parent]||[]; | ||||
|                 missingParents[parent].push(objects[n.id]) | ||||
|             } | ||||
|         } else { | ||||
|             // No parent - add to Global flow list | ||||
|             globalConfigNodes.treeList.addChild(objects[n.id]) | ||||
|         } | ||||
|         objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) | ||||
|     } | ||||
|  | ||||
|     function onSelectionChanged(selection) { | ||||
|         // treeList.treeList('clearSelection'); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         build: build, | ||||
|         search: function(val) { | ||||
|             searchInput.searchBox('value',val) | ||||
|         }, | ||||
|         select: function(node) { | ||||
|             if (node) { | ||||
|                 if (Array.isArray(node)) { | ||||
|                     treeList.treeList('select', node.map(function(n) { return objects[n.id] }), false) | ||||
|                 } else { | ||||
|                     treeList.treeList('select', objects[node.id], false) | ||||
|  | ||||
|                 } | ||||
|             } else { | ||||
|                 treeList.treeList('clearSelection') | ||||
|             } | ||||
|         }, | ||||
|         reveal: function(node) { | ||||
|             treeList.treeList('show', objects[node.id]) | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
| @@ -16,16 +16,32 @@ | ||||
| RED.sidebar.info = (function() { | ||||
|  | ||||
|     var content; | ||||
|     var sections; | ||||
|     var propertiesSection; | ||||
|     var panels; | ||||
|     var infoSection; | ||||
|     var helpSection; | ||||
|  | ||||
|     var propertiesPanelContent; | ||||
|     var propertiesPanelHeader; | ||||
|     var propertiesPanelHeaderIcon; | ||||
|     var propertiesPanelHeaderLabel; | ||||
|     var propertiesPanelHeaderReveal; | ||||
|     var propertiesPanelHeaderHelp; | ||||
|  | ||||
|     var selectedObject; | ||||
|  | ||||
|     var tipContainer; | ||||
|     var tipBox; | ||||
|  | ||||
|     // TODO: remove this | ||||
|     var expandedSections = { | ||||
|         "property": false | ||||
|     }; | ||||
|  | ||||
|     function resizeStack() { | ||||
|         if (panels) { | ||||
|             var h = $(content).parent().height() - tipContainer.outerHeight(); | ||||
|             panels.resize(h) | ||||
|         } | ||||
|     } | ||||
|     function init() { | ||||
|  | ||||
|         content = document.createElement("div"); | ||||
| @@ -35,31 +51,79 @@ RED.sidebar.info = (function() { | ||||
|  | ||||
|         var stackContainer = $("<div>",{class:"red-ui-sidebar-info-stack"}).appendTo(content); | ||||
|  | ||||
|         sections = RED.stack.create({ | ||||
|             container: stackContainer | ||||
|         }).hide(); | ||||
|         var outlinerPanel = $("<div>").css({ | ||||
|             "overflow": "hidden", | ||||
|             "height": "calc(70%)" | ||||
|         }).appendTo(stackContainer); | ||||
|         var propertiesPanel = $("<div>").css({ | ||||
|             "overflow":"hidden", | ||||
|             "height":"100%", | ||||
|             "display": "flex", | ||||
|             "flex-direction": "column" | ||||
|         }).appendTo(stackContainer); | ||||
|         propertiesPanelHeader = $("<div>", {class:"red-ui-palette-header red-ui-info-header"}).css({ | ||||
|             "flex":"0 0 auto" | ||||
|         }).appendTo(propertiesPanel); | ||||
|  | ||||
|         propertiesSection = sections.add({ | ||||
|             title: RED._("sidebar.info.info"), | ||||
|             collapsible: true | ||||
|         propertiesPanelHeaderIcon = $("<span>").appendTo(propertiesPanelHeader); | ||||
|         propertiesPanelHeaderLabel = $("<span>").appendTo(propertiesPanelHeader); | ||||
|         propertiesPanelHeaderHelp = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({ | ||||
|             position: 'absolute', | ||||
|             top: '12px', | ||||
|             right: '32px' | ||||
|         }).on("click", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             if (selectedObject) { | ||||
|                 RED.sidebar.help.show(selectedObject.type); | ||||
|             } | ||||
|         }).appendTo(propertiesPanelHeader); | ||||
|         RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp")); | ||||
|  | ||||
|  | ||||
|         propertiesPanelHeaderReveal = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({ | ||||
|             position: 'absolute', | ||||
|             top: '12px', | ||||
|             right: '8px' | ||||
|         }).on("click", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             if (selectedObject) { | ||||
|                 RED.sidebar.info.outliner.reveal(selectedObject); | ||||
|                 RED.view.reveal(selectedObject.id); | ||||
|             } | ||||
|         }).appendTo(propertiesPanelHeader); | ||||
|         RED.popover.tooltip(propertiesPanelHeaderReveal,RED._("sidebar.help.showInOutline")); | ||||
|  | ||||
|  | ||||
|         propertiesPanelContent = $("<div>").css({ | ||||
|             "flex":"1 1 auto", | ||||
|             "overflow-y":"scroll", | ||||
|         }).appendTo(propertiesPanel); | ||||
|  | ||||
|  | ||||
|         panels = RED.panels.create({container: stackContainer}) | ||||
|         panels.ratio(0.6); | ||||
|         RED.sidebar.info.outliner.build().appendTo(outlinerPanel); | ||||
|  | ||||
|  | ||||
|         RED.sidebar.addTab({ | ||||
|             id: "info", | ||||
|             label: RED._("sidebar.info.label"), | ||||
|             name: RED._("sidebar.info.name"), | ||||
|             iconClass: "fa fa-info", | ||||
|             action:"core:show-info-tab", | ||||
|             content: content, | ||||
|             pinned: true, | ||||
|             enableOnEdit: true | ||||
|         }); | ||||
|         propertiesSection.expand(); | ||||
|  | ||||
|         infoSection = sections.add({ | ||||
|             title: RED._("sidebar.info.desc"), | ||||
|             collapsible: true | ||||
|         }); | ||||
|         infoSection.expand(); | ||||
|         infoSection.content.css("padding","6px"); | ||||
|         $(window).on("resize", resizeStack); | ||||
|         $(window).on("focus", resizeStack); | ||||
|  | ||||
|         helpSection = sections.add({ | ||||
|             title: RED._("sidebar.info.nodeHelp"), | ||||
|             collapsible: true | ||||
|         }); | ||||
|         helpSection.expand(); | ||||
|         helpSection.content.css("padding","6px"); | ||||
|  | ||||
|         var tipContainer = $('<div class="red-ui-help-tips"></div>').appendTo(content); | ||||
|         // Tip Box | ||||
|         tipContainer = $('<div class="red-ui-help-tips"></div>').appendTo(content); | ||||
|         tipBox = $('<div class="red-ui-help-tip"></div>').appendTo(tipContainer); | ||||
|         var tipButtons = $('<div class="red-ui-help-tips-buttons"></div>').appendTo(tipContainer); | ||||
|  | ||||
| @@ -75,17 +139,6 @@ RED.sidebar.info = (function() { | ||||
|             RED.actions.invoke("core:toggle-show-tips"); | ||||
|             RED.notify(RED._("sidebar.info.showTips")); | ||||
|         }); | ||||
|  | ||||
|         RED.sidebar.addTab({ | ||||
|             id: "info", | ||||
|             label: RED._("sidebar.info.label"), | ||||
|             name: RED._("sidebar.info.name"), | ||||
|             iconClass: "fa fa-info", | ||||
|             action:"core:show-info-tab", | ||||
|             content: content, | ||||
|             pinned: true, | ||||
|             enableOnEdit: true | ||||
|         }); | ||||
|         if (tips.enabled()) { | ||||
|             tips.start(); | ||||
|         } else { | ||||
| @@ -113,46 +166,36 @@ RED.sidebar.info = (function() { | ||||
|             refreshSelection(); | ||||
|             return; | ||||
|         } | ||||
|         sections.show(); | ||||
|         $(propertiesSection.content).empty(); | ||||
|         $(infoSection.content).empty(); | ||||
|         $(helpSection.content).empty(); | ||||
|         infoSection.title.text(RED._("sidebar.info.desc")); | ||||
|         $(propertiesPanelContent).empty(); | ||||
|  | ||||
|         var propRow; | ||||
|  | ||||
|         var table = $('<table class="red-ui-info-table"></table>').appendTo(propertiesSection.content); | ||||
|         var table = $('<table class="red-ui-info-table"></table>').appendTo(propertiesPanelContent); | ||||
|         var tableBody = $('<tbody>').appendTo(table); | ||||
|  | ||||
|         var subflowNode; | ||||
|         var subflowUserCount; | ||||
|  | ||||
|         var activeProject = RED.projects.getActiveProject(); | ||||
|         if (activeProject) { | ||||
|             propRow = $('<tr class="red-ui-help-info-row"><td>'+ RED._("sidebar.project.name") + '</td><td></td></tr>').appendTo(tableBody); | ||||
|             $(propRow.children()[1]).text(activeProject.name||""); | ||||
|             $('<tr class="red-ui-help-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody); | ||||
|             var editProjectButton = $('<button class="red-ui-button red-ui-button-small" style="position:absolute;right:2px;"><i class="fa fa-ellipsis-h"></i></button>') | ||||
|                 .appendTo(propRow.children()[1]) | ||||
|                 .on("click", function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     RED.projects.editProject(); | ||||
|                 }); | ||||
|             RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings')); | ||||
|         } | ||||
|         propertiesSection.container.show(); | ||||
|         infoSection.container.show(); | ||||
|         helpSection.container.show(); | ||||
|         if (node === null) { | ||||
|             RED.sidebar.info.outliner.select(null); | ||||
|             return; | ||||
|         } else if (Array.isArray(node)) { | ||||
|             // Multiple things selected | ||||
|             // - hide help and info sections | ||||
|             RED.sidebar.info.outliner.select(node); | ||||
|  | ||||
|             propertiesPanelHeaderIcon.empty(); | ||||
|             RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon); | ||||
|             propertiesPanelHeaderLabel.text("Selection"); | ||||
|             propertiesPanelHeaderReveal.hide(); | ||||
|             propertiesPanelHeaderHelp.hide(); | ||||
|             selectedObject = null; | ||||
|  | ||||
|             var types = { | ||||
|                 nodes:0, | ||||
|                 flows:0, | ||||
|                 subflows:0 | ||||
|                 subflows:0, | ||||
|                 groups: 0 | ||||
|             } | ||||
|             node.forEach(function(n) { | ||||
|                 if (n.type === 'tab') { | ||||
| @@ -160,12 +203,13 @@ RED.sidebar.info = (function() { | ||||
|                     types.nodes += RED.nodes.filterNodes({z:n.id}).length; | ||||
|                 } else if (n.type === 'subflow') { | ||||
|                     types.subflows++; | ||||
|                 } else if (n.type === 'group') { | ||||
|                     types.groups++; | ||||
|                 } else { | ||||
|                     types.nodes++; | ||||
|                 } | ||||
|             }); | ||||
|             helpSection.container.hide(); | ||||
|             infoSection.container.hide(); | ||||
|             // infoSection.container.hide(); | ||||
|             // - show the count of selected nodes | ||||
|             propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.selection")+"</td><td></td></tr>").appendTo(tableBody); | ||||
|  | ||||
| @@ -179,14 +223,19 @@ RED.sidebar.info = (function() { | ||||
|             if (types.nodes > 0) { | ||||
|                 $('<div>').text(RED._("clipboard.node",{count:types.nodes})).appendTo(counts); | ||||
|             } | ||||
|             if (types.groups > 0) { | ||||
|                 $('<div>').text(RED._("clipboard.group",{count:types.groups})).appendTo(counts); | ||||
|             } | ||||
|         } else { | ||||
|             // A single 'thing' selected. | ||||
|  | ||||
|             RED.sidebar.info.outliner.select(node); | ||||
|  | ||||
|             // Check to see if this is a subflow or subflow instance | ||||
|             var m = /^subflow(:(.+))?$/.exec(node.type); | ||||
|             if (m) { | ||||
|                 if (m[2]) { | ||||
|                     subflowNode = RED.nodes.subflow(m[2]); | ||||
|             var subflowRegex = /^subflow(:(.+))?$/.exec(node.type); | ||||
|             if (subflowRegex) { | ||||
|                 if (subflowRegex[2]) { | ||||
|                     subflowNode = RED.nodes.subflow(subflowRegex[2]); | ||||
|                 } else { | ||||
|                     subflowNode = node; | ||||
|                 } | ||||
| @@ -199,33 +248,76 @@ RED.sidebar.info = (function() { | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             propertiesPanelHeaderIcon.empty(); | ||||
|             RED.utils.createNodeIcon(node).appendTo(propertiesPanelHeaderIcon); | ||||
|             var objectLabel = RED.utils.getNodeLabel(node, node.type+": "+node.id) | ||||
|             var newlineIndex = objectLabel.indexOf("\\n"); | ||||
|             if (newlineIndex > -1) { | ||||
|                 objectLabel = objectLabel.substring(0,newlineIndex)+"..."; | ||||
|             } | ||||
|             propertiesPanelHeaderLabel.text(objectLabel); | ||||
|             propertiesPanelHeaderReveal.show(); | ||||
|             selectedObject = node; | ||||
|  | ||||
|             propRow = $('<tr class="red-ui-help-info-row"><td></td><td></td></tr>').appendTo(tableBody); | ||||
|             var objectType = "node"; | ||||
|             if (node.type === "subflow" || subflowRegex) { | ||||
|                 objectType = "subflow"; | ||||
|             } else if (node.type === "tab") { | ||||
|                 objectType = "flow"; | ||||
|             }else if (node.type === "group") { | ||||
|                 objectType = "group"; | ||||
|             } | ||||
|             $(propRow.children()[0]).text(RED._("sidebar.info."+objectType)) | ||||
|             RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); | ||||
|  | ||||
|             if (node.type === "tab" || node.type === "subflow") { | ||||
|                 // If nothing is selected, but we're on a flow or subflow tab. | ||||
|                 propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info."+(node.type==='tab'?'flow':'subflow'))+'</td><td></td></tr>').appendTo(tableBody); | ||||
|                 RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); | ||||
|                 propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.tabName")+"</td><td></td></tr>").appendTo(tableBody); | ||||
|                 $(propRow.children()[1]).text(node.label||node.name||""); | ||||
|                 if (node.type === "tab") { | ||||
|                     propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody); | ||||
|                     $(propRow.children()[1]).text((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled")) | ||||
|                 propertiesPanelHeaderHelp.hide(); | ||||
|  | ||||
|             } else if (node.type === "group") { | ||||
|                 propertiesPanelHeaderHelp.hide(); | ||||
|  | ||||
|                 propRow = $('<tr class="red-ui-help-info-row"><td> </td><td></td></tr>').appendTo(tableBody); | ||||
|  | ||||
|                 var typeCounts = { | ||||
|                     nodes:0, | ||||
|                     groups: 0 | ||||
|                 } | ||||
|                 var allNodes = RED.group.getNodes(node,true); | ||||
|                 allNodes.forEach(function(n) { | ||||
|                     if (n.type === "group") { | ||||
|                         typeCounts.groups++; | ||||
|                     } else { | ||||
|                         typeCounts.nodes++ | ||||
|                     } | ||||
|                 }); | ||||
|                 var counts = $('<div>').appendTo($(propRow.children()[1])); | ||||
|                 if (typeCounts.nodes > 0) { | ||||
|                     $('<div>').text(RED._("clipboard.node",{count:typeCounts.nodes})).appendTo(counts); | ||||
|                 } | ||||
|                 if (typeCounts.groups > 0) { | ||||
|                     $('<div>').text(RED._("clipboard.group",{count:typeCounts.groups})).appendTo(counts); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } else { | ||||
|                 // An actual node is selected in the editor - build up its properties table | ||||
|                 propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.node")+"</td><td></td></tr>").appendTo(tableBody); | ||||
|                 RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]); | ||||
|                 if (node.type !== "subflow" && node.type !== "unknown" && node.name) { | ||||
|                     propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("common.label.name")+'</td><td></td></tr>').appendTo(tableBody); | ||||
|                     $('<span class="red-ui-text-bidi-aware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'"></span>').text(node.name).appendTo(propRow.children()[1]); | ||||
|                 } | ||||
|                 if (!m) { | ||||
|                     propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+"</td><td></td></tr>").appendTo(tableBody); | ||||
|                 propertiesPanelHeaderHelp.show(); | ||||
|  | ||||
|                 if (!subflowRegex) { | ||||
|                     propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+'</td><td></td></tr>').appendTo(tableBody); | ||||
|                     $(propRow.children()[1]).text((node.type === "unknown")?node._orig.type:node.type); | ||||
|                     if (node.type === "unknown") { | ||||
|                         $('<span style="float: right; font-size: 0.8em"><i class="fa fa-warning"></i></span>').prependTo($(propRow.children()[1])) | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 var count = 0; | ||||
|                 if (!m && node.type != "subflow") { | ||||
|                 if (!subflowRegex && node.type != "subflow" && node.type != "group") { | ||||
|  | ||||
|                     var blankRow = $('<tr class="red-ui-help-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody); | ||||
|  | ||||
|                     var defaults; | ||||
|                     if (node.type === 'unknown') { | ||||
|                         defaults = {}; | ||||
| @@ -240,7 +332,6 @@ RED.sidebar.info = (function() { | ||||
|                         $(propRow.children()[1]).text(RED.nodes.getType(node.type).set.module); | ||||
|                         count++; | ||||
|                     } | ||||
|                     $('<tr class="red-ui-help-property-expand red-ui-help-info-property-row blank'+(expandedSections.property?"":" hide")+'"><td colspan="2"></td></tr>').appendTo(tableBody); | ||||
|  | ||||
|                     if (defaults) { | ||||
|                         for (var n in defaults) { | ||||
| @@ -248,7 +339,8 @@ RED.sidebar.info = (function() { | ||||
|                                 var val = node[n]; | ||||
|                                 var type = typeof val; | ||||
|                                 count++; | ||||
|                                 propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody); | ||||
|                                 propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td></td><td></td></tr>').appendTo(tableBody); | ||||
|                                 $(propRow.children()[0]).text(n); | ||||
|                                 if (defaults[n].type) { | ||||
|                                     var configNode = RED.nodes.node(val); | ||||
|                                     if (!configNode) { | ||||
| @@ -278,37 +370,35 @@ RED.sidebar.info = (function() { | ||||
|                         } | ||||
|                     } | ||||
|                     if (count > 0) { | ||||
|                         $('<tr class="red-ui-help-property-expand blank"><td colspan="2"><a href="#" class="node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="red-ui-help-property-more">'+RED._("sidebar.info.showMore")+'</span><span class="red-ui-help-property-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody); | ||||
|                         $('<a href="#" class="node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="red-ui-help-property-more">'+RED._("sidebar.info.showMore")+'</span><span class="red-ui-help-property-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a>').appendTo(blankRow.children()[0]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (node.type !== 'tab') { | ||||
|                     if (m) { | ||||
|                     if (subflowRegex) { | ||||
|                         $('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody); | ||||
|                         $('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+RED.utils.sanitize(subflowNode.name)+'</span></td></tr>').appendTo(tableBody); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (m) { | ||||
|             if (subflowRegex) { | ||||
|                 propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("subflow.category")+'</td><td></td></tr>').appendTo(tableBody); | ||||
|                 var category = subflowNode.category||"subflows"; | ||||
|                 $(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category})) | ||||
|                 $('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody); | ||||
|             } | ||||
|  | ||||
|             var helpText = ""; | ||||
|             if (node.type === "tab" || node.type === "subflow") { | ||||
|                 $(helpSection.container).hide(); | ||||
|             } else { | ||||
|                 $(helpSection.container).show(); | ||||
|                 if (subflowNode && node.type !== "subflow") { | ||||
|                     // Selected a subflow instance node. | ||||
|                     // - The subflow template info goes into help | ||||
|                     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>'); | ||||
|                 } | ||||
|                 setInfoText(helpText, helpSection.content); | ||||
|             } | ||||
|             // var helpText = ""; | ||||
|             // if (node.type === "tab" || node.type === "subflow") { | ||||
|             // } else { | ||||
|             //     if (subflowNode && node.type !== "subflow") { | ||||
|             //         // Selected a subflow instance node. | ||||
|             //         // - The subflow template info goes into help | ||||
|             //         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>'); | ||||
|             //     } | ||||
|             //     setInfoText(helpText, helpSection.content); | ||||
|             // } | ||||
|  | ||||
|             var infoText = ""; | ||||
|  | ||||
| @@ -320,7 +410,26 @@ RED.sidebar.info = (function() { | ||||
|             if (node.info) { | ||||
|                 infoText = infoText + RED.utils.renderMarkdown(node.info || "") | ||||
|             } | ||||
|             setInfoText(infoText, infoSection.content); | ||||
|             var infoSectionContainer = $("<div>").css("padding","0 6px 6px").appendTo(propertiesPanelContent) | ||||
|  | ||||
|             // var editInfo = $('<button class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-file-text-o"></button>').appendTo(infoSectionContainer).on("click", function(evt) { | ||||
|             //     //.text(RED._("sidebar.info.editDescription")) | ||||
|             //     evt.preventDefault(); | ||||
|             //     evt.stopPropagation(); | ||||
|             //     if (node.type === 'tab') { | ||||
|             // | ||||
|             //     } else if (node.type === 'subflow') { | ||||
|             // | ||||
|             //     } else if (node.type === 'group') { | ||||
|             // | ||||
|             //     } else if (node._def.category !== 'config') { | ||||
|             //         RED.editor.edit(node,"editor-tab-description"); | ||||
|             //     } else { | ||||
|             // | ||||
|             //     } | ||||
|             // }) | ||||
|  | ||||
|             setInfoText(infoText, infoSectionContainer); | ||||
|  | ||||
|             $(".red-ui-sidebar-info-stack").scrollTop(0); | ||||
|             $(".node-info-property-header").on("click", function(e) { | ||||
| @@ -336,7 +445,7 @@ RED.sidebar.info = (function() { | ||||
|         // propRow = $('<tr class="red-ui-help-info-row"><td>Actions</td><td></td></tr>').appendTo(tableBody); | ||||
|         // var actionBar = $(propRow.children()[1]); | ||||
|         // | ||||
|         // // var actionBar = $('<div>',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesSection.content); | ||||
|         // // var actionBar = $('<div>',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesPanel); | ||||
|         // $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar); | ||||
|         // $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar); | ||||
|         // $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar); | ||||
| @@ -411,6 +520,7 @@ RED.sidebar.info = (function() { | ||||
|         } | ||||
|         function startTips() { | ||||
|             $(".red-ui-sidebar-info").addClass('show-tips'); | ||||
|             resizeStack(); | ||||
|             if (enabled) { | ||||
|                 if (!startTimeout && !refreshTimeout) { | ||||
|                     if (tipCount === -1) { | ||||
| @@ -424,6 +534,7 @@ RED.sidebar.info = (function() { | ||||
|         } | ||||
|         function stopTips() { | ||||
|             $(".red-ui-sidebar-info").removeClass('show-tips'); | ||||
|             resizeStack(); | ||||
|             clearInterval(refreshTimeout); | ||||
|             clearTimeout(startTimeout); | ||||
|             refreshTimeout = null; | ||||
| @@ -448,15 +559,8 @@ RED.sidebar.info = (function() { | ||||
|     } | ||||
|  | ||||
|     function set(html,title) { | ||||
|         // tips.stop(); | ||||
|         // sections.show(); | ||||
|         refresh(null); | ||||
|         propertiesSection.container.hide(); | ||||
|         helpSection.container.hide(); | ||||
|         infoSection.container.show(); | ||||
|         infoSection.title.text(title||RED._("sidebar.info.desc")); | ||||
|         setInfoText(html,infoSection.content); | ||||
|         $(".red-ui-sidebar-info-stack").scrollTop(0); | ||||
|         console.warn("Deprecated use of RED.sidebar.info.set - use RED.sidebar.help.set instead") | ||||
|         RED.sidebar.help.set(html,title); | ||||
|     } | ||||
|  | ||||
|     function refreshSelection(selection) { | ||||
|   | ||||
| @@ -806,9 +806,9 @@ RED.utils = (function() { | ||||
|     function separateIconPath(icon) { | ||||
|         var result = {module: "", file: ""}; | ||||
|         if (icon) { | ||||
|             var index = icon.indexOf('icons/'); | ||||
|             if (index !== -1) { | ||||
|                 icon = icon.substring(index+6); | ||||
|             var index = icon.indexOf(RED.settings.apiRootUrl+'icons/'); | ||||
|             if (index === 0) { | ||||
|                 icon = icon.substring((RED.settings.apiRootUrl+'icons/').length); | ||||
|             } | ||||
|             index = icon.indexOf('/'); | ||||
|             if (index !== -1) { | ||||
| @@ -859,10 +859,15 @@ RED.utils = (function() { | ||||
|     } | ||||
|  | ||||
|     function getNodeIcon(def,node) { | ||||
|         if (def.category === 'config') { | ||||
|         if (node && node.type === '_selection_') { | ||||
|             return "font-awesome/fa-object-ungroup"; | ||||
|         } else if (node && node.type === 'group') { | ||||
|             return "font-awesome/fa-object-group" | ||||
|         } else if (def.category === 'config') { | ||||
|             return RED.settings.apiRootUrl+"icons/node-red/cog.svg" | ||||
|         } else if (node && node.type === 'tab') { | ||||
|             return RED.settings.apiRootUrl+"icons/node-red/subflow.svg" | ||||
|             return "red-ui-icons/red-ui-icons-flow" | ||||
|             // return RED.settings.apiRootUrl+"images/subflow_tab.svg" | ||||
|         } else if (node && node.type === 'unknown') { | ||||
|             return RED.settings.apiRootUrl+"icons/node-red/alert.svg" | ||||
|         } else if (node && node.icon) { | ||||
| @@ -921,6 +926,8 @@ RED.utils = (function() { | ||||
|         var l; | ||||
|         if (node.type === 'tab') { | ||||
|             l = node.label || defaultLabel | ||||
|         } else if (node.type === 'group') { | ||||
|             l = node.name || defaultLabel | ||||
|         } else { | ||||
|             l = node._def.label; | ||||
|             try { | ||||
| @@ -1054,11 +1061,63 @@ RED.utils = (function() { | ||||
|             } | ||||
|             // If the specified name is not defined in font-awesome, show arrow-in icon. | ||||
|             iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg" | ||||
|         } else if (iconPath.module === "red-ui-icons") { | ||||
|             var redIconElement = $('<i/>').appendTo(iconContainer); | ||||
|             redIconElement.addClass("red-ui-palette-icon red-ui-icons " + iconPath.file); | ||||
|             return; | ||||
|         } | ||||
|         var imageIconElement = $('<div/>',{class:"red-ui-palette-icon"}).appendTo(iconContainer); | ||||
|         imageIconElement.css("backgroundImage", "url("+iconUrl+")"); | ||||
|     } | ||||
|  | ||||
|     function createNodeIcon(node) { | ||||
|         var def = node._def; | ||||
|         var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}) | ||||
|         if (node.type === "_selection_") { | ||||
|             nodeDiv.addClass("red-ui-palette-icon-selection"); | ||||
|         } else if (node.type === "group") { | ||||
|             nodeDiv.addClass("red-ui-palette-icon-group"); | ||||
|         } else if (node.type === 'tab') { | ||||
|             nodeDiv.addClass("red-ui-palette-icon-flow"); | ||||
|         } else { | ||||
|             var colour = RED.utils.getNodeColor(node.type,def); | ||||
|             // if (node.type === 'tab') { | ||||
|             //     colour = "#C0DEED"; | ||||
|             // } | ||||
|             nodeDiv.css('backgroundColor',colour); | ||||
|             var borderColor = getDarkerColor(colour); | ||||
|             if (borderColor !== colour) { | ||||
|                 nodeDiv.css('border-color',borderColor) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var icon_url = RED.utils.getNodeIcon(def,node); | ||||
|         var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); | ||||
|         RED.utils.createIconElement(icon_url, iconContainer, true); | ||||
|         return nodeDiv; | ||||
|     } | ||||
|  | ||||
|     function getDarkerColor(c) { | ||||
|         var r,g,b; | ||||
|         if (/^#[a-f0-9]{6}$/i.test(c)) { | ||||
|             r = parseInt(c.substring(1, 3), 16); | ||||
|             g = parseInt(c.substring(3, 5), 16); | ||||
|             b = parseInt(c.substring(5, 7), 16); | ||||
|         } else if (/^#[a-f0-9]{3}$/i.test(c)) { | ||||
|             r = parseInt(c.substring(1, 2)+c.substring(1, 2), 16); | ||||
|             g = parseInt(c.substring(2, 3)+c.substring(2, 3), 16); | ||||
|             b = parseInt(c.substring(3, 4)+c.substring(3, 4), 16); | ||||
|         } else { | ||||
|             return c; | ||||
|         } | ||||
|         var l = 0.3 * r/255 + 0.59 * g/255 + 0.11 * b/255 ; | ||||
|         r = Math.max(0,r-50); | ||||
|         g = Math.max(0,g-50); | ||||
|         b = Math.max(0,b-50); | ||||
|         var s = ((r<<16) + (g<<8) + b).toString(16); | ||||
|         return '#'+'000000'.slice(0, 6-s.length)+s; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         createObjectElement: buildMessageElement, | ||||
|         getMessageProperty: getMessageProperty, | ||||
| @@ -1076,6 +1135,8 @@ RED.utils = (function() { | ||||
|         parseContextKey: parseContextKey, | ||||
|         createIconElement: createIconElement, | ||||
|         sanitize: sanitize, | ||||
|         renderMarkdown: renderMarkdown | ||||
|         renderMarkdown: renderMarkdown, | ||||
|         createNodeIcon: createNodeIcon, | ||||
|         getDarkerColor: getDarkerColor | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -67,9 +67,16 @@ RED.view.tools = (function() { | ||||
|  | ||||
|     function moveSelection(dx,dy) { | ||||
|         if (moving_set === null) { | ||||
|             moving_set = []; | ||||
|             var selection = RED.view.selection(); | ||||
|             if (selection.nodes) { | ||||
|                 moving_set = selection.nodes.map(function(n) { return {n:n}}); | ||||
|                 while (selection.nodes.length > 0) { | ||||
|                     var n = selection.nodes.shift(); | ||||
|                     moving_set.push({n:n}); | ||||
|                     if (n.type === "group") { | ||||
|                         selection.nodes = selection.nodes.concat(n.nodes); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (moving_set && moving_set.length > 0) { | ||||
| @@ -93,6 +100,9 @@ RED.view.tools = (function() { | ||||
|                 node.n.x += dx; | ||||
|                 node.n.y += dy; | ||||
|                 node.n.dirty = true; | ||||
|                 if (node.n.type === "group") { | ||||
|                     RED.group.markDirty(node.n); | ||||
|                 } | ||||
|                 minX = Math.min(node.n.x-node.n.w/2-5,minX); | ||||
|                 minY = Math.min(node.n.y-node.n.h/2-5,minY); | ||||
|             } | ||||
| @@ -105,13 +115,86 @@ RED.view.tools = (function() { | ||||
|                 } | ||||
|             } | ||||
|             RED.view.redraw(); | ||||
|         } else { | ||||
|             RED.view.scroll(dx*10,dy*10); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function setSelectedNodeLabelState(labelShown) { | ||||
|         var selection = RED.view.selection(); | ||||
|         var historyEvents = []; | ||||
|         var nodes = []; | ||||
|         if (selection.nodes) { | ||||
|             selection.nodes.forEach(function(n) { | ||||
|                 if (n.type !== 'subflow' && n.type !== 'group') { | ||||
|                     nodes.push(n); | ||||
|                 } else if (n.type === 'group') { | ||||
|                     nodes = nodes.concat( RED.group.getNodes(n,true)); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         nodes.forEach(function(n) { | ||||
|             var modified = false; | ||||
|             var oldValue = n.l === undefined?true:n.l; | ||||
|             var isLink = /^link (in|out)$/.test(n._def.type); | ||||
|  | ||||
|             if (labelShown) { | ||||
|                 if (n.l === false || (isLink && !n.hasOwnProperty('l'))) { | ||||
|                     n.l = true; | ||||
|                     modified = true; | ||||
|                 } | ||||
|             } else { | ||||
|                 if ((!isLink && (!n.hasOwnProperty('l') || n.l === true)) || (isLink && n.l === true) ) { | ||||
|                     n.l = false; | ||||
|                     modified = true; | ||||
|                 } | ||||
|             } | ||||
|             if (modified) { | ||||
|                 historyEvents.push({ | ||||
|                     t: "edit", | ||||
|                     node: n, | ||||
|                     changed: n.changed, | ||||
|                     changes: { | ||||
|                         l: oldValue | ||||
|                     } | ||||
|                 }) | ||||
|                 n.changed = true; | ||||
|                 n.dirty = true; | ||||
|                 n.resize = true; | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         if (historyEvents.length > 0) { | ||||
|             RED.history.push({ | ||||
|                 t: "multi", | ||||
|                 events: historyEvents, | ||||
|                 dirty: RED.nodes.dirty() | ||||
|             }) | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
|  | ||||
|         RED.view.redraw(); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); }) | ||||
|             RED.actions.add("core:hide-selected-node-labels", function() { setSelectedNodeLabelState(false); }) | ||||
|  | ||||
|             RED.actions.add("core:align-selection-to-grid", alignToGrid); | ||||
|  | ||||
|             RED.actions.add("core:scroll-view-up", function() { RED.view.scroll(0,-RED.view.gridSize());}); | ||||
|             RED.actions.add("core:scroll-view-right", function() { RED.view.scroll(RED.view.gridSize(),0);}); | ||||
|             RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());}); | ||||
|             RED.actions.add("core:scroll-view-left", function() { RED.view.scroll(-RED.view.gridSize(),0);}); | ||||
|  | ||||
|             RED.actions.add("core:step-view-up", function() { RED.view.scroll(0,-5*RED.view.gridSize());}); | ||||
|             RED.actions.add("core:step-view-right", function() { RED.view.scroll(5*RED.view.gridSize(),0);}); | ||||
|             RED.actions.add("core:step-view-down", function() { RED.view.scroll(0,5*RED.view.gridSize());}); | ||||
|             RED.actions.add("core:step-view-left", function() { RED.view.scroll(-5*RED.view.gridSize(),0);}); | ||||
|  | ||||
|             RED.actions.add("core:move-selection-up", function() { moveSelection(0,-1);}); | ||||
|             RED.actions.add("core:move-selection-right", function() { moveSelection(1,0);}); | ||||
|             RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);}); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -128,10 +128,6 @@ RED.workspaces = (function() { | ||||
|                             RED.history.push(historyEvent); | ||||
|                             RED.nodes.dirty(true); | ||||
|                             RED.sidebar.config.refresh(); | ||||
|                             var selection = RED.view.selection(); | ||||
|                             if (!selection.nodes && !selection.links) { | ||||
|                                 RED.sidebar.info.refresh(workspace); | ||||
|                             } | ||||
|                             if (changes.hasOwnProperty('disabled')) { | ||||
|                                 RED.nodes.eachNode(function(n) { | ||||
|                                     if (n.z === workspace.id) { | ||||
| @@ -140,6 +136,7 @@ RED.workspaces = (function() { | ||||
|                                 }); | ||||
|                                 RED.view.redraw(); | ||||
|                             } | ||||
|                             RED.events.emit("flows:change",workspace); | ||||
|                         } | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
| @@ -219,7 +216,10 @@ RED.workspaces = (function() { | ||||
|                 if (RED.view.state() != RED.state.IMPORT_DRAGGING) { | ||||
|                     RED.view.state(RED.state.DEFAULT); | ||||
|                 } | ||||
|                 RED.sidebar.info.refresh(workspace); | ||||
|                 var selection = RED.view.selection(); | ||||
|                 if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) { | ||||
|                     RED.sidebar.info.refresh(workspace); | ||||
|                 } | ||||
|                 tabflowEditor.destroy(); | ||||
|             } | ||||
|         } | ||||
| @@ -371,7 +371,9 @@ RED.workspaces = (function() { | ||||
|             var changes = { disabled: workspace.disabled }; | ||||
|             workspace.disabled = disabled; | ||||
|             $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); | ||||
|             $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); | ||||
|             if (id === activeWorkspace) { | ||||
|                 $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); | ||||
|             } | ||||
|             var historyEvent = { | ||||
|                 t: "edit", | ||||
|                 changes:changes, | ||||
| @@ -380,10 +382,11 @@ RED.workspaces = (function() { | ||||
|             } | ||||
|             workspace.changed = true; | ||||
|             RED.history.push(historyEvent); | ||||
|             RED.events.emit("flows:change",workspace); | ||||
|             RED.nodes.dirty(true); | ||||
|             RED.sidebar.config.refresh(); | ||||
|             var selection = RED.view.selection(); | ||||
|             if (!selection.nodes && !selection.links) { | ||||
|             if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) { | ||||
|                 RED.sidebar.info.refresh(workspace); | ||||
|             } | ||||
|             if (changes.hasOwnProperty('disabled')) { | ||||
| @@ -412,9 +415,14 @@ RED.workspaces = (function() { | ||||
|     } | ||||
|  | ||||
|     function setWorkspaceOrder(order) { | ||||
|         RED.nodes.setWorkspaceOrder(order.filter(function(id) { | ||||
|         var newOrder = order.filter(function(id) { | ||||
|             return RED.nodes.workspace(id) !== undefined; | ||||
|         })); | ||||
|         }) | ||||
|         var currentOrder = RED.nodes.getWorkspaceOrder(); | ||||
|         if (JSON.stringify(newOrder) !== JSON.stringify(currentOrder)) { | ||||
|             RED.nodes.setWorkspaceOrder(newOrder); | ||||
|             RED.events.emit("flows:reorder",newOrder); | ||||
|         } | ||||
|         workspace_tabs.order(order); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -23,7 +23,7 @@ $primary-background: #f3f3f3;//#0ff; | ||||
| $secondary-background: #fff;//#ff0; | ||||
| $secondary-background-selected: #efefef;//#e9e900; | ||||
| $secondary-background-inactive: #f0f0f0;//#f0f000; | ||||
| $secondary-background-hover: #ddd;//#dd0; | ||||
| $secondary-background-hover: #e6e6e6;//#dd0; | ||||
| $secondary-background-disabled: #f9f9f9;//#fafa0; | ||||
|  | ||||
| $tertiary-background:  #f7f7f7;//#f0f; | ||||
| @@ -94,7 +94,7 @@ $list-item-secondary-color: $secondary-text-color; | ||||
| $list-item-background: $secondary-background; | ||||
| $list-item-background-disabled: $secondary-background-inactive; | ||||
| $list-item-background-hover: $secondary-background-hover; | ||||
| $list-item-background-selected: $secondary-background-selected; | ||||
| $list-item-background-selected: #ffebc7; // #fff1e5; | ||||
| $list-item-border-selected: $secondary-text-color-selected; | ||||
|  | ||||
| $tab-text-color-active: $header-text-color; | ||||
| @@ -284,3 +284,8 @@ $debug-message-border: #eee; | ||||
| $debug-message-border-hover: #999; | ||||
| $debug-message-border-warning: #ffdf9d; | ||||
| $debug-message-border-error: #f99; | ||||
|  | ||||
| $group-default-fill: none; | ||||
| $group-default-fill-opacity: 1; | ||||
| $group-default-stroke: #999; | ||||
| $group-default-stroke-opacity: 1; | ||||
|   | ||||
| @@ -304,9 +304,6 @@ button.red-ui-button-small | ||||
|         &:first-child { | ||||
|             padding: 20px 20px 0; | ||||
|         } | ||||
|         &:last-child { | ||||
|             padding-bottom: 20px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .red-ui-editor-type-expression-tab-content { | ||||
| @@ -411,6 +408,133 @@ button.red-ui-button.red-ui-editor-node-appearance-button { | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-group-layout-picker { | ||||
|     padding: 5px; | ||||
|     background: $primary-background; | ||||
| } | ||||
| .red-ui-group-layout-picker-cell-text { | ||||
|     position: absolute; | ||||
|     width: 14px; | ||||
|     height: 2px; | ||||
|     border-top: 2px solid $secondary-text-color; | ||||
|     border-bottom: 2px solid $secondary-text-color; | ||||
|     margin: 2px; | ||||
|  | ||||
|     &.red-ui-group-layout-text-pos-nw { top: 0; left: 0; } | ||||
|     &.red-ui-group-layout-text-pos-n  { top: 0; left: calc(50% - 9px); } | ||||
|     &.red-ui-group-layout-text-pos-ne { top: 0; right: 0; } | ||||
|     &.red-ui-group-layout-text-pos-sw { bottom: 0; left: 0; } | ||||
|     &.red-ui-group-layout-text-pos-s { bottom: 0; left: calc(50% - 9px); } | ||||
|     &.red-ui-group-layout-text-pos-se { bottom: 0; right: 0; } | ||||
|     &.red-ui-group-layout-text-pos- { | ||||
|         width: 100%; | ||||
|         height: 100%; | ||||
|         border-radius: 5px; | ||||
|         margin: 0; | ||||
|         background-color: #FFF; | ||||
|         background-size: 100% 100%; | ||||
|         background-position: 0 0, 50% 50%; | ||||
|         background-image: linear-gradient(45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%,  transparent 55%, transparent),linear-gradient(-45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%,  transparent 55%, transparent); | ||||
|         border: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-group-layout-picker button.red-ui-search-result-node { | ||||
|     float: none; | ||||
|     position: relative; | ||||
|     padding: 0; | ||||
|     margin: 5px; | ||||
|     width: 32px; | ||||
|     height: 27px; | ||||
| } | ||||
|  | ||||
| button.red-ui-group-layout-picker-none { | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| .red-ui-color-picker { | ||||
|     input[type="text"] { | ||||
|         border-radius:0; | ||||
|         width: 100%; | ||||
|         margin-bottom: 0; | ||||
|         border: none; | ||||
|         border-bottom: 1px solid $form-input-border-color; | ||||
|     } | ||||
|     small { | ||||
|         color: $secondary-text-color; | ||||
|         margin-left: 5px; | ||||
|         margin-right: 4px; | ||||
|         display: inline-block; | ||||
|         min-width: 35px; | ||||
|         text-align: right; | ||||
|     } | ||||
|     background: $primary-background; | ||||
| } | ||||
| .red-ui-editor-node-appearance-button { | ||||
|     .red-ui-search-result-node { | ||||
|         overflow: hidden | ||||
|     } | ||||
| } | ||||
| .red-ui-color-picker-cell { | ||||
|     padding: 0; | ||||
|     border-style: solid; | ||||
|     border-width: 1px; | ||||
|     border-color: $secondary-border-color; | ||||
| } | ||||
| .red-ui-color-picker-swatch { | ||||
|     position: absolute; | ||||
|     top:-1px;right:-1px;left:-1px;bottom:-1px; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .red-ui-color-picker-cell-none { | ||||
|     height: 100%; | ||||
|     background-color: #FFF; | ||||
|     background-size: 100% 100%; | ||||
|     background-position: 0 0, 50% 50%; | ||||
|     background-image: linear-gradient(45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%,  transparent 55%, transparent),linear-gradient(-45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%,  transparent 55%, transparent) | ||||
| } | ||||
| .red-ui-search-result-node .red-ui-color-picker-cell-none { | ||||
|     border-radius: 4px; | ||||
|     background-size: 50% 50%; | ||||
|     background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); | ||||
| } | ||||
|  | ||||
| .red-ui-color-picker-opacity-slider { | ||||
|     position:relative; | ||||
|     vertical-align: middle; | ||||
|     display: inline-block; | ||||
|     width: calc(100% - 50px); | ||||
|     height: 14px; | ||||
|     margin: 6px 3px 8px; | ||||
|     box-sizing: border-box; | ||||
|     background-color: white; | ||||
|     background-image: | ||||
|         linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 25%), | ||||
|         linear-gradient(-45deg, #eee 25%, transparent 25%, transparent 75%, #eee 25%); | ||||
|     background-size: 6px 6px; | ||||
| } | ||||
| .red-ui-color-picker-opacity-slider-overlay { | ||||
|     position: absolute; | ||||
|     top:0;right:0;left:0;bottom:0; | ||||
|     background-image:linear-gradient(90deg, transparent 0%, #f00 100%); | ||||
|     background-size: 100% 100%; | ||||
|     border: 1px solid $primary-border-color; | ||||
| } | ||||
|  | ||||
| div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle { | ||||
|     z-Index: 10; | ||||
|     top: -4px; | ||||
|     cursor: pointer; | ||||
|     min-width: 0; | ||||
|     width: 10px; | ||||
|     height: 22px; | ||||
|     padding: 0; | ||||
|     border: 1px solid $primary-border-color; | ||||
|     border-radius: 1px; | ||||
|     background: $secondary-background; | ||||
|     box-sizing: border-box; | ||||
| } | ||||
| .red-ui-icon-picker { | ||||
|     select { | ||||
|         box-sizing: border-box; | ||||
|   | ||||
| @@ -71,6 +71,48 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-flow-group { | ||||
|     &.red-ui-flow-group-hovered { | ||||
|         .red-ui-flow-group-outline-select { | ||||
|             stroke-opacity: 0.8 !important; | ||||
|             stroke-dasharray: 10 4 !important; | ||||
|         } | ||||
|     } | ||||
|     &.red-ui-flow-group-active-hovered:not(.red-ui-flow-group-hovered) { | ||||
|         .red-ui-flow-group-outline-select { | ||||
|             stroke: $link-link-color; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-flow-group-outline { | ||||
|     fill: none; | ||||
|     stroke: $node-selected-color; | ||||
|     stroke-opacity: 0; | ||||
|     stroke-width: 12; | ||||
|     pointer-events: stroke; | ||||
| } | ||||
| .red-ui-flow-group-outline-select { | ||||
|     fill: none; | ||||
|     stroke: $node-selected-color; | ||||
|     pointer-events: stroke; | ||||
|     stroke-opacity: 0; | ||||
|     stroke-width: 3; | ||||
| } | ||||
| .red-ui-flow-group-body { | ||||
|     pointer-events: none; | ||||
|     fill: $group-default-fill; | ||||
|     fill-opacity: $group-default-fill-opacity; | ||||
|     stroke-width: 2; | ||||
|     stroke: $group-default-stroke; | ||||
|     stroke-opacity: $group-default-stroke-opacity; | ||||
| } | ||||
| .red-ui-flow-group-label { | ||||
|     @include disable-selection; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| .red-ui-flow-node-unknown { | ||||
|     stroke-dasharray:10,4; | ||||
|     stroke: $node-border-unknown; | ||||
| @@ -166,6 +208,9 @@ g.red-ui-flow-node-selected { | ||||
|         fill-opacity: 1; | ||||
|         stroke-dasharray: none; | ||||
|     } | ||||
|     .red-ui-flow-group, .red-ui-flow-group-body { | ||||
|         stroke-dasharray: 8, 3; | ||||
|     } | ||||
| } | ||||
| .red-ui-flow-node-disabled { | ||||
|     &.red-ui-flow-node, .red-ui-flow-node { | ||||
| @@ -248,6 +293,7 @@ g.red-ui-flow-node-selected { | ||||
|  | ||||
| .red-ui-flow-link-outline { | ||||
|     stroke: $view-background; | ||||
|     stroke-opacity: 0.4; | ||||
|     stroke-width: 5; | ||||
|     cursor: crosshair; | ||||
|     fill: none; | ||||
|   | ||||
| @@ -37,7 +37,7 @@ | ||||
|          border-radius: 4px; | ||||
|          font-family: $monospace-font !important; | ||||
|          font-size: 13px !important; | ||||
|          height: 300px; | ||||
|          height: 100%; | ||||
|          line-height: 1.3em; | ||||
|          padding: 6px 10px; | ||||
|          background: $clipboard-textarea-background; | ||||
| @@ -62,6 +62,7 @@ | ||||
|     background: $form-input-background; | ||||
|     &>div { | ||||
|         height: 100%; | ||||
|         box-sizing: border-box; | ||||
|     } | ||||
| } | ||||
| .red-ui-clipboard-dialog-box { | ||||
|   | ||||
| @@ -186,6 +186,21 @@ | ||||
|     background-size: contain; | ||||
|     background-repeat: no-repeat; | ||||
| } | ||||
| .red-ui-search-result-node { | ||||
|     &.red-ui-palette-icon-flow, | ||||
|     &.red-ui-palette-icon-group, | ||||
|     &.red-ui-palette-icon-selection { | ||||
|         background: none; | ||||
|         border-color: transparent; | ||||
|         .red-ui-palette-icon-container { | ||||
|             background: none; | ||||
|         } | ||||
|         .red-ui-palette-icon-fa { | ||||
|             color: $secondary-text-color; | ||||
|             font-size: 18px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .red-ui-palette-icon-fa { | ||||
|     color: white; | ||||
|     position: absolute; | ||||
|   | ||||
| @@ -22,9 +22,19 @@ | ||||
|         // border: 1px solid red; | ||||
|         box-sizing: border-box; | ||||
|     } | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|  | ||||
|     >.red-ui-panel:first-child { | ||||
|         flex: 0 0 auto; | ||||
|     } | ||||
|     >.red-ui-panel:last-child { | ||||
|         flex: 1 1 auto; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-panels-separator { | ||||
|     flex: 0 0 auto; | ||||
|     border-top: 1px solid $secondary-border-color; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     height: 7px; | ||||
| @@ -37,10 +47,13 @@ | ||||
| .red-ui-panel { | ||||
|     overflow: auto; | ||||
|     height: calc(50% - 4px); | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .red-ui-panels.red-ui-panels-horizontal { | ||||
|     height: 100%; | ||||
|     flex-direction: row; | ||||
|  | ||||
|     &>.red-ui-panel { | ||||
|         vertical-align: top; | ||||
|         display: inline-block; | ||||
|   | ||||
| @@ -150,6 +150,16 @@ | ||||
|  | ||||
| .red-ui-popover a.red-ui-button, | ||||
| .red-ui-popover button.red-ui-button { | ||||
|     &:not(.primary) { | ||||
|         border-color: $popover-button-border-color; | ||||
|         background: $popover-background; | ||||
|         color: $popover-color !important; | ||||
|     } | ||||
|     &:not(.primary):not(.disabled):not(.ui-button-disabled):hover { | ||||
|         border-color: $popover-button-border-color-hover; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     &.primary { | ||||
|         border-color: $popover-button-border-color; | ||||
|     } | ||||
|   | ||||
| @@ -18,7 +18,12 @@ | ||||
|     .red-ui-editableList-container { | ||||
|         padding: 0px; | ||||
|     } | ||||
|  | ||||
|     padding: 0; | ||||
|     .red-ui-projects-dialog-box { | ||||
|         box-sizing: border-box; | ||||
|         overflow-y: auto; | ||||
|         padding: 25px 25px 10px 25px; | ||||
|     } | ||||
| } | ||||
| #red-ui-project-settings-tab-settings { | ||||
|     overflow-y: scroll; | ||||
| @@ -99,6 +104,7 @@ | ||||
| .red-ui-projects-dialog-screen-create { | ||||
|     min-height: 500px; | ||||
|     button.red-ui-projects-dialog-screen-create-type { | ||||
|         position: relative; | ||||
|         height: auto; | ||||
|         padding: 10px; | ||||
|     } | ||||
| @@ -169,9 +175,14 @@ | ||||
| .red-ui-projects-dialog-project-list-container { | ||||
|     border: 1px solid $secondary-border-color; | ||||
|     border-radius: 2px; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     .red-ui-search-container { | ||||
|         flex-grow: 0; | ||||
|     } | ||||
| } | ||||
| .red-ui-projects-dialog-project-list-inner-container { | ||||
|     height: 300px; | ||||
|     flex-grow: 1 ; | ||||
|     overflow-y: scroll; | ||||
|     position:relative; | ||||
|     .red-ui-editableList-border { | ||||
| @@ -254,6 +265,9 @@ | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .red-ui-projects-dialog-screen-create-type { | ||||
|     position: relative; | ||||
| } | ||||
| .red-ui-projects-dialog-screen-create-type.red-ui-button.toggle.selected:not(.disabled):not(:disabled) { | ||||
|     color: $secondary-text-color-active !important; | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,13 @@ | ||||
|     top: 0px; | ||||
|     border: 1px solid $primary-border-color; | ||||
|     box-shadow: 0 0 10px $shadow; | ||||
|     background: $secondary-background; | ||||
|  | ||||
|     .red-ui-searchBox-container { | ||||
|         display: inline-block; | ||||
|         margin-right: 6px; | ||||
|         width: calc(100% - 30px); | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-type-search { | ||||
| @@ -87,6 +94,8 @@ | ||||
|     } | ||||
|     .red-ui-palette-icon { | ||||
|         width: 15px; | ||||
|         position:relative; | ||||
|         left: -1px; | ||||
|     } | ||||
|     .red-ui-search-result-description { | ||||
|         margin-left:28px; | ||||
| @@ -153,7 +162,7 @@ | ||||
|     width: 30px; | ||||
|     float:left; | ||||
|     height: 25px; | ||||
|     border-radius: 5px; | ||||
|     border-radius: 3px; | ||||
|     border: 1px solid $node-border; | ||||
|     background-position: 5% 50%; | ||||
|     background-repeat: no-repeat; | ||||
|   | ||||
| @@ -42,6 +42,7 @@ | ||||
| @import "tab-config"; | ||||
| @import "tab-context"; | ||||
| @import "tab-info"; | ||||
| @import "tab-help"; | ||||
| @import "popover"; | ||||
| @import "flow"; | ||||
| @import "palette-editor"; | ||||
|   | ||||
							
								
								
									
										27
									
								
								packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								packages/node_modules/@node-red/editor-client/src/sass/tab-help.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| .red-ui-sidebar-help-stack { | ||||
|     // height: calc(100% - 39px); | ||||
| } | ||||
| .red-ui-help-search { | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
| } | ||||
|  | ||||
| .red-ui-sidebar-help-toc { | ||||
|     .red-ui-treeList-label { | ||||
|         font-size: 13px; | ||||
|         padding: 2px 0; | ||||
|         overflow: hidden; | ||||
|         white-space: nowrap; | ||||
|     } | ||||
|  | ||||
| } | ||||
| #red-ui-sidebar-help-show-toc { | ||||
|     i.fa-angle-right { | ||||
|         transition: all 0.2s ease-in-out; | ||||
|     } | ||||
|     &.selected { | ||||
|         i.fa-angle-right { | ||||
|             transform: rotate(90deg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -14,9 +14,25 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| .red-ui-sidebar-info { | ||||
|     height: 100%; | ||||
| } | ||||
| .red-ui-sidebar-info  hr { | ||||
|     margin: 10px 0; | ||||
| } | ||||
| .red-ui-info-header { | ||||
|     padding-left: 9px; | ||||
|     line-height: 21px; | ||||
|     cursor: default; | ||||
|     > * { | ||||
|         vertical-align: middle | ||||
|     } | ||||
|     > span { | ||||
|         display: inline-block; | ||||
|         margin-left: 5px; | ||||
|     } | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
| } | ||||
| table.red-ui-info-table { | ||||
|     font-size: 14px; | ||||
|     margin: 0 0 10px; | ||||
| @@ -125,6 +141,9 @@ div.red-ui-info-table { | ||||
|         font-size: 1.296em; | ||||
|         line-height: 1.3em; | ||||
|         margin: 8px auto; | ||||
|         &.red-ui-help-title { | ||||
|             border-bottom: 1px solid $tertiary-border-color; | ||||
|         } | ||||
|     } | ||||
|     h2 { | ||||
|         font-weight: 500; | ||||
| @@ -214,12 +233,13 @@ div.red-ui-info-table { | ||||
|  | ||||
| } | ||||
| .red-ui-sidebar-info-stack { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     overflow-y: scroll; | ||||
|     height: 100%; | ||||
|     // position: absolute; | ||||
|     // top: 0; | ||||
|     // bottom: 0; | ||||
|     // left: 0; | ||||
|     // right: 0; | ||||
|     // overflow-y: scroll; | ||||
| } | ||||
| .red-ui-help-tips { | ||||
|     display: none; | ||||
| @@ -227,20 +247,23 @@ div.red-ui-info-table { | ||||
|     left:0; | ||||
|     right:0; | ||||
|     bottom: 0; | ||||
|     height: 150px; | ||||
|     height: 0; | ||||
|     transition: height 0.2s, padding 0.2s; | ||||
|     box-sizing: border-box; | ||||
|     border-top: 1px solid $secondary-border-color; | ||||
|     background-color: $secondary-background; | ||||
|     padding: 20px; | ||||
|     padding: 0; | ||||
|     box-shadow: 0 5px 20px 0px $shadow; | ||||
|     overflow-y: auto; | ||||
| } | ||||
| .red-ui-sidebar-info.show-tips { | ||||
|     .red-ui-sidebar-info-stack { | ||||
|         bottom: 150px; | ||||
|         height: calc(100% - 150px); | ||||
|     } | ||||
|     .red-ui-help-tips { | ||||
|         display: block; | ||||
|         height: 150px; | ||||
|         padding: 20px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -279,3 +302,220 @@ div.red-ui-info-table { | ||||
|     border-radius: 4px; | ||||
|     padding: 2px 4px 2px; | ||||
| } | ||||
|  | ||||
| .red-ui-info-outline,.red-ui-sidebar-help-toc { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|  | ||||
|     .red-ui-treeList { | ||||
|         flex-grow: 1; | ||||
|         position: relative; | ||||
|     } | ||||
|     .red-ui-treeList-container { | ||||
|         position: absolute; | ||||
|         top: 0; | ||||
|         bottom: 0; | ||||
|     } | ||||
|  | ||||
|     .red-ui-treeList-container,.red-ui-editableList-border { | ||||
|         border: none; | ||||
|         border-radius: 0; | ||||
|     } | ||||
|     .red-ui-treeList-label { | ||||
|         font-size: 13px; | ||||
|         padding: 2px 0; | ||||
|         overflow: hidden; | ||||
|     } | ||||
|     .red-ui-info-outline-project { | ||||
|         border-bottom: 1px solid $secondary-border-color; | ||||
|     } | ||||
|  | ||||
|     .red-ui-info-outline-item { | ||||
|         display: inline-block; | ||||
|         padding: 0; | ||||
|         font-size: 13px; | ||||
|         border: none; | ||||
|         .red-ui-palette-icon-fa { | ||||
|             position: relative; | ||||
|             top: 1px; | ||||
|             left: 0px; | ||||
|         } | ||||
|         &:hover { | ||||
|             background: inherit | ||||
|         } | ||||
|  | ||||
|         &.red-ui-info-outline-item-flow { | ||||
|             .red-ui-search-result-description { | ||||
|                 margin-left: 4px; | ||||
|             } | ||||
|         } | ||||
|         &.red-ui-info-outline-item-group .red-ui-search-result-node { | ||||
|             background: none; | ||||
|             border-color: transparent; | ||||
|             .red-ui-palette-icon-container { | ||||
|                 background: none; | ||||
|             } | ||||
|             .red-ui-palette-icon-fa { | ||||
|                 color: $secondary-text-color; | ||||
|                 font-size: 18px; | ||||
|             } | ||||
|         } | ||||
|         &.red-ui-info-outline-item-empty { | ||||
|             font-style: italic; | ||||
|             color: $form-placeholder-color; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .red-ui-search-result-node { | ||||
|         width: 24px; | ||||
|         height: 20px; | ||||
|         margin-top: 1px; | ||||
|     } | ||||
|  | ||||
|     .red-ui-palette-icon-container { | ||||
|         width: 24px; | ||||
|     } | ||||
|     .red-ui-palette-icon { | ||||
|         width: 20px; | ||||
|     } | ||||
|     .red-ui-search-result-description { | ||||
|         margin-left: 32px; | ||||
|         line-height: 22px; | ||||
|         white-space: nowrap; | ||||
|     } | ||||
|     .red-ui-search-result-node-label { | ||||
|         color: $secondary-text-color; | ||||
|     } | ||||
| } | ||||
| .red-ui-info-outline-item-control-spacer { | ||||
|     display: inline-block; | ||||
|     width: 23px; | ||||
| } | ||||
| .red-ui-info-outline-gutter { | ||||
|     display:none; | ||||
|     button { | ||||
|         position: absolute; | ||||
|         top: 1px; | ||||
|         left: 2px; | ||||
|     } | ||||
|     .red-ui-treeList-label:hover & { | ||||
|         display: inline; | ||||
|     } | ||||
| } | ||||
| .red-ui-info-outline-item-controls { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     bottom: 0; | ||||
|     right: 0px; | ||||
|     padding: 2px 3px 0 1px; | ||||
|     text-align: right; | ||||
|     background: $list-item-background; | ||||
|  | ||||
|     .red-ui-treeList-label:hover & { | ||||
|         background: $list-item-background-hover; | ||||
|     } | ||||
|     .red-ui-treeList-label.selected & { | ||||
|         background: $list-item-background-selected; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     &.red-ui-info-outline-item-hover-controls button { | ||||
|         min-width: 23px; | ||||
|     } | ||||
|  | ||||
|     .red-ui-treeList-label:not(:hover) &.red-ui-info-outline-item-hover-controls { | ||||
|         button { | ||||
|             border: none; | ||||
|             background: none; | ||||
|         } | ||||
|     } | ||||
|     .red-ui-info-outline-item-control-reveal, | ||||
|     .red-ui-info-outline-item-control-action { | ||||
|         display: none; | ||||
|     } | ||||
|     .red-ui-treeList-label:hover & { | ||||
|         .red-ui-info-outline-item-control-reveal, | ||||
|         .red-ui-info-outline-item-control-action { | ||||
|             display: inline-block; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .fa-ban { | ||||
|         display: none; | ||||
|     } | ||||
|     .red-ui-info-outline-item.red-ui-info-outline-item-disabled & { | ||||
|         .fa-ban { | ||||
|             display: inline-block; | ||||
|         } | ||||
|         .fa-circle-thin { | ||||
|             display: none; | ||||
|         } | ||||
|     } | ||||
|     button { | ||||
|         margin-right: 3px | ||||
|     } | ||||
| } | ||||
| .red-ui-info-outline-item-disabled { | ||||
|     .red-ui-search-result-node { | ||||
|         opacity: 0.4; | ||||
|     } | ||||
|     .red-ui-info-outline-item-label { | ||||
|         font-style: italic; | ||||
|         color: $secondary-text-color-disabled; | ||||
|     } | ||||
|     .red-ui-icons-flow { | ||||
|         opacity: 0.4; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| .red-ui-icons { | ||||
|     display: inline-block; | ||||
|     width: 18px; | ||||
|     &:before { | ||||
|         white-space: pre; | ||||
|         content: ' ' | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| .red-ui-icons-flow { | ||||
|     background-image: url('images/subflow_tab.svg'); | ||||
|     background-repeat: no-repeat; | ||||
|     background-size: contain; | ||||
|     filter: brightness(2.5); | ||||
| } | ||||
|  | ||||
| .red-ui-info-toolbar { | ||||
|     min-height: 39px; | ||||
|     height: 39px; | ||||
|     box-sizing: border-box; | ||||
|     text-align: left; | ||||
|     // padding-left: 9px; | ||||
|     // box-sizing: border-box; | ||||
|     // background: $palette-header-background; | ||||
|     // border-bottom: 1px solid $secondary-border-color; | ||||
|  | ||||
|     .red-ui-searchBox-container { | ||||
|         position: absolute; | ||||
|         top: 6px; | ||||
|         right: 8px; | ||||
|         width: calc(100% - 150px); | ||||
|         max-width: 250px; | ||||
|         background: $palette-header-background; | ||||
|         input.red-ui-searchBox-input { | ||||
|             border: 1px solid $secondary-border-color; | ||||
|             border-radius: 3px; | ||||
|             font-size: 12px; | ||||
|             height: 26px; | ||||
|         } | ||||
|         input:focus.red-ui-searchBox-input { | ||||
|             border: 1px solid $secondary-border-color; | ||||
|         } | ||||
|         i.fa-search, i.fa-times { | ||||
|             top: 7px; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -51,7 +51,10 @@ | ||||
|         white-space: nowrap; | ||||
|         overflow: hidden; | ||||
|         text-overflow: ellipsis; | ||||
|  | ||||
|         .red-ui-typedInput-value-label-inactive { | ||||
|             background: $secondary-background-disabled; | ||||
|             color: $secondary-text-color-disabled; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .red-ui-typedInput-options { | ||||
| @@ -117,7 +120,7 @@ button.red-ui-typedInput-option-trigger | ||||
|     } | ||||
|     &.disabled { | ||||
|         cursor: default; | ||||
|         i.red-ui-typedInput-icon { | ||||
|         > i.red-ui-typedInput-icon { | ||||
|             color: $secondary-text-color-disabled; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
| @@ -172,3 +172,44 @@ button.red-ui-footer-button-toggle { | ||||
|         margin-right: 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #red-ui-loading-progress { | ||||
|     position: absolute; | ||||
|     background: $primary-background; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     right: 0; | ||||
|     left: 0; | ||||
|     z-index: 200; | ||||
|     & > div { | ||||
|         position: absolute; | ||||
|         top: 30%; | ||||
|         left: 50%; | ||||
|         transform: translate(-50%, -50%); | ||||
|         width: 300px; | ||||
|         height:80px; | ||||
|         text-align: center; | ||||
|         color: $secondary-text-color; | ||||
|  | ||||
|     } | ||||
| } | ||||
| .red-ui-loading-bar { | ||||
|     box-sizing: border-box; | ||||
|     width: 300px; | ||||
|     height: 30px; | ||||
|     border: 2px solid $primary-border-color; | ||||
|     border-radius: 4px; | ||||
|  | ||||
|     > span { | ||||
|         display: block; | ||||
|         height: 100%; | ||||
|         background: $secondary-border-color; | ||||
|         transition: width 0.2s; | ||||
|         width: 10%; | ||||
|     } | ||||
| } | ||||
| .red-ui-loading-bar-label { | ||||
|     font-size: 13px; | ||||
|     margin-bottom: 2px; | ||||
| } | ||||
|   | ||||
| @@ -14,16 +14,14 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="inject"> | ||||
| <script type="text/html" data-template-name="inject"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label> | ||||
|         <input type="text" id="node-input-payload" style="width:70%"> | ||||
|         <input type="hidden" id="node-input-payloadType"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label> | ||||
|         <input type="text" id="node-input-topic"> | ||||
|     <div class="form-row node-input-property-container-row"> | ||||
|         <ol id="node-input-property-container"></ol> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row" id="node-once"> | ||||
| @@ -114,12 +112,7 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-tips" data-i18n="[html]inject.tip"></div> | ||||
| </script> | ||||
| <style> | ||||
|     .inject-time-row { | ||||
| @@ -155,37 +148,76 @@ | ||||
| </style> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
| (function() { | ||||
|  | ||||
|     function resizeDialog(size) { | ||||
|         size = size || { height: $(".red-ui-tray-content form").height() } | ||||
|         var rows = $("#dialog-form>div:not(.node-input-property-container-row):visible"); | ||||
|         var height = size.height; | ||||
|         for (var i=0; i<rows.length; i++) { | ||||
|             height -= $(rows[i]).outerHeight(true); | ||||
|         } | ||||
|         var editorRow = $("#dialog-form>div.node-input-property-container-row"); | ||||
|         height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); | ||||
|         height += 16; | ||||
|         $("#node-input-property-container").editableList('height',height); | ||||
|     } | ||||
|  | ||||
|     RED.nodes.registerType('inject',{ | ||||
|         category: 'common', | ||||
|         color:"#a6bbcf", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|             props:{value:[{p:"payload"},{p:"topic",vt:"str"}]}, | ||||
|             repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 2147483))) }}, | ||||
|             crontab: {value:""}, | ||||
|             once: {value:false}, | ||||
|             onceDelay: {value:0.1} | ||||
|             onceDelay: {value:0.1}, | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|         }, | ||||
|         icon: "inject.svg", | ||||
|         inputs:0, | ||||
|         outputs:1, | ||||
|         outputLabels: function(index) { | ||||
|             var lab = this.payloadType; | ||||
|             if (lab === "json") { | ||||
|                 try { | ||||
|                     lab = typeof JSON.parse(this.payload); | ||||
|                     if (lab === "object") { | ||||
|                         if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; } | ||||
|                     } | ||||
|                 } catch(e) { | ||||
|                     return this._("inject.label.invalid"); } | ||||
|             var lab = ''; | ||||
|  | ||||
|             // if only payload and topic - display payload type | ||||
|             // if only one property - show it's type | ||||
|             // if more than one property (other than payload and topic) - show "x properties" where x is the number of properties. | ||||
|             // this.props will not be an array for legacy inject nodes until they are re-deployed | ||||
|             // | ||||
|             var props = this.props; | ||||
|             if (!Array.isArray(props)) { | ||||
|                 props = [ | ||||
|                     { p:"payload", v: this.payload, vt: this.payloadType }, | ||||
|                     { p:"topic", v: this.topic, vt: "str" } | ||||
|                 ] | ||||
|             } | ||||
|             var name = "inject.label."+lab; | ||||
|             var label = this._(name); | ||||
|             if (name !== label) { | ||||
|                 return label; | ||||
|             if (props) { | ||||
|                 for (var i=0,l=props.length; i<l; i++) { | ||||
|                     if (i > 0) lab += "\n"; | ||||
|                     if (i === 5) { | ||||
|                         lab += " + "+(props.length-4); | ||||
|                         break; | ||||
|                     } | ||||
|                     lab += props[i].p+": "; | ||||
|  | ||||
|                     var propType = props[i].p === "payload"? this.payloadType : props[i].vt; | ||||
|                     if (propType === "json") { | ||||
|                         try { | ||||
|                             var parsedProp = JSON.parse(props[i].p === "payload"? this.payload : props[i].v); | ||||
|                             propType = typeof parsedProp; | ||||
|                             if (propType === "object" && Array.isArray(parsedProp)) { | ||||
|                                 propType = "Array"; | ||||
|                             } | ||||
|                         } catch(e) { | ||||
|                             propType = "invalid"; | ||||
|                         } | ||||
|                     } | ||||
|                     lab += this._("inject.label."+propType); | ||||
|                 } | ||||
|             } | ||||
|             return lab; | ||||
|         }, | ||||
| @@ -201,27 +233,33 @@ | ||||
|             } | ||||
|             if (this.name) { | ||||
|                 return this.name+suffix; | ||||
|             } else if (this.payloadType === "string" || | ||||
|                     this.payloadType === "str" || | ||||
|                     this.payloadType === "num" || | ||||
|                     this.payloadType === "bool" || | ||||
|                     this.payloadType === "json") { | ||||
|                 if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) { | ||||
|                     return this.topic + ":" + this.payload+suffix; | ||||
|                 } else if (this.payload.length > 0 && this.payload.length < 24) { | ||||
|                     return this.payload+suffix; | ||||
|             } | ||||
|  | ||||
|             var payload = this.payload || ""; | ||||
|             var payloadType = this.payloadType || "str"; | ||||
|             var topic = this.topic || ""; | ||||
|  | ||||
|             if (payloadType === "string" || | ||||
|                     payloadType === "str" || | ||||
|                     payloadType === "num" || | ||||
|                     payloadType === "bool" || | ||||
|                     payloadType === "json") { | ||||
|                 if ((topic !== "") && ((topic.length + payload.length) <= 32)) { | ||||
|                     return topic + ":" + payload+suffix; | ||||
|                 } else if (payload.length > 0 && payload.length < 24) { | ||||
|                     return payload+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.inject")+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'date') { | ||||
|                 if ((this.topic !== "") && (this.topic.length <= 16)) { | ||||
|                     return this.topic + ":" + this._("inject.timestamp")+suffix; | ||||
|             } else if (payloadType === 'date' || payloadType === 'bin' || payloadType === 'env') { | ||||
|                 if ((topic !== "") && (topic.length <= 16)) { | ||||
|                     return topic + ":" + this._('inject.label.'+payloadType)+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.timestamp")+suffix; | ||||
|                     return this._('inject.label.'+payloadType)+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'flow' || this.payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(this.payload); | ||||
|                 return this.payloadType+"."+key.key+suffix; | ||||
|             } else if (payloadType === 'flow' || payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(payload); | ||||
|                 return payloadType+"."+key.key+suffix; | ||||
|             } else { | ||||
|                 return this._("inject.inject")+suffix; | ||||
|             } | ||||
| @@ -239,13 +277,6 @@ | ||||
|             } else if (this.payloadType === 'string' || this.payloadType === 'none') { | ||||
|                 this.payloadType = "str"; | ||||
|             } | ||||
|             $("#node-input-payloadType").val(this.payloadType); | ||||
|  | ||||
|             $("#node-input-payload").typedInput({ | ||||
|                 default: 'str', | ||||
|                 typeField: $("#node-input-payloadType"), | ||||
|                 types:['flow','global','str','num','bool','json','bin','date','env'] | ||||
|             }); | ||||
|  | ||||
|             $("#inject-time-type-select").on("change", function() { | ||||
|                 $("#node-input-crontab").val(''); | ||||
| @@ -259,6 +290,11 @@ | ||||
|                     $("#node-once").hide(); | ||||
|                     $("#node-input-once").prop('checked', false); | ||||
|                 } | ||||
|  | ||||
|                 // Scroll down | ||||
|                 var scrollDiv = $("#dialog-form").parent(); | ||||
|                 scrollDiv.scrollTop(scrollDiv.prop('scrollHeight')); | ||||
|                 resizeDialog(); | ||||
|             }); | ||||
|  | ||||
|             $("#node-input-once").on("change", function() { | ||||
| @@ -378,7 +414,70 @@ | ||||
|             $("#inject-time-type-select").val(repeattype); | ||||
|             $("#inject-time-row-"+repeattype).show(); | ||||
|  | ||||
|             $("#node-input-payload").typedInput('type',this.payloadType); | ||||
|             /* */ | ||||
|  | ||||
|             $('#node-input-property-container').css('min-height','120px').css('min-width','450px').editableList({ | ||||
|                 addItem: function(container,i,opt) { | ||||
|                     var prop = opt; | ||||
|                     if (!prop.hasOwnProperty('p')) { | ||||
|                         prop = {p:"",v:"",vt:"str"}; | ||||
|                     } | ||||
|                     container.css({ | ||||
|                         overflow: 'hidden', | ||||
|                         whiteSpace: 'nowrap' | ||||
|                     }); | ||||
|                     var row = $('<div/>').appendTo(container); | ||||
|  | ||||
|                     var propertyName = $('<input/>',{class:"node-input-prop-property-name",type:"text"}) | ||||
|                         .css("width","30%") | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({types:['msg']}); | ||||
|  | ||||
|                     $('<div/>',{style: 'display:inline-block; padding:0px 6px;'}) | ||||
|                         .text('=') | ||||
|                         .appendTo(row); | ||||
|  | ||||
|                     var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"}) | ||||
|                         .css("width","calc(70% - 30px)") | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']}); | ||||
|  | ||||
|                     propertyName.typedInput('value',prop.p); | ||||
|  | ||||
|                     propertyValue.typedInput('value',prop.v); | ||||
|                     propertyValue.typedInput('type',prop.vt); | ||||
|                 }, | ||||
|                 removable: true, | ||||
|                 sortable: true | ||||
|             }); | ||||
|  | ||||
|             if (!this.props) { | ||||
|                 var payload = { | ||||
|                     p:'payload', | ||||
|                     v: this.payload ? this.payload : '', | ||||
|                     vt:this.payloadType ? this.payloadType : 'date' | ||||
|                 }; | ||||
|                 var topic = { | ||||
|                     p:'topic', | ||||
|                     v: this.topic ? this.topic : '', | ||||
|                     vt:'string' | ||||
|                 } | ||||
|                 this.props = [payload,topic]; | ||||
|             } | ||||
|  | ||||
|             for (var i=0; i<this.props.length; i++) { | ||||
|                 var prop = this.props[i]; | ||||
|                 var newProp = { p: prop.p, v: prop.v, vt: prop.vt }; | ||||
|                 if (newProp.v === undefined) { | ||||
|                     if (prop.p === 'payload') { | ||||
|                         newProp.v = this.payload ? this.payload : ''; | ||||
|                         newProp.vt = this.payloadType ? this.payloadType : 'date'; | ||||
|                     } else if (prop.p === 'topic' && prop.vt === "str") { | ||||
|                         newProp.v =  this.topic ? this.topic : ''; | ||||
|                     } | ||||
|                 } | ||||
|                 $("#node-input-property-container").editableList('addItem',newProp); | ||||
|             } | ||||
|  | ||||
|             $("#inject-time-type-select").trigger("change"); | ||||
|             $("#inject-time-interval-time-start").trigger("change"); | ||||
| @@ -474,6 +573,34 @@ | ||||
|  | ||||
|             $("#node-input-repeat").val(repeat); | ||||
|             $("#node-input-crontab").val(crontab); | ||||
|  | ||||
|             /* Gather the injected properties of the msg object */ | ||||
|             var props = $("#node-input-property-container").editableList('items'); | ||||
|             var node = this; | ||||
|             node.props= []; | ||||
|             delete node.payloadType; | ||||
|             delete node.payload; | ||||
|             delete node.topic; | ||||
|             props.each(function(i) { | ||||
|                 var prop = $(this); | ||||
|                 var p = { | ||||
|                     p:prop.find(".node-input-prop-property-name").typedInput('value') | ||||
|                 }; | ||||
|                 if (p.p) { | ||||
|                     p.v = prop.find(".node-input-prop-property-value").typedInput('value'); | ||||
|                     p.vt = prop.find(".node-input-prop-property-value").typedInput('type'); | ||||
|                     if (p.p === "payload") { // save payload to old "legacy" property | ||||
|                         node.payloadType = p.vt; | ||||
|                         node.payload = p.v; | ||||
|                         delete p.v; | ||||
|                         delete p.vt; | ||||
|                     } else if (p.p === "topic" && p.vt === "str") { | ||||
|                         node.topic = p.v; | ||||
|                         delete p.v; | ||||
|                     } | ||||
|                     node.props.push(p); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|         button: { | ||||
|             enabled: function() { | ||||
| @@ -483,12 +610,7 @@ | ||||
|                 if (this.changed) { | ||||
|                     return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning"); | ||||
|                 } | ||||
|                 var payload = this.payload; | ||||
|                 if ((this.payloadType === 'flow') || | ||||
|                     (this.payloadType === 'global')) { | ||||
|                     var key = RED.utils.parseContextKey(payload); | ||||
|                     payload = this.payloadType+"."+key.key; | ||||
|                 } | ||||
|  | ||||
|                 var label = this._def.label.call(this); | ||||
|                 if (label.length > 30) { | ||||
|                     label = label.substring(0,50)+"..."; | ||||
| @@ -514,7 +636,8 @@ | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         }, | ||||
|         oneditresize: resizeDialog | ||||
|     }); | ||||
|  | ||||
| })(); | ||||
| </script> | ||||
|   | ||||
| @@ -20,9 +20,32 @@ module.exports = function(RED) { | ||||
|  | ||||
|     function InjectNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.topic = n.topic; | ||||
|         this.payload = n.payload; | ||||
|         this.payloadType = n.payloadType; | ||||
|  | ||||
|         /* Handle legacy */ | ||||
|         if(!Array.isArray(n.props)){ | ||||
|             n.props = []; | ||||
|             n.props.push({ | ||||
|                 p:'payload', | ||||
|                 v:n.payload, | ||||
|                 vt:n.payloadType | ||||
|             }); | ||||
|             n.props.push({ | ||||
|                 p:'topic', | ||||
|                 v:n.topic, | ||||
|                 vt:'str' | ||||
|             }); | ||||
|         } else { | ||||
|             for (var i=0,l=n.props.length; i<l; i++) { | ||||
|                 if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) { | ||||
|                     n.props[i].v = n.payload; | ||||
|                     n.props[i].vt = n.payloadType; | ||||
|                 } else if (n.props[i].p === 'topic' && n.props[i].vt === 'str' && !n.props[i].hasOwnProperty('v')) { | ||||
|                     n.props[i].v = n.topic; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.props = n.props; | ||||
|         this.repeat = n.repeat; | ||||
|         this.crontab = n.crontab; | ||||
|         this.once = n.once; | ||||
| @@ -31,65 +54,83 @@ module.exports = function(RED) { | ||||
|         this.cronjob = null; | ||||
|         var node = this; | ||||
|  | ||||
|         node.props.forEach(function (prop) { | ||||
|             if (prop.vt === "jsonata") { | ||||
|                 try { | ||||
|                     var val = prop.v ? prop.v : ""; | ||||
|                     prop.exp = RED.util.prepareJSONataExpression(val, node); | ||||
|                 } | ||||
|                 catch (err) { | ||||
|                     node.error(RED._("inject.errors.invalid-expr", {error:err.message})); | ||||
|                     prop.exp = null; | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         if (node.repeat > 2147483) { | ||||
|             node.error(RED._("inject.errors.toolong", this)); | ||||
|             delete node.repeat; | ||||
|         } | ||||
|  | ||||
|         node.repeaterSetup = function () { | ||||
|           if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) { | ||||
|             this.repeat = this.repeat * 1000; | ||||
|             if (RED.settings.verbose) { | ||||
|               this.log(RED._("inject.repeat", this)); | ||||
|             if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) { | ||||
|                 this.repeat = this.repeat * 1000; | ||||
|                 if (RED.settings.verbose) { | ||||
|                     this.log(RED._("inject.repeat", this)); | ||||
|                 } | ||||
|                 this.interval_id = setInterval(function() { | ||||
|                     node.emit("input", {}); | ||||
|                 }, this.repeat); | ||||
|             } else if (this.crontab) { | ||||
|                 if (RED.settings.verbose) { | ||||
|                     this.log(RED._("inject.crontab", this)); | ||||
|                 } | ||||
|                 this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true); | ||||
|             } | ||||
|             this.interval_id = setInterval(function() { | ||||
|               node.emit("input", {}); | ||||
|             }, this.repeat); | ||||
|           } else if (this.crontab) { | ||||
|             if (RED.settings.verbose) { | ||||
|               this.log(RED._("inject.crontab", this)); | ||||
|             } | ||||
|             this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true); | ||||
|           } | ||||
|         }; | ||||
|  | ||||
|         if (this.once) { | ||||
|             this.onceTimeout = setTimeout( function() { | ||||
|               node.emit("input",{}); | ||||
|               node.repeaterSetup(); | ||||
|                 node.emit("input",{}); | ||||
|                 node.repeaterSetup(); | ||||
|             }, this.onceDelay); | ||||
|         } else { | ||||
|           node.repeaterSetup(); | ||||
|             node.repeaterSetup(); | ||||
|         } | ||||
|  | ||||
|         this.on("input",function(msg) { | ||||
|             msg.topic = this.topic; | ||||
|             if (this.payloadType !== 'flow' && this.payloadType !== 'global') { | ||||
|                 try { | ||||
|                     if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") { | ||||
|                         msg.payload = Date.now(); | ||||
|                     } else if (this.payloadType == null) { | ||||
|                         msg.payload = this.payload; | ||||
|                     } else if (this.payloadType === 'none') { | ||||
|                         msg.payload = ""; | ||||
|                     } else { | ||||
|                         msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg); | ||||
|                     } | ||||
|                     this.send(msg); | ||||
|                     msg = null; | ||||
|                 } catch(err) { | ||||
|                     this.error(err,msg); | ||||
|                 } | ||||
|             } else { | ||||
|                 RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg, function(err,res) { | ||||
|                     if (err) { | ||||
|                         node.error(err,msg); | ||||
|                     } else { | ||||
|                         msg.payload = res; | ||||
|                         node.send(msg); | ||||
|                     } | ||||
|         this.on("input", function(msg) { | ||||
|             var errors = []; | ||||
|  | ||||
|                 }); | ||||
|             this.props.forEach(p => { | ||||
|                 var property = p.p; | ||||
|                 var value = p.v ? p.v : ''; | ||||
|                 var valueType = p.vt ? p.vt : 'str'; | ||||
|  | ||||
|                 if (!property) return; | ||||
|  | ||||
|                 if (valueType === "jsonata") { | ||||
|                     if (p.exp) { | ||||
|                         try { | ||||
|                             var val = RED.util.evaluateJSONataExpression(p.exp, msg); | ||||
|                             RED.util.setMessageProperty(msg, property, val, true); | ||||
|                         } | ||||
|                         catch  (err) { | ||||
|                             errors.push(err.message); | ||||
|                         } | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|                 try { | ||||
|                     RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true); | ||||
|                 } catch (err) { | ||||
|                     errors.push(err.toString()); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (errors.length) { | ||||
|                 node.error(errors.join('; '), msg); | ||||
|             } else { | ||||
|                 node.send(msg); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -1,30 +1,35 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="debug"> | ||||
| <script type="text/html" data-template-name="debug"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label> | ||||
|         <input id="node-input-typed-complete" type="text" style="width: 70%"> | ||||
|         <input id="node-input-complete" type="hidden"> | ||||
|         <input id="node-input-targetType" type="hidden"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-tosidebar"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label> | ||||
|         <label for="node-input-tosidebar" style="width:70%"> | ||||
|         <input type="checkbox" id="node-input-tosidebar" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toSidebar"></span> | ||||
|         <input type="checkbox" id="node-input-tosidebar" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toSidebar"></span> | ||||
|         </label> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-console"> </label> | ||||
|         <label for="node-input-console" style="width:70%"> | ||||
|         <input type="checkbox" id="node-input-console" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toConsole"></span> | ||||
|         <input type="checkbox" id="node-input-console" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toConsole"></span> | ||||
|         </label> | ||||
|     </div> | ||||
|     <div class="form-row" id="node-tostatus-line"> | ||||
|     <div class="form-row"> | ||||
|     <label for="node-input-tostatus"> </label> | ||||
|     <label for="node-input-tostatus" style="width:70%"> | ||||
|         <input type="checkbox" id="node-input-tostatus" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toStatus"></span> | ||||
|         <input type="checkbox" id="node-input-tostatus" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toStatus"></span> | ||||
|     </label> | ||||
|     </div> | ||||
|     <div class="form-row" id="node-tostatus-line"> | ||||
|         <label for="node-input-typed-status"><i class="fa fa-ellipsis-h"></i> <span data-i18n="debug.status"></span></label> | ||||
|         <input id="node-input-typed-status" type="text" style="width: 70%"> | ||||
|         <input id="node-input-statusVal" type="hidden"> | ||||
|         <input id="node-input-statusType" type="hidden"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
| @@ -36,6 +41,36 @@ | ||||
| <script type="text/javascript"> | ||||
| (function() { | ||||
|     var subWindow = null; | ||||
|  | ||||
|     function activateAjaxCall(node, active, successCallback) { | ||||
|         var url; | ||||
|         var body; | ||||
|  | ||||
|         if (Array.isArray(node)) { | ||||
|             url = "debug/"+(active?"enable":"disable"); | ||||
|             body = {nodes: node.map(function(n) { return n.id})} | ||||
|             node = node[0]; | ||||
|         } else { | ||||
|             url = "debug/"+node.id+"/"+(active?"enable":"disable"); | ||||
|         } | ||||
|         $.ajax({ | ||||
|             url: url, | ||||
|             type: "POST", | ||||
|             data: body, | ||||
|             success: successCallback, | ||||
|             error: function(jqXHR,textStatus,errorThrown) { | ||||
|                 if (jqXHR.status == 404) { | ||||
|                     RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.not-deployed")}),"error"); | ||||
|                 } else if (jqXHR.status === 0) { | ||||
|                     RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.no-response")}),"error"); | ||||
|                 } else { | ||||
|                     // TODO where is the err.status comming from? | ||||
|                     RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:err.status,message:err.response})}),"error"); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     RED.nodes.registerType('debug',{ | ||||
|         category: 'common', | ||||
|         defaults: { | ||||
| @@ -45,7 +80,9 @@ | ||||
|             console: {value:false}, | ||||
|             tostatus: {value:false}, | ||||
|             complete: {value:"false", required:true}, | ||||
|             targetType: {value:undefined} | ||||
|             targetType: {value:undefined}, | ||||
|             statusVal: {value:""}, | ||||
|             statusType: {value:"auto"} | ||||
|         }, | ||||
|         label: function() { | ||||
|             var suffix = ""; | ||||
| @@ -73,38 +110,28 @@ | ||||
|             onclick: function() { | ||||
|                 var label = this.name||"debug"; | ||||
|                 var node = this; | ||||
|                 $.ajax({ | ||||
|                     url: "debug/"+this.id+"/"+(this.active?"enable":"disable"), | ||||
|                     type: "POST", | ||||
|                     success: function(resp, textStatus, xhr) { | ||||
|                         var historyEvent = { | ||||
|                             t:'edit', | ||||
|                             node:node, | ||||
|                             changes:{ | ||||
|                                 active:!node.active | ||||
|                             }, | ||||
|                             dirty:node.dirty, | ||||
|                             changed:node.changed | ||||
|                         }; | ||||
|                         node.changed = true; | ||||
|                         node.dirty = true; | ||||
|                         RED.nodes.dirty(true); | ||||
|                         RED.history.push(historyEvent); | ||||
|                         RED.view.redraw(); | ||||
|                         if (xhr.status == 200) { | ||||
|                             RED.notify(node._("debug.notification.activated",{label:label}),"success"); | ||||
|                         } else if (xhr.status == 201) { | ||||
|                             RED.notify(node._("debug.notification.deactivated",{label:label}),"success"); | ||||
|                         } | ||||
|                     }, | ||||
|                     error: function(jqXHR,textStatus,errorThrown) { | ||||
|                         if (jqXHR.status == 404) { | ||||
|                             RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.not-deployed")}),"error"); | ||||
|                         } else if (jqXHR.status === 0) { | ||||
|                             RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.no-response")}),"error"); | ||||
|                         } else { | ||||
|                             RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:err.status,message:err.response})}),"error"); | ||||
|                 activateAjaxCall(node, node.active, function(resp, textStatus, xhr) { | ||||
|                     var historyEvent = { | ||||
|                         t:'edit', | ||||
|                         node:node, | ||||
|                         changes:{ | ||||
|                             active:!node.active | ||||
|                         }, | ||||
|                         dirty:node.dirty, | ||||
|                         changed:node.changed, | ||||
|                         callback: function(ev) { | ||||
|                             activateAjaxCall(ev.node, ev.node.active); | ||||
|                         } | ||||
|                     }; | ||||
|                     node.changed = true; | ||||
|                     node.dirty = true; | ||||
|                     RED.nodes.dirty(true); | ||||
|                     RED.history.push(historyEvent); | ||||
|                     RED.view.redraw(); | ||||
|                     if (xhr.status == 200) { | ||||
|                         RED.notify(node._("debug.notification.activated",{label:label}),"success"); | ||||
|                     } else if (xhr.status == 201) { | ||||
|                         RED.notify(node._("debug.notification.deactivated",{label:label}),"success"); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
| @@ -266,6 +293,78 @@ | ||||
|             RED.events.on("project:change", this.clearMessageList); | ||||
|             RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) }); | ||||
|  | ||||
|             RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); }); | ||||
|             RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); }); | ||||
|             RED.actions.add("core:activate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, false),true); }); | ||||
|  | ||||
|             RED.actions.add("core:deactivate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(false), false); }); | ||||
|             RED.actions.add("core:deactivate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, true),false); }); | ||||
|             RED.actions.add("core:deactivate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, false),false); }); | ||||
|  | ||||
|             function getSelectedDebugNodes(state) { | ||||
|                 var nodes = []; | ||||
|                 var selection = RED.view.selection(); | ||||
|                 if (selection.nodes) { | ||||
|                     selection.nodes.forEach(function(n) { | ||||
|                         if (RED.nodes.subflow(n.z)) { | ||||
|                             return; | ||||
|                         } | ||||
|                         if (n.type === 'debug' && n.active !== state) { | ||||
|                             nodes.push(n); | ||||
|                         } else if (n.type === 'group') { | ||||
|                             nodes = nodes.concat( RED.group.getNodes(n,true).filter(function(n) { | ||||
|                                 return n.type === 'debug' && n.active !== state | ||||
|                             })); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|                 return nodes; | ||||
|  | ||||
|             } | ||||
|             function getMatchingDebugNodes(state,globally) { | ||||
|                 var nodes = []; | ||||
|                 var filter = {type:"debug"}; | ||||
|                 if (!globally) { | ||||
|                     filter.z = RED.workspaces.active(); | ||||
|                 } | ||||
|                 var candidateNodes = RED.nodes.filterNodes(filter); | ||||
|                 nodes = candidateNodes.filter(function(n) { | ||||
|                     return n.active !== state && !RED.nodes.subflow(n.z) | ||||
|                 }) | ||||
|                 return nodes; | ||||
|             } | ||||
|  | ||||
|             function setDebugNodeState(nodes,state) { | ||||
|                 var historyEvents = []; | ||||
|                 if (nodes.length > 0) { | ||||
|                     activateAjaxCall(nodes,false, function(resp, textStatus, xhr) { | ||||
|                         nodes.forEach(function(n) { | ||||
|                             historyEvents.push({ | ||||
|                                 t: "edit", | ||||
|                                 node: n, | ||||
|                                 changed: n.changed, | ||||
|                                 changes: { | ||||
|                                     active: n.active | ||||
|                                 } | ||||
|                             }); | ||||
|                             n.active = state; | ||||
|                             n.changed = true; | ||||
|                             n.dirty = true; | ||||
|                         }) | ||||
|                         RED.history.push({ | ||||
|                             t: "multi", | ||||
|                             events: historyEvents, | ||||
|                             dirty: RED.nodes.dirty(), | ||||
|                             callback: function() { | ||||
|                                 activateAjaxCall(nodes,nodes[0].active); | ||||
|                             } | ||||
|                         }); | ||||
|                         RED.nodes.dirty(true); | ||||
|                         RED.view.redraw(); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $("#red-ui-sidebar-debug-open").on("click", function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600"); | ||||
| @@ -308,10 +407,20 @@ | ||||
|             window.removeEventListener("message",this.handleWindowMessage); | ||||
|             RED.actions.remove("core:show-debug-tab"); | ||||
|             RED.actions.remove("core:clear-debug-messages"); | ||||
|  | ||||
|             delete RED._debug; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var autoType = { | ||||
|                 value: "auto", | ||||
|                 label: RED._("node-red:debug.autostatus"), | ||||
|                 hasValue: false | ||||
|             }; | ||||
|             $("#node-input-typed-status").typedInput({ | ||||
|                 default: "auto", | ||||
|                 types:[autoType, "msg", "jsonata"], | ||||
|                 typeField: $("#node-input-statusType") | ||||
|             }); | ||||
|             var that = this; | ||||
|             var none = { | ||||
|                 value: "none", | ||||
|                 label: RED._("node-red:debug.none"), | ||||
| @@ -321,6 +430,14 @@ | ||||
|                 this.tosidebar = true; | ||||
|                 $("#node-input-tosidebar").prop('checked', true); | ||||
|             } | ||||
|             if (this.statusVal === undefined) { | ||||
|                 this.statusVal = (this.complete === "false") ? "payload" : ((this.complete === "true") ? "payload" : this.complete+""); | ||||
|                 $("#node-input-typed-status").typedInput('value',this.statusVal || ""); | ||||
|             } | ||||
|             if (this.statusType === undefined) { | ||||
|                 this.statusType = this.targetType; | ||||
|                 $("#node-input-typed-status").typedInput('type',this.statusType || "auto"); | ||||
|             } | ||||
|             if (typeof this.console === "string") { | ||||
|                 this.console = (this.console == 'true'); | ||||
|                 $("#node-input-console").prop('checked', this.console); | ||||
| @@ -331,6 +448,7 @@ | ||||
|                 label: RED._("node-red:debug.msgobj"), | ||||
|                 hasValue: false | ||||
|             }; | ||||
|  | ||||
|             $("#node-input-typed-complete").typedInput({ | ||||
|                 default: "msg", | ||||
|                 types:['msg', fullType, "jsonata"], | ||||
| @@ -354,17 +472,29 @@ | ||||
|                 ) { | ||||
|                     $("#node-input-typed-complete").typedInput('value','payload'); | ||||
|                 } | ||||
|                 if ($("#node-input-typed-complete").typedInput('type') === 'full') { | ||||
|                     $("#node-tostatus-line").hide(); | ||||
|                 } else { | ||||
|             }); | ||||
|  | ||||
|             $("#node-input-tostatus").on('change',function() { | ||||
|                 if ($(this).is(":checked")) { | ||||
|                     if (!that.hasOwnProperty("statusVal") || that.statusVal === "") { | ||||
|                         var type = $("#node-input-typed-complete").typedInput('type'); | ||||
|                         var comp = "payload"; | ||||
|                         if (type !== 'full') { | ||||
|                             comp = $("#node-input-typed-complete").typedInput('value'); | ||||
|                         } | ||||
|                         that.statusType = "auto"; | ||||
|                         that.statusVal = comp; | ||||
|                     } | ||||
|                     $("#node-input-typed-status").typedInput('type',that.statusType); | ||||
|                     $("#node-input-typed-status").typedInput('value',that.statusVal); | ||||
|                     $("#node-tostatus-line").show(); | ||||
|                 } | ||||
|             }); | ||||
|             $("#node-input-complete").on('change',function() { | ||||
|                 if ($("#node-input-typed-complete").typedInput('type') === 'full') { | ||||
|                 else { | ||||
|                     $("#node-tostatus-line").hide(); | ||||
|                 } else { | ||||
|                     $("#node-tostatus-line").show(); | ||||
|                     that.statusType = "auto"; | ||||
|                     that.statusVal = ""; | ||||
|                     $("#node-input-typed-status").typedInput('type',that.statusType); | ||||
|                     $("#node-input-typed-status").typedInput('value',that.statusVal); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
| @@ -375,6 +505,7 @@ | ||||
|             } else { | ||||
|                 $("#node-input-complete").val($("#node-input-typed-complete").typedInput('value')); | ||||
|             } | ||||
|             $("#node-input-statusVal").val($("#node-input-typed-status").typedInput('value')); | ||||
|         } | ||||
|     }); | ||||
| })(); | ||||
|   | ||||
| @@ -2,7 +2,7 @@ module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var util = require("util"); | ||||
|     var events = require("events"); | ||||
|     var path = require("path"); | ||||
|     //var path = require("path"); | ||||
|     var debuglength = RED.settings.debugMaxLength || 1000; | ||||
|     var useColors = RED.settings.debugUseColors || false; | ||||
|     util.inspect.styles.boolean = "red"; | ||||
| @@ -15,36 +15,20 @@ module.exports = function(RED) { | ||||
|         this.complete = hasEditExpression ? null : (n.complete||"payload").toString(); | ||||
|         if (this.complete === "false") { this.complete = "payload"; } | ||||
|         this.console = ""+(n.console || false); | ||||
|         this.tostatus = (this.complete !== "true") && (n.tostatus || false); | ||||
|         this.tostatus = n.tostatus || false; | ||||
|         this.statusType = n.statusType || "msg"; | ||||
|         this.statusVal = n.statusVal || this.complete; | ||||
|         this.tosidebar = n.tosidebar; | ||||
|         if (this.tosidebar === undefined) { this.tosidebar = true; } | ||||
|         this.severity = n.severity || 40; | ||||
|         this.active = (n.active === null || typeof n.active === "undefined") || n.active; | ||||
|         if (this.tostatus) { this.status({fill:"grey", shape:"ring"}); } | ||||
|         else { this.status({}); } | ||||
|         var hasStatExpression = (n.statusType === "jsonata"); | ||||
|         var statExpression = hasStatExpression ? n.statusVal : null; | ||||
|  | ||||
|         var node = this; | ||||
|         var levels = { | ||||
|             off: 1, | ||||
|             fatal: 10, | ||||
|             error: 20, | ||||
|             warn: 30, | ||||
|             info: 40, | ||||
|             debug: 50, | ||||
|             trace: 60, | ||||
|             audit: 98, | ||||
|             metric: 99 | ||||
|         }; | ||||
|         var colors = { | ||||
|             "0": "grey", | ||||
|             "10": "grey", | ||||
|             "20": "red", | ||||
|             "30": "yellow", | ||||
|             "40": "grey", | ||||
|             "50": "green", | ||||
|             "60": "blue" | ||||
|         }; | ||||
|         var preparedEditExpression = null; | ||||
|         var preparedStatExpression = null; | ||||
|         if (editExpression) { | ||||
|             try { | ||||
|                 preparedEditExpression = RED.util.prepareJSONataExpression(editExpression, this); | ||||
| @@ -54,16 +38,22 @@ module.exports = function(RED) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         if (statExpression) { | ||||
|             try { | ||||
|                 preparedStatExpression = RED.util.prepareJSONataExpression(statExpression, this); | ||||
|             } | ||||
|             catch (e) { | ||||
|                 node.error(RED._("debug.invalid-exp", {error: editExpression})); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function prepareValue(msg, done) { | ||||
|             // Either apply the jsonata expression or... | ||||
|             if (preparedEditExpression) { | ||||
|                 RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => { | ||||
|                     if (err) { | ||||
|                         done(RED._("debug.invalid-exp", {error: editExpression})); | ||||
|                     } else { | ||||
|                         done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); | ||||
|                     } | ||||
|                     if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } | ||||
|                     else { done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); } | ||||
|                 }); | ||||
|             } else { | ||||
|                 // Extract the required message property | ||||
| @@ -71,17 +61,67 @@ module.exports = function(RED) { | ||||
|                 var output = msg[property]; | ||||
|                 if (node.complete !== "false" && typeof node.complete !== "undefined") { | ||||
|                     property = node.complete; | ||||
|                     try { | ||||
|                         output = RED.util.getMessageProperty(msg,node.complete); | ||||
|                     } catch(err) { | ||||
|                         output = undefined; | ||||
|                     } | ||||
|                     try { output = RED.util.getMessageProperty(msg,node.complete); } | ||||
|                     catch(err) { output = undefined; } | ||||
|                 } | ||||
|                 done(null,{id:node.id, z:node.z, _alias: node._alias,  path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output}); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function prepareStatus(msg, done) { | ||||
|             if (node.statusType === "auto") { | ||||
|                 if (node.complete === "true") { | ||||
|                     done(null,{msg:msg.payload}); | ||||
|                 } | ||||
|                 else { | ||||
|                     prepareValue(msg,function(err,debugMsg) { | ||||
|                         if (err) { node.error(err); return; } | ||||
|                         done(null,{msg:debugMsg.msg}); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 // Either apply the jsonata expression or... | ||||
|                 if (preparedStatExpression) { | ||||
|                     RED.util.evaluateJSONataExpression(preparedStatExpression, msg, (err, value) => { | ||||
|                         if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); } | ||||
|                         else { done(null,{msg:value}); } | ||||
|                     }); | ||||
|                 } | ||||
|                 else { | ||||
|                     // Extract the required message property | ||||
|                     var output; | ||||
|                     try { output = RED.util.getMessageProperty(msg,node.statusVal); } | ||||
|                     catch(err) { output = undefined; } | ||||
|                     done(null,{msg:output}); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.on("input", function(msg, send, done) { | ||||
|             if (node.tostatus === true) { | ||||
|                 prepareStatus(msg, function(err,debugMsg) { | ||||
|                     if (err) { node.error(err); return; } | ||||
|                     var output = debugMsg.msg; | ||||
|                     var st = (typeof output === 'string') ? output : util.inspect(output); | ||||
|                     var fill = "grey"; | ||||
|                     var shape = "dot"; | ||||
|                     if (node.statusType === "auto") { | ||||
|                         if (msg.hasOwnProperty("error")) { | ||||
|                             fill = "red"; | ||||
|                             st = msg.error.message; | ||||
|                         } | ||||
|                         if (msg.hasOwnProperty("status")) { | ||||
|                             if (msg.status.hasOwnProperty("fill")) { fill = msg.status.fill; } | ||||
|                             if (msg.status.hasOwnProperty("shape")) { shape = msg.status.shape; } | ||||
|                             if (msg.status.hasOwnProperty("text")) { st = msg.status.text; } | ||||
|                         } | ||||
|                     } | ||||
|                     if (st.length > 32) { st = st.substr(0,32) + "..."; } | ||||
|                     node.status({fill:fill, shape:shape, text:st}); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             if (this.complete === "true") { | ||||
|                 // debug complete msg object | ||||
|                 if (this.console === "true") { | ||||
| @@ -91,7 +131,8 @@ module.exports = function(RED) { | ||||
|                     sendDebug({id:node.id, z:node.z, _alias: node._alias,  path:node._flow.path, name:node.name, topic:msg.topic, msg:msg}); | ||||
|                 } | ||||
|                 done(); | ||||
|             } else { | ||||
|             } | ||||
|             else { | ||||
|                 prepareValue(msg,function(err,debugMsg) { | ||||
|                     if (err) { | ||||
|                         node.error(err); | ||||
| @@ -107,12 +148,6 @@ module.exports = function(RED) { | ||||
|                             node.log(util.inspect(output, {colors:useColors})); | ||||
|                         } | ||||
|                     } | ||||
|                     if (node.tostatus === true) { | ||||
|                         var st = (typeof output === 'string')?output:util.inspect(output); | ||||
|                         var severity = node.severity; | ||||
|                         if (st.length > 32) { st = st.substr(0,32) + "..."; } | ||||
|                         node.status({fill:colors[severity], shape:"dot", text:st}); | ||||
|                     } | ||||
|                     if (node.active) { | ||||
|                         if (node.tosidebar == true) { | ||||
|                             sendDebug(debugMsg); | ||||
| @@ -150,24 +185,49 @@ module.exports = function(RED) { | ||||
|     }); | ||||
|     RED.log.addHandler(DebugNode.logHandler); | ||||
|  | ||||
|     RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { | ||||
|         var node = RED.nodes.getNode(req.params.id); | ||||
|         var state = req.params.state; | ||||
|         if (node !== null && typeof node !== "undefined" ) { | ||||
|             if (state === "enable") { | ||||
|                 node.active = true; | ||||
|                 res.sendStatus(200); | ||||
|                 if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); } | ||||
|             } else if (state === "disable") { | ||||
|                 node.active = false; | ||||
|                 res.sendStatus(201); | ||||
|                 if (node.tostatus && node.hasOwnProperty("oldStatus")) { | ||||
|                     node.oldStatus.shape = "dot"; | ||||
|                     node.status(node.oldStatus); | ||||
|                 } | ||||
|             } else { | ||||
|                 res.sendStatus(404); | ||||
|     function setNodeState(node,state) { | ||||
|         if (state) { | ||||
|             node.active = true; | ||||
|             if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); } | ||||
|         } else { | ||||
|             node.active = false; | ||||
|             if (node.tostatus && node.hasOwnProperty("oldStatus")) { | ||||
|                 node.oldStatus.shape = "dot"; | ||||
|                 node.status(node.oldStatus); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     RED.httpAdmin.post("/debug/:state", RED.auth.needsPermission("debug.write"), function(req,res) { | ||||
|         var state = req.params.state; | ||||
|         if (state !== 'enable' && state !== 'disable') { | ||||
|             res.sendStatus(404); | ||||
|             return; | ||||
|         } | ||||
|         var nodes = req.body && req.body.nodes; | ||||
|         if (Array.isArray(nodes)) { | ||||
|             nodes.forEach(function(id) { | ||||
|                 var node = RED.nodes.getNode(id); | ||||
|                 if (node !== null && typeof node !== "undefined" ) { | ||||
|                     setNodeState(node, state === "enable"); | ||||
|                 } | ||||
|             }) | ||||
|             res.sendStatus(state === "enable" ? 200 : 201); | ||||
|         } else { | ||||
|             res.sendStatus(400); | ||||
|         } | ||||
|     }) | ||||
|  | ||||
|     RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { | ||||
|         var state = req.params.state; | ||||
|         if (state !== 'enable' && state !== 'disable') { | ||||
|             res.sendStatus(404); | ||||
|             return; | ||||
|         } | ||||
|         var node = RED.nodes.getNode(req.params.id); | ||||
|         if (node !== null && typeof node !== "undefined" ) { | ||||
|             setNodeState(node,state === "enable"); | ||||
|             res.sendStatus(state === "enable" ? 200 : 201); | ||||
|         } else { | ||||
|             res.sendStatus(404); | ||||
|         } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <script type="text/x-red" data-template-name="complete"> | ||||
| <script type="text/html" data-template-name="complete"> | ||||
|     <div class="form-row node-input-target-row"> | ||||
|         <button id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button> | ||||
|     </div> | ||||
| @@ -89,7 +89,8 @@ | ||||
|                     node: n, | ||||
|                     label: label, | ||||
|                     sublabel: sublabel, | ||||
|                     selected: isChecked | ||||
|                     selected: isChecked, | ||||
|                     checkbox: true | ||||
|                 }; | ||||
|                 items.push(nodeItemMap[n.id]); | ||||
|             }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="catch"> | ||||
| <script type="text/html" data-template-name="catch"> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label> | ||||
|         <select id="node-input-scope-select"> | ||||
| @@ -104,7 +104,8 @@ | ||||
|                     node: n, | ||||
|                     label: label, | ||||
|                     sublabel: sublabel, | ||||
|                     selected: isChecked | ||||
|                     selected: isChecked, | ||||
|                     checkbox: true | ||||
|                 }; | ||||
|                 items.push(nodeItemMap[n.id]); | ||||
|             }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="status"> | ||||
| <script type="text/html" data-template-name="status"> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label> | ||||
|         <select id="node-input-scope-select"> | ||||
| @@ -92,7 +92,8 @@ | ||||
|                     node: n, | ||||
|                     label: label, | ||||
|                     sublabel: sublabel, | ||||
|                     selected: isChecked | ||||
|                     selected: isChecked, | ||||
|                     checkbox: true | ||||
|                 }; | ||||
|                 items.push(nodeItemMap[n.id]); | ||||
|             }); | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="link in"> | ||||
| <script type="text/html" data-template-name="link in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|     <div class="form-row node-input-link-row"></div> | ||||
| </script> | ||||
| <script type="text/x-red" data-template-name="link out"> | ||||
| <script type="text/html" data-template-name="link out"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
| @@ -83,7 +83,8 @@ | ||||
|                     id: n.id, | ||||
|                     node: n, | ||||
|                     label: n.name||n.id, | ||||
|                     selected: isChecked | ||||
|                     selected: isChecked, | ||||
|                     checkbox: true | ||||
|                 }) | ||||
|             } | ||||
|         }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="comment"> | ||||
| <script type="text/html" data-template-name="comment"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="unknown"> | ||||
| <script type="text/html" data-template-name="unknown"> | ||||
|     <div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div> | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -1,22 +1,57 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="function"> | ||||
| <script type="text/html" data-template-name="function"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div> | ||||
|     </div> | ||||
|     <div class="form-row" style="margin-bottom: 0px;"> | ||||
|         <label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="function.label.function"></span></label> | ||||
|         <input type="hidden" id="node-input-func" autofocus="autofocus"> | ||||
|         <input type="hidden" id="node-input-noerr"> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul> | ||||
|     </div> | ||||
|     <div class="form-row node-text-editor-row" style="position:relative"> | ||||
|         <div style="position: absolute; right:0; bottom:calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> | ||||
|         <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div> | ||||
|     </div> | ||||
|     <div class="form-row" style="margin-bottom: 0px"> | ||||
|         <label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label> | ||||
|         <input id="node-input-outputs" style="width: 60px;" value="1"> | ||||
|  | ||||
|     <div id="func-tabs-content" style="min-height: calc(100% - 95px);"> | ||||
|  | ||||
|         <div id="func-tab-init" style="display:none"> | ||||
|             <div class="form-row" style="margin-bottom: 0px;"> | ||||
|                 <input type="hidden" id="node-input-initialize" autofocus="autofocus"> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row node-text-editor-row" style="position:relative"> | ||||
|                 <div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> | ||||
|                 <div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-init-editor" ></div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div id="func-tab-body" style="display:none"> | ||||
|             <div class="form-row" style="margin-bottom: 0px;"> | ||||
|                 <input type="hidden" id="node-input-func" autofocus="autofocus"> | ||||
|                 <input type="hidden" id="node-input-noerr"> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row node-text-editor-row" style="position:relative"> | ||||
|                 <div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> | ||||
|                 <div style="height: 220px; min-height:120px; margin-top: 30px;" class="node-text-editor" id="node-input-func-editor" ></div> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row" style="margin-bottom: 0px"> | ||||
|                 <label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label> | ||||
|                 <input id="node-input-outputs" style="width: 60px;" value="1"> | ||||
|             </div> | ||||
|  | ||||
|         </div> | ||||
|  | ||||
|         <div id="func-tab-finalize" style="display:none"> | ||||
|             <div class="form-row" style="margin-bottom: 0px;"> | ||||
|                 <input type="hidden" id="node-input-finalize" autofocus="autofocus"> | ||||
|             </div> | ||||
|             <div class="form-row node-text-editor-row" style="position:relative"> | ||||
|                 <div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> | ||||
|                 <div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-finalize-editor" ></div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|     </div> | ||||
|  | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
| @@ -27,7 +62,9 @@ | ||||
|             name: {value:""}, | ||||
|             func: {value:"\nreturn msg;"}, | ||||
|             outputs: {value:1}, | ||||
|             noerr: {value:0,required:true,validate:function(v) { return !v; }} | ||||
|             noerr: {value:0,required:true,validate:function(v) { return !v; }}, | ||||
|             initialize: {value:""}, | ||||
|             finalize: {value:""} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:1, | ||||
| @@ -40,6 +77,28 @@ | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var that = this; | ||||
|  | ||||
|             var tabs = RED.tabs.create({ | ||||
|                 id: "func-tabs", | ||||
|                 onchange: function(tab) { | ||||
|                     $("#func-tabs-content").children().hide(); | ||||
|                     $("#" + tab.id).show(); | ||||
|                 } | ||||
|             }); | ||||
|             tabs.addTab({ | ||||
|                 id: "func-tab-init", | ||||
|                 label: that._("function.label.initialize") | ||||
|             }); | ||||
|             tabs.addTab({ | ||||
|                 id: "func-tab-body", | ||||
|                 label: that._("function.label.function") | ||||
|             }); | ||||
|             tabs.addTab({ | ||||
|                 id: "func-tab-finalize", | ||||
|                 label: that._("function.label.finalize") | ||||
|             }); | ||||
|             tabs.activateTab("func-tab-body"); | ||||
|  | ||||
|             $( "#node-input-outputs" ).spinner({ | ||||
|                 min:0, | ||||
|                 change: function(event, ui) { | ||||
| @@ -50,74 +109,145 @@ | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this.editor = RED.editor.createEditor({ | ||||
|                 id: 'node-input-func-editor', | ||||
|                 mode: 'ace/mode/nrjavascript', | ||||
|                 value: $("#node-input-func").val(), | ||||
|                 globals: { | ||||
|                     msg:true, | ||||
|                     context:true, | ||||
|                     RED: true, | ||||
|                     util: true, | ||||
|                     flow: true, | ||||
|                     global: true, | ||||
|                     console: true, | ||||
|                     Buffer: true, | ||||
|                     setTimeout: true, | ||||
|                     clearTimeout: true, | ||||
|                     setInterval: true, | ||||
|                     clearInterval: true | ||||
|             var buildEditor = function(id, value, defaultValue) { | ||||
|                 var editor = RED.editor.createEditor({ | ||||
|                     id: id, | ||||
|                     mode: 'ace/mode/nrjavascript', | ||||
|                     value: value || defaultValue || "", | ||||
|                     globals: { | ||||
|                         msg:true, | ||||
|                         context:true, | ||||
|                         RED: true, | ||||
|                         util: true, | ||||
|                         flow: true, | ||||
|                         global: true, | ||||
|                         console: true, | ||||
|                         Buffer: true, | ||||
|                         setTimeout: true, | ||||
|                         clearTimeout: true, | ||||
|                         setInterval: true, | ||||
|                         clearInterval: true | ||||
|                     } | ||||
|                 }); | ||||
|                 if (defaultValue && value === "") { | ||||
|                     editor.moveCursorTo(defaultValue.split("\n").length - 1, 0); | ||||
|                 } | ||||
|             }); | ||||
|                 return editor; | ||||
|             } | ||||
|             this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize")) | ||||
|             this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val()) | ||||
|             this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize")) | ||||
|  | ||||
|             RED.library.create({ | ||||
|                 url:"functions", // where to get the data from | ||||
|                 type:"function", // the type of object the library is for | ||||
|                 editor:this.editor, // the field name the main text body goes to | ||||
|                 mode:"ace/mode/nrjavascript", | ||||
|                 fields:['name','outputs'], | ||||
|                 fields:[ | ||||
|                     'name', 'outputs', | ||||
|                     { | ||||
|                         name: 'initialize', | ||||
|                         get: function() { | ||||
|                             return that.initEditor.getValue(); | ||||
|                         }, | ||||
|                         set: function(v) { | ||||
|                             that.initEditor.setValue(v||RED._("node-red:function.text.initialize"), -1); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'finalize', | ||||
|                         get: function() { | ||||
|                             return that.finalizeEditor.getValue(); | ||||
|                         }, | ||||
|                         set: function(v) { | ||||
|                             that.finalizeEditor.setValue(v||RED._("node-red:function.text.finalize"), -1); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'info', | ||||
|                         get: function() { | ||||
|                             return that.infoEditor.getValue(); | ||||
|                         }, | ||||
|                         set: function(v) { | ||||
|                             that.infoEditor.setValue(v||"", -1); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 ext:"js" | ||||
|             }); | ||||
|             this.editor.focus(); | ||||
|  | ||||
|             RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand")); | ||||
|  | ||||
|             $("#node-function-expand-js").on("click", function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 var value = that.editor.getValue(); | ||||
|                 RED.editor.editJavaScript({ | ||||
|                     value: value, | ||||
|                     width: "Infinity", | ||||
|                     cursor: that.editor.getCursorPosition(), | ||||
|                     mode: "ace/mode/nrjavascript", | ||||
|                     complete: function(v,cursor) { | ||||
|                         that.editor.setValue(v, -1); | ||||
|                         that.editor.gotoLine(cursor.row+1,cursor.column,false); | ||||
|                         setTimeout(function() { | ||||
|                             that.editor.focus(); | ||||
|                         },300); | ||||
|                     } | ||||
|                 }) | ||||
|             }) | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             var annot = this.editor.getSession().getAnnotations(); | ||||
|             this.noerr = 0; | ||||
|             $("#node-input-noerr").val(0); | ||||
|             for (var k=0; k < annot.length; k++) { | ||||
|                 //console.log(annot[k].type,":",annot[k].text, "on line", annot[k].row); | ||||
|                 if (annot[k].type === "error") { | ||||
|                     $("#node-input-noerr").val(annot.length); | ||||
|                     this.noerr = annot.length; | ||||
|             var expandButtonClickHandler = function(editor) { | ||||
|                 return function(e) { | ||||
|                     e.preventDefault(); | ||||
|                     var value = editor.getValue(); | ||||
|                     RED.editor.editJavaScript({ | ||||
|                         value: value, | ||||
|                         width: "Infinity", | ||||
|                         cursor: editor.getCursorPosition(), | ||||
|                         mode: "ace/mode/nrjavascript", | ||||
|                         complete: function(v,cursor) { | ||||
|                             editor.setValue(v, -1); | ||||
|                             editor.gotoLine(cursor.row+1,cursor.column,false); | ||||
|                             setTimeout(function() { | ||||
|                                 editor.focus(); | ||||
|                             },300); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|             $("#node-input-func").val(this.editor.getValue()); | ||||
|             this.editor.destroy(); | ||||
|             delete this.editor; | ||||
|             $("#node-init-expand-js").on("click", expandButtonClickHandler(this.initEditor)); | ||||
|             $("#node-function-expand-js").on("click", expandButtonClickHandler(this.editor)); | ||||
|             $("#node-finalize-expand-js").on("click", expandButtonClickHandler(this.finalizeEditor)); | ||||
|  | ||||
|             RED.popover.tooltip($("#node-init-expand-js"), RED._("node-red:common.label.expand")); | ||||
|             RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand")); | ||||
|             RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand")); | ||||
|  | ||||
|  | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             var node = this; | ||||
|             var noerr = 0; | ||||
|             $("#node-input-noerr").val(0); | ||||
|  | ||||
|             var disposeEditor = function(editorName,targetName,defaultValue) { | ||||
|                 var editor = node[editorName]; | ||||
|                 var annot = editor.getSession().getAnnotations(); | ||||
|                 for (var k=0; k < annot.length; k++) { | ||||
|                     if (annot[k].type === "error") { | ||||
|                         noerr += annot.length; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 var val = editor.getValue(); | ||||
|                 if (defaultValue && val == defaultValue) { | ||||
|                     val = ""; | ||||
|                 } | ||||
|                 editor.destroy(); | ||||
|                 delete node[editorName]; | ||||
|                 $("#"+targetName).val(val); | ||||
|             } | ||||
|             disposeEditor("editor","node-input-func"); | ||||
|             disposeEditor("initEditor","node-input-initialize", RED._("node-red:function.text.initialize")); | ||||
|             disposeEditor("finalizeEditor","node-input-finalize", RED._("node-red:function.text.finalize")); | ||||
|  | ||||
|             $("#node-input-noerr").val(noerr); | ||||
|             this.noerr = noerr; | ||||
|  | ||||
|         }, | ||||
|         oneditcancel: function() { | ||||
|             this.editor.destroy(); | ||||
|             delete this.editor; | ||||
|             var node = this; | ||||
|  | ||||
|             node.editor.destroy(); | ||||
|             delete node.editor; | ||||
|  | ||||
|             node.initEditor.destroy(); | ||||
|             delete node.initEditor; | ||||
|  | ||||
|             node.finalizeEditor.destroy(); | ||||
|             delete node.finalizeEditor; | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|             var rows = $("#dialog-form>div:not(.node-text-editor-row)"); | ||||
| @@ -129,6 +259,16 @@ | ||||
|             height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); | ||||
|             $(".node-text-editor").css("height",height+"px"); | ||||
|             this.editor.resize(); | ||||
|  | ||||
|             var height = size.height; | ||||
|             $("#node-input-init-editor").css("height", (height -105)+"px"); | ||||
|             $("#node-input-func-editor").css("height", (height -145)+"px"); | ||||
|             $("#node-input-finalize-editor").css("height", (height -105)+"px"); | ||||
|  | ||||
|             this.initEditor.resize(); | ||||
|             this.editor.resize(); | ||||
|             this.finalizeEditor.resize(); | ||||
|  | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -57,22 +57,55 @@ module.exports = function(RED) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function createVMOpt(node, kind) { | ||||
|         var opt = { | ||||
|             filename: 'Function node'+kind+':'+node.id+(node.name?' ['+node.name+']':''), // filename for stack traces | ||||
|             displayErrors: true | ||||
|             // Using the following options causes node 4/6 to not include the line number | ||||
|             // in the stack output. So don't use them. | ||||
|             // lineOffset: -11, // line number offset to be used for stack traces | ||||
|             // columnOffset: 0, // column number offset to be used for stack traces | ||||
|         }; | ||||
|         return opt; | ||||
|     } | ||||
|  | ||||
|     function updateErrorInfo(err) { | ||||
|         if (err.stack) { | ||||
|             var stack = err.stack.toString(); | ||||
|             var m = /^([^:]+):([^:]+):(\d+).*/.exec(stack); | ||||
|             if (m) { | ||||
|                 var line = parseInt(m[3]) -1; | ||||
|                 var kind = "body:"; | ||||
|                 if (/setup/.exec(m[1])) { | ||||
|                     kind = "setup:"; | ||||
|                 } | ||||
|                 if (/cleanup/.exec(m[1])) { | ||||
|                     kind = "cleanup:"; | ||||
|                 } | ||||
|                 err.message += " ("+kind+"line "+line+")"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function FunctionNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         var node = this; | ||||
|         this.name = n.name; | ||||
|         this.func = n.func; | ||||
|         node.name = n.name; | ||||
|         node.func = n.func; | ||||
|         node.ini = n.initialize ? n.initialize : ""; | ||||
|         node.fin = n.finalize ? n.finalize : ""; | ||||
|  | ||||
|         var handleNodeDoneCall = true; | ||||
|  | ||||
|         // Check to see if the Function appears to call `node.done()`. If so, | ||||
|         // we will assume it is well written and does actually call node.done(). | ||||
|         // Otherwise, we will call node.done() after the function returns regardless. | ||||
|         if (/node\.done\s*\(\s*\)/.test(this.func)) { | ||||
|         if (/node\.done\s*\(\s*\)/.test(node.func)) { | ||||
|             handleNodeDoneCall = false; | ||||
|         } | ||||
|  | ||||
|         var functionText = "var results = null;"+ | ||||
|                            "results = (function(msg,__send__,__done__){ "+ | ||||
|                            "results = (async function(msg,__send__,__done__){ "+ | ||||
|                               "var __msgid__ = msg._msgid;"+ | ||||
|                               "var node = {"+ | ||||
|                                  "id:__node__.id,"+ | ||||
| @@ -87,11 +120,13 @@ module.exports = function(RED) { | ||||
|                                  "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ | ||||
|                                  "done:__done__"+ | ||||
|                               "};\n"+ | ||||
|                               this.func+"\n"+ | ||||
|                               node.func+"\n"+ | ||||
|                            "})(msg,send,done);"; | ||||
|         this.topic = n.topic; | ||||
|         this.outstandingTimers = []; | ||||
|         this.outstandingIntervals = []; | ||||
|         var finScript = null; | ||||
|         var finOpt = null; | ||||
|         node.topic = n.topic; | ||||
|         node.outstandingTimers = []; | ||||
|         node.outstandingIntervals = []; | ||||
|         var sandbox = { | ||||
|             console:console, | ||||
|             util:util, | ||||
| @@ -182,12 +217,12 @@ module.exports = function(RED) { | ||||
|                 arguments[0] = function() { | ||||
|                     sandbox.clearTimeout(timerId); | ||||
|                     try { | ||||
|                         func.apply(this,arguments); | ||||
|                         func.apply(node,arguments); | ||||
|                     } catch(err) { | ||||
|                         node.error(err,{}); | ||||
|                     } | ||||
|                 }; | ||||
|                 timerId = setTimeout.apply(this,arguments); | ||||
|                 timerId = setTimeout.apply(node,arguments); | ||||
|                 node.outstandingTimers.push(timerId); | ||||
|                 return timerId; | ||||
|             }, | ||||
| @@ -203,12 +238,12 @@ module.exports = function(RED) { | ||||
|                 var timerId; | ||||
|                 arguments[0] = function() { | ||||
|                     try { | ||||
|                         func.apply(this,arguments); | ||||
|                         func.apply(node,arguments); | ||||
|                     } catch(err) { | ||||
|                         node.error(err,{}); | ||||
|                     } | ||||
|                 }; | ||||
|                 timerId = setInterval.apply(this,arguments); | ||||
|                 timerId = setInterval.apply(node,arguments); | ||||
|                 node.outstandingIntervals.push(timerId); | ||||
|                 return timerId; | ||||
|             }, | ||||
| @@ -226,37 +261,48 @@ module.exports = function(RED) { | ||||
|                     sandbox.setTimeout(function(){ resolve(value); }, after); | ||||
|                 }); | ||||
|             }; | ||||
|             sandbox.promisify = util.promisify; | ||||
|         } | ||||
|         var context = vm.createContext(sandbox); | ||||
|         try { | ||||
|             this.script = vm.createScript(functionText, { | ||||
|                 filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces | ||||
|                 displayErrors: true | ||||
|                 // Using the following options causes node 4/6 to not include the line number | ||||
|                 // in the stack output. So don't use them. | ||||
|                 // lineOffset: -11, // line number offset to be used for stack traces | ||||
|                 // columnOffset: 0, // column number offset to be used for stack traces | ||||
|             }); | ||||
|             this.on("input", function(msg,send,done) { | ||||
|                 try { | ||||
|                     var start = process.hrtime(); | ||||
|                     context.msg = msg; | ||||
|                     context.send = send; | ||||
|                     context.done = done; | ||||
|             var iniScript = null; | ||||
|             var iniOpt = null; | ||||
|             if (node.ini && (node.ini !== "")) { | ||||
|                 var iniText = "(async function () {\n"+node.ini +"\n})();"; | ||||
|                 iniOpt = createVMOpt(node, " setup"); | ||||
|                 iniScript = new vm.Script(iniText, iniOpt); | ||||
|             } | ||||
|             node.script = vm.createScript(functionText, createVMOpt(node, "")); | ||||
|             if (node.fin && (node.fin !== "")) { | ||||
|                 var finText = "(function () {\n"+node.fin +"\n})();"; | ||||
|                 finOpt = createVMOpt(node, " cleanup"); | ||||
|                 finScript = new vm.Script(finText, finOpt); | ||||
|             } | ||||
|             var promise = Promise.resolve(); | ||||
|             if (iniScript) { | ||||
|                 promise = iniScript.runInContext(context, iniOpt); | ||||
|             } | ||||
|  | ||||
|                     this.script.runInContext(context); | ||||
|                     sendResults(this,send,msg._msgid,context.results,false); | ||||
|             function processMessage(msg, send, done) { | ||||
|                 var start = process.hrtime(); | ||||
|                 context.msg = msg; | ||||
|                 context.send = send; | ||||
|                 context.done = done; | ||||
|  | ||||
|                 node.script.runInContext(context); | ||||
|                 context.results.then(function(results) { | ||||
|                     sendResults(node,send,msg._msgid,results,false); | ||||
|                     if (handleNodeDoneCall) { | ||||
|                         done(); | ||||
|                     } | ||||
|  | ||||
|                     var duration = process.hrtime(start); | ||||
|                     var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100; | ||||
|                     this.metric("duration", msg, converted); | ||||
|                     node.metric("duration", msg, converted); | ||||
|                     if (process.env.NODE_RED_FUNCTION_TIME) { | ||||
|                         this.status({fill:"yellow",shape:"dot",text:""+converted}); | ||||
|                         node.status({fill:"yellow",shape:"dot",text:""+converted}); | ||||
|                     } | ||||
|                 } catch(err) { | ||||
|                 }).catch(err => { | ||||
|                     if ((typeof err === "object") && err.hasOwnProperty("stack")) { | ||||
|                         //remove unwanted part | ||||
|                         var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/); | ||||
| @@ -294,23 +340,67 @@ module.exports = function(RED) { | ||||
|                     else { | ||||
|                         done(JSON.stringify(err)); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             const RESOLVING = 0; | ||||
|             const RESOLVED = 1; | ||||
|             const ERROR = 2; | ||||
|             var state = RESOLVING; | ||||
|             var messages = []; | ||||
|  | ||||
|             node.on("input", function(msg,send,done) { | ||||
|                 if(state === RESOLVING) { | ||||
|                     messages.push({msg:msg, send:send, done:done}); | ||||
|                 } | ||||
|                 else if(state === RESOLVED) { | ||||
|                     processMessage(msg, send, done); | ||||
|                 } | ||||
|             }); | ||||
|             this.on("close", function() { | ||||
|             node.on("close", function() { | ||||
|                 if (finScript) { | ||||
|                     try { | ||||
|                         finScript.runInContext(context, finOpt); | ||||
|                     } | ||||
|                     catch (err) { | ||||
|                         node.error(err); | ||||
|                     } | ||||
|                 } | ||||
|                 while (node.outstandingTimers.length > 0) { | ||||
|                     clearTimeout(node.outstandingTimers.pop()); | ||||
|                 } | ||||
|                 while (node.outstandingIntervals.length > 0) { | ||||
|                     clearInterval(node.outstandingIntervals.pop()); | ||||
|                 } | ||||
|                 this.status({}); | ||||
|                 node.status({}); | ||||
|             }); | ||||
|         } catch(err) { | ||||
|  | ||||
|             promise.then(function (v) { | ||||
|                 var msgs = messages; | ||||
|                 messages = []; | ||||
|                 while (msgs.length > 0) { | ||||
|                     msgs.forEach(function (s) { | ||||
|                         processMessage(s.msg, s.send, s.done); | ||||
|                     }); | ||||
|                     msgs = messages; | ||||
|                     messages = []; | ||||
|                 } | ||||
|                 state = RESOLVED; | ||||
|             }).catch((error) => { | ||||
|                 messages = []; | ||||
|                 state = ERROR; | ||||
|                 node.error(error); | ||||
|             }); | ||||
|  | ||||
|         } | ||||
|         catch(err) { | ||||
|             // eg SyntaxError - which v8 doesn't include line number information | ||||
|             // so we can't do better than this | ||||
|             this.error(err); | ||||
|             updateErrorInfo(err); | ||||
|             node.error(err); | ||||
|         } | ||||
|     } | ||||
|     RED.nodes.registerType("function",FunctionNode); | ||||
|     RED.library.register("functions"); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="range"> | ||||
| <script type="text/html" data-template-name="range"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label> | ||||
|         <input type="text" id="node-input-property" style="width:calc(70% - 1px)"/> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="template"> | ||||
| <script type="text/html" data-template-name="template"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="delay"> | ||||
| <script type="text/html" data-template-name="delay"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-delay-action"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label> | ||||
|         <select id="node-input-delay-action" style="width:270px !important"> | ||||
|   | ||||
| @@ -158,6 +158,7 @@ module.exports = function(RED) { | ||||
|                         clearInterval(node.intervalID); | ||||
|                         node.intervalID = -1; | ||||
|                     } | ||||
|                     delete node.lastSent; | ||||
|                     node.buffer = []; | ||||
|                     node.status({text:"reset"}); | ||||
|                     return; | ||||
|   | ||||
| @@ -47,6 +47,10 @@ | ||||
|         <input type="hidden" id="node-input-op2type"> | ||||
|         <input style="width:70%" type="text" id="node-input-op2" placeholder="0"> | ||||
|     </div> | ||||
|     <div class="form-row" id="node-second-output"> | ||||
|         <label></label> | ||||
|             <input type="checkbox" id="node-input-second" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-second" data-i18n="trigger.second"></label> | ||||
|         </div> | ||||
|     <div class="form-row"> | ||||
|         <label data-i18n="trigger.label.reset" style="width:auto"></label> | ||||
|         <div style="display:inline-block; width:70%;vertical-align:top"> | ||||
| @@ -58,10 +62,13 @@ | ||||
|     <br/> | ||||
|     <div class="form-row"> | ||||
|         <label data-i18n="trigger.for" for="node-input-bytopic"></label> | ||||
|         <select id="node-input-bytopic"> | ||||
|         <select id="node-input-bytopic" style="width:120px;"> | ||||
|             <option value="all" data-i18n="trigger.alltopics"></option> | ||||
|             <option value="topic" data-i18n="trigger.bytopics"></option> | ||||
|         </select> | ||||
|         <span class="form-row" id="node-stream-topic"> | ||||
|             <input type="text" id="node-input-topic" style="width:46%;"/> | ||||
|         </span> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
| @@ -74,6 +81,7 @@ | ||||
|         category: 'function', | ||||
|         color:"#E6E0F8", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             op1: {value:"1", validate: RED.validators.typedInput("op1type")}, | ||||
|             op2: {value:"0", validate: RED.validators.typedInput("op2type")}, | ||||
|             op1type: {value:"val"}, | ||||
| @@ -82,8 +90,9 @@ | ||||
|             extend: {value:"false"}, | ||||
|             units: {value:"ms"}, | ||||
|             reset: {value:""}, | ||||
|             bytopic: {value: "all"}, | ||||
|             name: {value:""} | ||||
|             bytopic: {value:"all"}, | ||||
|             topic: {value:"topic",required:true}, | ||||
|             outputs: {value:1} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:1, | ||||
| @@ -103,19 +112,48 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var that = this; | ||||
|             if (this.topic === undefined) { $("#node-input-topic").val("topic"); } | ||||
|             $("#node-input-topic").typedInput({default:'msg',types:['msg']}); | ||||
|             $("#node-input-bytopic").on("change", function() { | ||||
|                 if ($("#node-input-bytopic").val() === "all") { | ||||
|                     $("#node-stream-topic").hide(); | ||||
|                 } else { | ||||
|                     $("#node-stream-topic").show(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (this.outputs == 2) { $("#node-input-second").prop('checked', true) } | ||||
|             else { $("#node-input-second").prop('checked', false) } | ||||
|          | ||||
|             $("#node-input-second").change(function() { | ||||
|                 if ($("#node-input-second").is(":checked")) { | ||||
|                     that.outputs = 2; | ||||
|                 } | ||||
|                 else  { | ||||
|                     that.outputs = 1; | ||||
|                 } | ||||
|             }); | ||||
|             $("#node-then-type").on("change", function() { | ||||
|                 if ($(this).val() == "block") { | ||||
|                     $(".node-type-wait").hide(); | ||||
|                     $(".node-type-duration").hide(); | ||||
|                     $("#node-second-output").hide(); | ||||
|                     $("#node-input-second").prop('checked', false); | ||||
|                     that.outputs = 1; | ||||
|                 } | ||||
|                 else if ($(this).val() == "loop") { | ||||
|                     if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); } | ||||
|                     $(".node-type-wait").hide(); | ||||
|                     $(".node-type-duration").show(); | ||||
|                     $("#node-second-output").hide(); | ||||
|                     $("#node-input-second").prop('checked', false); | ||||
|                     that.outputs = 1; | ||||
|                 } else { | ||||
|                     if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); } | ||||
|                     $(".node-type-wait").show(); | ||||
|                     $(".node-type-duration").show(); | ||||
|                     $("#node-second-output").show(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
| @@ -177,7 +215,7 @@ | ||||
|             } | ||||
|             if ($("#node-then-type").val() == "loop") { | ||||
|                 $("#node-input-duration").val($("#node-input-duration").val() * -1); | ||||
|             }     | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -24,6 +24,8 @@ module.exports = function(RED) { | ||||
|         this.op2 = n.op2 || "0"; | ||||
|         this.op1type = n.op1type || "str"; | ||||
|         this.op2type = n.op2type || "str"; | ||||
|         this.second = (n.outputs == 2) ? true : false; | ||||
|         this.topic = n.topic || "topic"; | ||||
|  | ||||
|         if (this.op1type === 'val') { | ||||
|             if (this.op1 === 'true' || this.op1 === 'false') { | ||||
| @@ -111,8 +113,15 @@ module.exports = function(RED) { | ||||
|             processMessageQueue(msg); | ||||
|         }); | ||||
|  | ||||
|         var stat = function() { | ||||
|             var l = Object.keys(node.topics).length; | ||||
|             if (l === 0) { return {} } | ||||
|             else if (l === 1) { return {fill:"blue",shape:"dot"} } | ||||
|             else return {fill:"blue",shape:"dot",text:l};  | ||||
|         } | ||||
|  | ||||
|         var processMessage = function(msg) { | ||||
|             var topic = msg.topic || "_none"; | ||||
|             var topic = RED.util.getMessageProperty(msg,node.topic) || "_none"; | ||||
|             var promise; | ||||
|             if (node.bytopic === "all") { topic = "_none"; } | ||||
|             node.topics[topic] = node.topics[topic] || {}; | ||||
| @@ -120,7 +129,7 @@ module.exports = function(RED) { | ||||
|                 if (node.loop === true) { clearInterval(node.topics[topic].tout); } | ||||
|                 else { clearTimeout(node.topics[topic].tout); } | ||||
|                 delete node.topics[topic]; | ||||
|                 node.status({}); | ||||
|                 node.status(stat()); | ||||
|             } | ||||
|             else { | ||||
|                 if (node.op2type === "payl") { npay[topic] = RED.util.cloneMessage(msg); } | ||||
| @@ -189,27 +198,29 @@ module.exports = function(RED) { | ||||
|                                             } | ||||
|                                             promise.then(() => { | ||||
|                                                 if (node.op2type === "payl") { | ||||
|                                                     node.send(npay[topic]);  | ||||
|                                                     if (node.second === true) { node.send([null,npay[topic]]); } | ||||
|                                                     else { node.send(npay[topic]); } | ||||
|                                                     delete npay[topic]; | ||||
|                                                 } | ||||
|                                                 else {  | ||||
|                                                 else { | ||||
|                                                     msg2.payload = node.topics[topic].m2; | ||||
|                                                     node.send(msg2);  | ||||
|                                                     if (node.second === true) { node.send([null,msg2]); } | ||||
|                                                     else { node.send(msg2); } | ||||
|                                                 } | ||||
|                                                 delete node.topics[topic]; | ||||
|                                                 node.status({}); | ||||
|                                                 node.status(stat()); | ||||
|                                             }).catch(err => { | ||||
|                                                 node.error(err); | ||||
|                                             }); | ||||
|                                         } else { | ||||
|                                             delete node.topics[topic]; | ||||
|                                             node.status({}); | ||||
|                                             node.status(stat()); | ||||
|                                         } | ||||
|  | ||||
|                                     }, node.duration); | ||||
|                                 } | ||||
|                             } | ||||
|                             node.status({fill:"blue",shape:"dot",text:" "}); | ||||
|                             node.status(stat()); | ||||
|                             if (node.op1type !== "nul") { node.send(RED.util.cloneMessage(msg)); } | ||||
|                         }); | ||||
|                     }); | ||||
| @@ -245,13 +256,17 @@ module.exports = function(RED) { | ||||
|                                 } | ||||
|                             } | ||||
|                             delete node.topics[topic]; | ||||
|                             node.status({}); | ||||
|                             node.send(msg2); | ||||
|                             node.status(stat()); | ||||
|                             if (node.second === true) { node.send([null,msg2]); } | ||||
|                             else { node.send(msg2); } | ||||
|                         }).catch(err => { | ||||
|                             node.error(err); | ||||
|                         }); | ||||
|                     }, node.duration); | ||||
|                 } | ||||
|                 // else { | ||||
|                 //     if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } | ||||
|                 // } | ||||
|             } | ||||
|             return Promise.resolve(); | ||||
|         } | ||||
| @@ -264,7 +279,7 @@ module.exports = function(RED) { | ||||
|                     delete node.topics[t]; | ||||
|                 } | ||||
|             } | ||||
|             node.status({}); | ||||
|             node.status(stat()); | ||||
|         }); | ||||
|     } | ||||
|     RED.nodes.registerType("trigger",TriggerNode); | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="exec"> | ||||
| <script type="text/html" data-template-name="exec"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-command"><i class="fa fa-file"></i> <span data-i18n="exec.label.command"></span></label> | ||||
|         <input type="text" id="node-input-command" data-i18n="[placeholder]exec.label.command"> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="tls-config"> | ||||
| <script type="text/html" data-template-name="tls-config"> | ||||
|     <div class="form-row" class="hide" id="node-config-row-uselocalfiles"> | ||||
|         <input type="checkbox" id="node-config-input-uselocalfiles" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-config-input-uselocalfiles" style="width: 70%;"><span data-i18n="tls.label.use-local-files"></label> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="http proxy"> | ||||
| <script type="text/html" data-template-name="http proxy"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-config-input-name"> | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="mqtt in"> | ||||
| <script type="text/html" data-template-name="mqtt in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label> | ||||
|         <input type="text" id="node-input-broker"> | ||||
| @@ -75,7 +75,7 @@ | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="mqtt out"> | ||||
| <script type="text/html" data-template-name="mqtt out"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label> | ||||
|         <input type="text" id="node-input-broker"> | ||||
| @@ -303,7 +303,7 @@ | ||||
|                 return this.name; | ||||
|             } | ||||
|             var b = this.broker; | ||||
|             if (b === "") { b = "undefined"; } | ||||
|             if (!b) { b = "undefined"; } | ||||
|             var lab = ""; | ||||
|             lab = (this.clientid?this.clientid+"@":"")+b; | ||||
|             if (b.indexOf("://") === -1){ | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="http in"> | ||||
| <script type="text/html" data-template-name="http in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label> | ||||
|         <select type="text" id="node-input-method" style="width:70%;"> | ||||
| @@ -45,7 +45,7 @@ | ||||
|     <div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="http response"> | ||||
| <script type="text/html" data-template-name="http response"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="http request"> | ||||
| <script type="text/html" data-template-name="http request"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label> | ||||
|         <select type="text" id="node-input-method" style="width:70%;"> | ||||
| @@ -33,8 +33,12 @@ | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row node-input-paytoqs-row"> | ||||
|         <input type="checkbox" id="node-input-paytoqs" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-input-paytoqs" style="width: auto" data-i18n="httpin.label.paytoqs"></label> | ||||
|         <label for="node-input-paytoqs"><span data-i18n="common.label.payload"></span></label> | ||||
|         <select id="node-input-paytoqs" style="width: 70%;"> | ||||
|             <option value="ignore" data-i18n="httpin.label.paytoqs.ignore"></option> | ||||
|             <option value="query" data-i18n="httpin.label.paytoqs.query"></option> | ||||
|             <option value="body" data-i18n="httpin.label.paytoqs.body"></option> | ||||
|         </select> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
| @@ -168,6 +172,13 @@ | ||||
|                     $(".node-input-paytoqs-row").hide(); | ||||
|                 } | ||||
|             }); | ||||
|             if (this.paytoqs === true || this.paytoqs == "query") { | ||||
|                 $("#node-input-paytoqs").val("query"); | ||||
|             } else if (this.paytoqs === "body") { | ||||
|                 $("#node-input-paytoqs").val("body"); | ||||
|             } else { | ||||
|                 $("#node-input-paytoqs").val("ignore"); | ||||
|             } | ||||
|             if (this.authType) { | ||||
|                 $('#node-input-useAuth').prop('checked', true); | ||||
|                 $("#node-input-authType-select").val(this.authType); | ||||
|   | ||||
| @@ -28,7 +28,8 @@ module.exports = function(RED) { | ||||
|         var nodeUrl = n.url; | ||||
|         var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; | ||||
|         var nodeMethod = n.method || "GET"; | ||||
|         var paytoqs = n.paytoqs; | ||||
|         var paytoqs = false; | ||||
|         var paytobody = false; | ||||
|         var nodeHTTPPersistent = n["persist"]; | ||||
|         if (n.tls) { | ||||
|             var tlsNode = RED.nodes.getNode(n.tls); | ||||
| @@ -38,6 +39,10 @@ module.exports = function(RED) { | ||||
|         if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } | ||||
|         else { this.reqTimeout = 120000; } | ||||
|  | ||||
|         if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } | ||||
|         else if (n.paytoqs === "body") { paytobody = true; } | ||||
|  | ||||
|  | ||||
|         var prox, noprox; | ||||
|         if (process.env.http_proxy) { prox = process.env.http_proxy; } | ||||
|         if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } | ||||
| @@ -277,6 +282,14 @@ module.exports = function(RED) { | ||||
|                     node.error(RED._("httpin.errors.invalid-payload"),msg); | ||||
|                     nodeDone(); | ||||
|                     return; | ||||
|                 }  | ||||
|             } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) { | ||||
|                 if (typeof msg.payload === "object") { | ||||
|                     opts.body = JSON.stringify(msg.payload); | ||||
|                 } else if (typeof msg.payload == "number") { | ||||
|                     opts.body = msg.payload+""; | ||||
|                 } else if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { | ||||
|                     opts.body = msg.payload; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
| <!-- WebSocket Input Node --> | ||||
| <script type="text/x-red" data-template-name="websocket in"> | ||||
| <script type="text/html" data-template-name="websocket in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label> | ||||
|         <select id="node-input-mode"> | ||||
| @@ -198,7 +198,7 @@ | ||||
| </script> | ||||
|  | ||||
| <!-- WebSocket out Node --> | ||||
| <script type="text/x-red" data-template-name="websocket out"> | ||||
| <script type="text/html" data-template-name="websocket out"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label> | ||||
|         <select id="node-input-mode"> | ||||
| @@ -221,7 +221,7 @@ | ||||
| </script> | ||||
|  | ||||
| <!-- WebSocket Server configuration node --> | ||||
| <script type="text/x-red" data-template-name="websocket-listener"> | ||||
| <script type="text/html" data-template-name="websocket-listener"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label> | ||||
|         <input id="node-config-input-path" type="text" placeholder="/ws/example"> | ||||
| @@ -240,7 +240,7 @@ | ||||
| </script> | ||||
|  | ||||
| <!-- WebSocket Client configuration node --> | ||||
| <script type="text/x-red" data-template-name="websocket-client"> | ||||
| <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"> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="tcp in"> | ||||
| <script type="text/html" data-template-name="tcp in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-server"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label> | ||||
|         <select id="node-input-server" style="width:120px; margin-right:5px;"> | ||||
| @@ -108,7 +108,7 @@ | ||||
| </script> | ||||
|  | ||||
|  | ||||
| <script type="text/x-red" data-template-name="tcp out"> | ||||
| <script type="text/html" data-template-name="tcp out"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label> | ||||
|         <select id="node-input-beserver" style="width:150px; margin-right:5px;"> | ||||
| @@ -187,7 +187,7 @@ | ||||
| </script> | ||||
|  | ||||
|  | ||||
| <script type="text/x-red" data-template-name="tcp request"> | ||||
| <script type="text/html" data-template-name="tcp request"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label> | ||||
|         <input type="text" id="node-input-server" placeholder="ip.address" style="width:45%"> | ||||
|   | ||||
| @@ -74,7 +74,7 @@ module.exports = function(RED) { | ||||
|                     buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; | ||||
|                     node.connected = true; | ||||
|                     node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); | ||||
|                     node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|                     node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); | ||||
|                 }); | ||||
|                 client.setKeepAlive(true,120000); | ||||
|                 connectionPool[id] = client; | ||||
| @@ -121,7 +121,7 @@ module.exports = function(RED) { | ||||
|                 client.on('close', function() { | ||||
|                     delete connectionPool[id]; | ||||
|                     node.connected = false; | ||||
|                     node.status({fill:"red",shape:"ring",text:"common.status.disconnected"}); | ||||
|                     node.status({fill:"red",shape:"ring",text:"common.status.disconnected",_session:{type:"tcp",id:id}}); | ||||
|                     if (!node.closing) { | ||||
|                         if (end) { // if we were asked to close then try to reconnect once very quick. | ||||
|                             end = false; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
| --> | ||||
|  | ||||
| <!--  The Input Node  --> | ||||
| <script type="text/x-red" data-template-name="udp in"> | ||||
| <script type="text/html" data-template-name="udp in"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label> | ||||
|         <select id="node-input-multicast" style='width:70%'> | ||||
| @@ -115,7 +115,7 @@ | ||||
|  | ||||
|  | ||||
| <!--  The Output Node  --> | ||||
| <script type="text/x-red" data-template-name="udp out"> | ||||
| <script type="text/html" data-template-name="udp out"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-port"><i class="fa fa-envelope"></i> <span data-i18n="udp.label.send"></span></label> | ||||
|         <select id="node-input-multicast" style="width:40%"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="csv"> | ||||
| <script type="text/html" data-template-name="csv"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label> | ||||
|         <input type="text" id="node-input-temp" data-i18n="[placeholder]csv.placeholder.columns"> | ||||
| @@ -28,11 +28,15 @@ | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label> | ||||
|         <span data-i18n="csv.label.skip-s"></span> <input type="text" id="node-input-skip" style="width:30px; height:25px;"/> <span data-i18n="csv.label.skip-e"></span><br/> | ||||
|         <span data-i18n="csv.label.skip-s"></span> <input type="text" id="node-input-skip" style="width:40px; height:25px;"/> <span data-i18n="csv.label.skip-e"></span><br/> | ||||
|         <label> </label> | ||||
|         <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> </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> </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> </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> | ||||
| @@ -45,8 +49,13 @@ | ||||
|         <label style="width:100%;"><span data-i18n="csv.label.o2c"></span></label> | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label> | ||||
|         <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> | ||||
|         <label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label> | ||||
|         <!-- <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> --> | ||||
|         <select style="width:60%" id="node-input-hdrout"> | ||||
|             <option value="none" data-i18n="csv.hdrout.none"></option> | ||||
|             <option value="all" data-i18n="csv.hdrout.all"></option> | ||||
|             <option value="once" data-i18n="csv.hdrout.once"></option> | ||||
|         </select> | ||||
|     </div> | ||||
|     <div class="form-row" style="padding-left:20px;"> | ||||
|         <label></label> | ||||
| @@ -69,12 +78,14 @@ | ||||
|             sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)}, | ||||
|             //quo: {value:'"',required:true}, | ||||
|             hdrin: {value:""}, | ||||
|             hdrout: {value:""}, | ||||
|             hdrout: {value:"none"}, | ||||
|             multi: {value:"one",required:true}, | ||||
|             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, | ||||
| @@ -86,6 +97,8 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             if (this.hdrout === false) { this.hdrout = "none"; $("#node-input-hdrout").val("none"); } | ||||
|             if (this.hdrout === true) { this.hdrout = "all"; $("#node-input-hdrout").val("all");} | ||||
|             if (this.strings === undefined) { this.strings = true; $("#node-input-strings").prop('checked', true); } | ||||
|             if (this.skip === undefined) { this.skip = 0; $("#node-input-skip").val("0");} | ||||
|             $("#node-input-skip").spinner({ min:0 }); | ||||
|   | ||||
| @@ -26,12 +26,16 @@ module.exports = function(RED) { | ||||
|         this.lineend = "\n"; | ||||
|         this.multi = n.multi || "one"; | ||||
|         this.hdrin = n.hdrin || false; | ||||
|         this.hdrout = n.hdrout || false; | ||||
|         this.hdrout = n.hdrout || "none"; | ||||
|         this.goodtmpl = true; | ||||
|         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; } | ||||
|         if (this.hdrout === false) { this.hdrout = "none"; } | ||||
|         if (this.hdrout === true) { this.hdrout = "all"; } | ||||
|         var tmpwarn = true; | ||||
|         var node = this; | ||||
|  | ||||
| @@ -49,14 +53,22 @@ module.exports = function(RED) { | ||||
|             return col; | ||||
|         } | ||||
|         node.template = clean(node.template); | ||||
|         node.hdrSent = false; | ||||
|  | ||||
|         this.on("input", function(msg) { | ||||
|             if (msg.hasOwnProperty("reset")) { | ||||
|                 node.hdrSent = false; | ||||
|             } | ||||
|             if (msg.hasOwnProperty("payload")) { | ||||
|                 if (typeof msg.payload == "object") { // convert object to CSV string | ||||
|                     try { | ||||
|                         var ou = ""; | ||||
|                         if (node.hdrout) { | ||||
|                         if (node.hdrout !== "none" && node.hdrSent === false) { | ||||
|                             if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                 node.template = clean((msg.columns || "").split(",")); | ||||
|                             } | ||||
|                             ou += node.template.join(node.sep) + node.ret; | ||||
|                             if (node.hdrout === "once") { node.hdrSent = true; } | ||||
|                         } | ||||
|                         if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } | ||||
|                         for (var s = 0; s < msg.payload.length; s++) { | ||||
| @@ -75,13 +87,15 @@ module.exports = function(RED) { | ||||
|                                 ou += msg.payload[s].join(node.sep) + node.ret; | ||||
|                             } | ||||
|                             else { | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                     node.template = clean((msg.columns || "").split(",")); | ||||
|                                 } | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '')) { | ||||
|                                     /* istanbul ignore else */ | ||||
|                                     if (tmpwarn === true) { // just warn about missing template once | ||||
|                                         node.warn(RED._("csv.errors.obj_csv")); | ||||
|                                         tmpwarn = false; | ||||
|                                     } | ||||
|                                     ou = ""; | ||||
|                                     for (var p in msg.payload[0]) { | ||||
|                                         /* istanbul ignore else */ | ||||
|                                         if (msg.payload[0].hasOwnProperty(p)) { | ||||
| @@ -125,6 +139,7 @@ module.exports = function(RED) { | ||||
|                             } | ||||
|                         } | ||||
|                         msg.payload = ou; | ||||
|                         msg.columns = node.template.join(','); | ||||
|                         if (msg.payload !== '') { node.send(msg); } | ||||
|                     } | ||||
|                     catch(e) { node.error(e,msg); } | ||||
| @@ -173,20 +188,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 | ||||
|                                 else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // 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 | ||||
| @@ -202,17 +226,21 @@ module.exports = function(RED) { | ||||
|                             } | ||||
|                         } | ||||
|                         // Finished so finalize and send anything left | ||||
|                         //console.log(j,k,o,k[j]); | ||||
|                         if (f === false) { node.warn(RED._("csv.errors.bad_csv")); } | ||||
|                         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 | ||||
|                         } | ||||
|                         var has_parts = msg.hasOwnProperty("parts"); | ||||
|  | ||||
|                         if (node.multi !== "one") { | ||||
|                             msg.payload = a; | ||||
|                             if (has_parts) { | ||||
| @@ -221,12 +249,14 @@ module.exports = function(RED) { | ||||
|                                 } | ||||
|                                 if (msg.parts.index + 1 === msg.parts.count) { | ||||
|                                     msg.payload = node.store; | ||||
|                                     msg.columns = node.template.filter(val => val).join(','); | ||||
|                                     delete msg.parts; | ||||
|                                     node.send(msg); | ||||
|                                     node.store = []; | ||||
|                                 } | ||||
|                             } | ||||
|                             else { | ||||
|                                 msg.columns = node.template.filter(val => val).join(','); | ||||
|                                 node.send(msg); // finally send the array | ||||
|                             } | ||||
|                         } | ||||
| @@ -234,6 +264,7 @@ module.exports = function(RED) { | ||||
|                             var len = a.length; | ||||
|                             for (var i = 0; i < len; i++) { | ||||
|                                 var newMessage = RED.util.cloneMessage(msg); | ||||
|                                 newMessage.columns = node.template.filter(val => val).join(','); | ||||
|                                 newMessage.payload = a[i]; | ||||
|                                 if (!has_parts) { | ||||
|                                     newMessage.parts = { | ||||
| @@ -259,7 +290,11 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 else { node.warn(RED._("csv.errors.csv_js")); } | ||||
|             } | ||||
|             else { node.send(msg); } // If no payload - just pass it on. | ||||
|             else { | ||||
|                 if (!msg.hasOwnProperty("reset")) { | ||||
|                     node.send(msg); // If no payload and not reset - just pass it on. | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     RED.nodes.registerType("csv",CSVNode); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="html"> | ||||
| <script type="text/html" data-template-name="html"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label> | ||||
|         <input type="text" id="node-input-property" style="width:70%"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="json"> | ||||
| <script type="text/html" data-template-name="json"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="json.label.action"></span></label> | ||||
|         <select style="width:70%" id="node-input-action"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="xml"> | ||||
| <script type="text/html" data-template-name="xml"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label> | ||||
|         <input type="text" id="node-input-property" style="width:70%;"/> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="yaml"> | ||||
| <script type="text/html" data-template-name="yaml"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label> | ||||
|         <input type="text" id="node-input-property" style="width:70%;"/> | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="sort"> | ||||
| <script type="text/html" data-template-name="sort"> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-target"><i class="fa fa-dot-circle-o"></i> <span data-i18n="sort.target"></span></label> | ||||
|   | ||||
| @@ -179,6 +179,11 @@ module.exports = function(RED) { | ||||
|             } | ||||
|             node.pending = []; | ||||
|             this.on("input", function(msg) { | ||||
|                 if (msg.hasOwnProperty("reset")) { | ||||
|                     node.pending = []; | ||||
|                     node.pending_count = 0; | ||||
|                     return; | ||||
|                 } | ||||
|                 var queue = node.pending; | ||||
|                 queue.push(msg); | ||||
|                 node.pending_count++; | ||||
| @@ -204,11 +209,26 @@ module.exports = function(RED) { | ||||
|             var interval = Number(n.interval || "0") *1000; | ||||
|             var allow_empty_seq = n.allowEmptySequence; | ||||
|             node.pending = [] | ||||
|             var timer = setInterval(function() { | ||||
|             function msgHandler() { | ||||
|                 send_interval(node, allow_empty_seq); | ||||
|                 node.pending_count = 0; | ||||
|             }, interval); | ||||
|             } | ||||
|             var timer = undefined; | ||||
|             if (interval > 0) { | ||||
|                 timer = setInterval(msgHandler, interval); | ||||
|             } | ||||
|             this.on("input", function(msg) { | ||||
|                 if (msg.hasOwnProperty("reset")) { | ||||
|                     if (timer !== undefined) { | ||||
|                         clearInterval(timer); | ||||
|                     } | ||||
|                     node.pending = []; | ||||
|                     node.pending_count = 0; | ||||
|                     if (interval > 0) { | ||||
|                         timer = setInterval(msgHandler, interval); | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|                 node.pending.push(msg); | ||||
|                 node.pending_count++; | ||||
|                 var max_msgs = max_kept_msgs_count(node); | ||||
| @@ -219,7 +239,9 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|             }); | ||||
|             this.on("close", function() { | ||||
|                 clearInterval(timer); | ||||
|                 if (timer !== undefined) { | ||||
|                     clearInterval(timer); | ||||
|                 } | ||||
|                 node.pending = []; | ||||
|                 node.pending_count = 0; | ||||
|             }); | ||||
| @@ -230,6 +252,11 @@ module.exports = function(RED) { | ||||
|             }); | ||||
|             node.pending = {}; | ||||
|             this.on("input", function(msg) { | ||||
|                 if (msg.hasOwnProperty("reset")) { | ||||
|                     node.pending = {}; | ||||
|                     node.pending_count = 0; | ||||
|                     return; | ||||
|                 } | ||||
|                 concat_msg(node, msg); | ||||
|             }); | ||||
|             this.on("close", function() { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|   limitations under the License. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="watch"> | ||||
| <script type="text/html" data-template-name="watch"> | ||||
|     <div class="form-row node-input-filename"> | ||||
|          <label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label> | ||||
|          <input id="node-input-files" type="text" tabindex="1" data-i18n="[placeholder]watch.placeholder.files"> | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user