mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Resolve merge conflicts on update from upstream
This commit is contained in:
		
							
								
								
									
										3196
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3196
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										24
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								package.json
									
									
									
									
									
								
							@@ -6,7 +6,7 @@
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "repository": {
 | 
			
		||||
        "type": "git",
 | 
			
		||||
        "url": "https://github.com/andrewsgreene/not-node-red.git"
 | 
			
		||||
        "url": "https://github.com/defenseunicorns/Sparkles-Guide.git"
 | 
			
		||||
    },
 | 
			
		||||
    "private": "true",
 | 
			
		||||
    "scripts": {
 | 
			
		||||
@@ -19,13 +19,13 @@
 | 
			
		||||
        "docs": "grunt docs"
 | 
			
		||||
    },
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "acorn": "8.6.0",
 | 
			
		||||
        "acorn": "8.7.0",
 | 
			
		||||
        "acorn-walk": "8.2.0",
 | 
			
		||||
        "ajv": "8.8.2",
 | 
			
		||||
        "async-mutex": "0.3.2",
 | 
			
		||||
        "basic-auth": "2.0.1",
 | 
			
		||||
        "bcryptjs": "2.4.3",
 | 
			
		||||
        "body-parser": "1.19.0",
 | 
			
		||||
        "body-parser": "1.19.1",
 | 
			
		||||
        "cheerio": "1.0.0-rc.10",
 | 
			
		||||
        "clone": "2.1.2",
 | 
			
		||||
        "content-type": "1.0.4",
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
        "cors": "2.8.5",
 | 
			
		||||
        "cronosjs": "1.7.1",
 | 
			
		||||
        "denque": "2.0.1",
 | 
			
		||||
        "express": "4.17.1",
 | 
			
		||||
        "express": "4.17.2",
 | 
			
		||||
        "express-session": "1.17.2",
 | 
			
		||||
        "form-data": "4.0.0",
 | 
			
		||||
        "fs-extra": "10.0.0",
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
        "hash-sum": "2.0.0",
 | 
			
		||||
        "hpagent": "0.1.2",
 | 
			
		||||
        "https-proxy-agent": "5.0.0",
 | 
			
		||||
        "i18next": "21.5.4",
 | 
			
		||||
        "i18next": "21.6.6",
 | 
			
		||||
        "iconv-lite": "0.6.3",
 | 
			
		||||
        "is-utf8": "0.2.1",
 | 
			
		||||
        "js-yaml": "3.14.1",
 | 
			
		||||
@@ -54,21 +54,21 @@
 | 
			
		||||
        "memorystore": "1.6.6",
 | 
			
		||||
        "mime": "2.5.2",
 | 
			
		||||
        "moment-timezone": "0.5.34",
 | 
			
		||||
        "mqtt": "4.2.8",
 | 
			
		||||
        "mqtt": "4.3.4",
 | 
			
		||||
        "multer": "1.4.3",
 | 
			
		||||
        "mustache": "4.2.0",
 | 
			
		||||
        "node-red-admin": "^2.2.1",
 | 
			
		||||
        "nopt": "5.0.0",
 | 
			
		||||
        "oauth2orize": "1.11.1",
 | 
			
		||||
        "on-headers": "1.0.2",
 | 
			
		||||
        "passport": "0.5.0",
 | 
			
		||||
        "passport": "0.5.2",
 | 
			
		||||
        "passport-http-bearer": "1.0.1",
 | 
			
		||||
        "passport-oauth2-client-password": "0.1.2",
 | 
			
		||||
        "raw-body": "2.4.2",
 | 
			
		||||
        "semver": "7.3.5",
 | 
			
		||||
        "tar": "6.1.11",
 | 
			
		||||
        "tough-cookie": "4.0.0",
 | 
			
		||||
        "uglify-js": "3.14.4",
 | 
			
		||||
        "uglify-js": "3.14.5",
 | 
			
		||||
        "uuid": "8.3.2",
 | 
			
		||||
        "ws": "7.5.1",
 | 
			
		||||
        "xml2js": "0.4.23"
 | 
			
		||||
@@ -78,7 +78,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "devDependencies": {
 | 
			
		||||
        "cypress": "^9.1.1",
 | 
			
		||||
        "dompurify": "2.3.3",
 | 
			
		||||
        "dompurify": "2.3.4",
 | 
			
		||||
        "grunt": "1.4.1",
 | 
			
		||||
        "grunt-chmod": "~1.1.1",
 | 
			
		||||
        "grunt-cli": "~1.4.3",
 | 
			
		||||
@@ -101,17 +101,17 @@
 | 
			
		||||
        "i18next-http-backend": "1.3.1",
 | 
			
		||||
        "jquery-i18next": "1.2.1",
 | 
			
		||||
        "jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
 | 
			
		||||
        "marked": "3.0.7",
 | 
			
		||||
        "marked": "4.0.10",
 | 
			
		||||
        "minami": "1.2.3",
 | 
			
		||||
        "mocha": "9.1.3",
 | 
			
		||||
        "node-red-node-test-helper": "^0.2.7",
 | 
			
		||||
        "nodemon": "2.0.15",
 | 
			
		||||
        "proxy": "^1.0.2",
 | 
			
		||||
        "sass": "1.44.0",
 | 
			
		||||
        "sass": "1.48.0",
 | 
			
		||||
        "should": "13.2.3",
 | 
			
		||||
        "sinon": "11.1.2",
 | 
			
		||||
        "stoppable": "^1.1.0",
 | 
			
		||||
        "supertest": "6.1.6"
 | 
			
		||||
        "supertest": "6.2.1"
 | 
			
		||||
    },
 | 
			
		||||
    "engines": {
 | 
			
		||||
        "node": ">=16"
 | 
			
		||||
 
 | 
			
		||||
@@ -146,7 +146,7 @@ function authenticateUserToken(req) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    reject();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            }).catch(reject);
 | 
			
		||||
        } else {
 | 
			
		||||
            reject();
 | 
			
		||||
        }
 | 
			
		||||
@@ -163,6 +163,9 @@ TokensStrategy.prototype.authenticate = function(req) {
 | 
			
		||||
    authenticateUserToken(req).then(user => {
 | 
			
		||||
        this.success(user,{scope:user.permissions});
 | 
			
		||||
    }).catch(err => {
 | 
			
		||||
        if (err) {
 | 
			
		||||
            log.trace("token authentication failure: "+err.stack?err.stack:err)
 | 
			
		||||
        }
 | 
			
		||||
        this.fail(401);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,8 @@ function init(settings,_server,storage,runtimeAPI) {
 | 
			
		||||
                    auth.getToken,
 | 
			
		||||
                    auth.errorHandler
 | 
			
		||||
                );
 | 
			
		||||
            } else if (settings.adminAuth.tokens) {
 | 
			
		||||
                adminApp.use(passport.initialize());
 | 
			
		||||
            }
 | 
			
		||||
            adminApp.post("/auth/revoke",auth.needsPermission(""),auth.revoke,apiUtil.errorHandler);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/editor-api",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "main": "./lib/index.js",
 | 
			
		||||
    "repository": {
 | 
			
		||||
@@ -16,14 +16,14 @@
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@node-red/util": "2.1.4",
 | 
			
		||||
        "@node-red/editor-client": "2.1.4",
 | 
			
		||||
        "@node-red/util": "2.1.6",
 | 
			
		||||
        "@node-red/editor-client": "2.1.6",
 | 
			
		||||
        "bcryptjs": "2.4.3",
 | 
			
		||||
        "body-parser": "1.19.0",
 | 
			
		||||
        "body-parser": "1.19.1",
 | 
			
		||||
        "clone": "2.1.2",
 | 
			
		||||
        "cors": "2.8.5",
 | 
			
		||||
        "express-session": "1.17.2",
 | 
			
		||||
        "express": "4.17.1",
 | 
			
		||||
        "express": "4.17.2",
 | 
			
		||||
        "memorystore": "1.6.6",
 | 
			
		||||
        "mime": "2.5.2",
 | 
			
		||||
        "multer": "1.4.3",
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
        "oauth2orize": "1.11.1",
 | 
			
		||||
        "passport-http-bearer": "1.0.1",
 | 
			
		||||
        "passport-oauth2-client-password": "0.1.2",
 | 
			
		||||
        "passport": "0.5.0",
 | 
			
		||||
        "passport": "0.5.2",
 | 
			
		||||
        "ws": "7.5.1"
 | 
			
		||||
    },
 | 
			
		||||
    "optionalDependencies": {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,8 @@
 | 
			
		||||
        "hideOtherFlows": "他のフローを非表示",
 | 
			
		||||
        "showAllFlows": "全てのフローを表示",
 | 
			
		||||
        "hideAllFlows": "全てのフローを非表示",
 | 
			
		||||
        "hiddenFlows": "__count__ 個の非表示のフロー一覧",
 | 
			
		||||
        "hiddenFlows_plural": "__count__ 個の非表示のフロー一覧",
 | 
			
		||||
        "showLastHiddenFlow": "最後に非表示にしたフローを表示",
 | 
			
		||||
        "listFlows": "フロー一覧",
 | 
			
		||||
        "listSubflows": "サブフロー一覧",
 | 
			
		||||
@@ -669,7 +671,8 @@
 | 
			
		||||
                "unusedConfigNodes": "未使用の設定ノード",
 | 
			
		||||
                "invalidNodes": "不正なノード",
 | 
			
		||||
                "uknownNodes": "未知のノード",
 | 
			
		||||
                "unusedSubflows": "未使用のサブフロー"
 | 
			
		||||
                "unusedSubflows": "未使用のサブフロー",
 | 
			
		||||
                "hiddenFlows": "非表示のフロー"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "help": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/editor-client",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "repository": {
 | 
			
		||||
        "type": "git",
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                        text: RED._("common.label.cancel"),
 | 
			
		||||
                        click: function() {
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                            RED.view.focus();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    { // red-ui-clipboard-dialog-download
 | 
			
		||||
@@ -81,6 +82,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                            var data = $("#red-ui-clipboard-dialog-export-text").val();
 | 
			
		||||
                            downloadData("flows.json", data);
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                            RED.view.focus();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    { // red-ui-clipboard-dialog-export
 | 
			
		||||
@@ -95,6 +97,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                                $( this ).dialog( "close" );
 | 
			
		||||
                                copyText(flowData);
 | 
			
		||||
                                RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
 | 
			
		||||
                                RED.view.focus();
 | 
			
		||||
                            } else {
 | 
			
		||||
                                var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
 | 
			
		||||
                                var selectedPath = activeLibraries[activeTab].getSelected();
 | 
			
		||||
@@ -110,6 +113,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                                        contentType: "application/json; charset=utf-8"
 | 
			
		||||
                                    }).done(function() {
 | 
			
		||||
                                        $(dialog).dialog( "close" );
 | 
			
		||||
                                        RED.view.focus();
 | 
			
		||||
                                        RED.notify(RED._("library.exportedToLibrary"),"success");
 | 
			
		||||
                                    }).fail(function(xhr,textStatus,err) {
 | 
			
		||||
                                        if (xhr.status === 401) {
 | 
			
		||||
@@ -171,6 +175,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                            RED.view.focus();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    { // red-ui-clipboard-dialog-import-conflict
 | 
			
		||||
@@ -203,6 +208,7 @@ RED.clipboard = (function() {
 | 
			
		||||
                            // console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
 | 
			
		||||
                            RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
 | 
			
		||||
                            $( this ).dialog( "close" );
 | 
			
		||||
                            RED.view.focus();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
@@ -940,7 +946,8 @@ RED.clipboard = (function() {
 | 
			
		||||
        if (truncated) {
 | 
			
		||||
            msg += "_truncated";
 | 
			
		||||
        }
 | 
			
		||||
        $("#red-ui-clipboard-hidden").val(value).focus().select();
 | 
			
		||||
        var clipboardHidden = $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo(document.body);
 | 
			
		||||
        clipboardHidden.val(value).focus().select();
 | 
			
		||||
        var result =  document.execCommand("copy");
 | 
			
		||||
        if (result && element) {
 | 
			
		||||
            var popover = RED.popover.create({
 | 
			
		||||
@@ -954,14 +961,13 @@ RED.clipboard = (function() {
 | 
			
		||||
            },1000);
 | 
			
		||||
            popover.open();
 | 
			
		||||
        }
 | 
			
		||||
        $("#red-ui-clipboard-hidden").val("");
 | 
			
		||||
        clipboardHidden.remove();
 | 
			
		||||
        if (currentFocus) {
 | 
			
		||||
            $(currentFocus).focus();
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function importNodes(nodesStr,addFlow) {
 | 
			
		||||
        var newNodes = nodesStr;
 | 
			
		||||
        if (typeof nodesStr === 'string') {
 | 
			
		||||
@@ -1236,8 +1242,6 @@ RED.clipboard = (function() {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            setupDialogs();
 | 
			
		||||
 | 
			
		||||
            $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
 | 
			
		||||
 | 
			
		||||
            RED.actions.add("core:show-export-dialog",showExportNodes);
 | 
			
		||||
            RED.actions.add("core:show-import-dialog",showImportNodes);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -578,7 +578,7 @@ RED.tabs = (function() {
 | 
			
		||||
 | 
			
		||||
        function findPreviousVisibleTab(li) {
 | 
			
		||||
            if (!li) {
 | 
			
		||||
                li = ul.find("li.active").parent();
 | 
			
		||||
                li = ul.find("li.active");
 | 
			
		||||
            }
 | 
			
		||||
            var previous = li.prev();
 | 
			
		||||
            while(previous.length > 0 && previous.hasClass("hide-tab")) {
 | 
			
		||||
@@ -588,9 +588,9 @@ RED.tabs = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        function findNextVisibleTab(li) {
 | 
			
		||||
            if (!li) {
 | 
			
		||||
                li = ul.find("li.active").parent();
 | 
			
		||||
                li = ul.find("li.active");
 | 
			
		||||
            }
 | 
			
		||||
            var next = ul.find("li.active").next();
 | 
			
		||||
            var next = li.next();
 | 
			
		||||
            while(next.length > 0 && next.hasClass("hide-tab")) {
 | 
			
		||||
                next = next.next();
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -247,7 +247,7 @@
 | 
			
		||||
                        var currentExpression = expressionEditor.getValue();
 | 
			
		||||
                        var expr;
 | 
			
		||||
                        var usesContext = false;
 | 
			
		||||
                        var legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
 | 
			
		||||
                        var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
 | 
			
		||||
                        $(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
 | 
			
		||||
                        try {
 | 
			
		||||
                            expr = jsonata(currentExpression);
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,8 @@
 | 
			
		||||
                            clearTimeout: true,
 | 
			
		||||
                            setInterval: true,
 | 
			
		||||
                            clearInterval: true
 | 
			
		||||
                        }
 | 
			
		||||
                        },
 | 
			
		||||
                        extraLibs: options.extraLibs
 | 
			
		||||
                    });
 | 
			
		||||
                    if (options.cursor) {
 | 
			
		||||
                        expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,9 @@
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                if (!isSameObj(old_env, new_env)) {
 | 
			
		||||
                if (!old_env && new_env.length === 0) {
 | 
			
		||||
                    delete node.env;
 | 
			
		||||
                } else if (!isSameObj(old_env, new_env)) {
 | 
			
		||||
                    editState.changes.env = node.env;
 | 
			
		||||
                    if (new_env.length === 0) {
 | 
			
		||||
                        delete node.env;
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ RED.utils = (function() {
 | 
			
		||||
    window._marked.use({extensions: [descriptionList, description] } );
 | 
			
		||||
 | 
			
		||||
    function renderMarkdown(txt) {
 | 
			
		||||
        var rendered = _marked(txt);
 | 
			
		||||
        var rendered = _marked.parse(txt);
 | 
			
		||||
        var cleaned = DOMPurify.sanitize(rendered, {SAFE_FOR_JQUERY: true})
 | 
			
		||||
        return cleaned;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -596,7 +596,7 @@ RED.view = (function() {
 | 
			
		||||
            },
 | 
			
		||||
            tooltip: function(d) {
 | 
			
		||||
                if (d.validationErrors && d.validationErrors.length > 0) {
 | 
			
		||||
                    return RED._("editor.errors.invalidProperties")+"\n  - "+d.validationErrors.join("\n    - ")
 | 
			
		||||
                    return RED._("editor.errors.invalidProperties")+"\n  - "+d.validationErrors.join("\n  - ")
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            show: function(n) { return !n.valid }
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ RED.workspaces = (function() {
 | 
			
		||||
            var tabId = RED.nodes.id();
 | 
			
		||||
            do {
 | 
			
		||||
                workspaceIndex += 1;
 | 
			
		||||
            } while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
 | 
			
		||||
            } while ($("#red-ui-workspace-tabs li[flowname='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
 | 
			
		||||
 | 
			
		||||
            ws = {
 | 
			
		||||
                type: "tab",
 | 
			
		||||
@@ -79,12 +79,15 @@ RED.workspaces = (function() {
 | 
			
		||||
            };
 | 
			
		||||
            RED.nodes.addWorkspace(ws,targetIndex);
 | 
			
		||||
            workspace_tabs.addTab(ws,targetIndex);
 | 
			
		||||
 | 
			
		||||
            workspace_tabs.activateTab(tabId);
 | 
			
		||||
            if (!skipHistoryEntry) {
 | 
			
		||||
                RED.history.push({t:'add',workspaces:[ws],dirty:RED.nodes.dirty()});
 | 
			
		||||
                RED.nodes.dirty(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
 | 
			
		||||
 | 
			
		||||
        RED.view.focus();
 | 
			
		||||
        return ws;
 | 
			
		||||
    }
 | 
			
		||||
@@ -208,10 +211,20 @@ RED.workspaces = (function() {
 | 
			
		||||
            },
 | 
			
		||||
            onhide: function(tab) {
 | 
			
		||||
                hideStack.push(tab.id);
 | 
			
		||||
 | 
			
		||||
                var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
                hiddenTabs[tab.id] = true;
 | 
			
		||||
                RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
 | 
			
		||||
                RED.events.emit("workspace:hide",{workspace: tab.id})
 | 
			
		||||
            },
 | 
			
		||||
            onshow: function(tab) {
 | 
			
		||||
                removeFromHideStack(tab.id);
 | 
			
		||||
 | 
			
		||||
                var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
                delete hiddenTabs[tab.id];
 | 
			
		||||
                RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
 | 
			
		||||
                RED.events.emit("workspace:show",{workspace: tab.id})
 | 
			
		||||
            },
 | 
			
		||||
            minimumActiveTabWidth: 150,
 | 
			
		||||
@@ -542,9 +555,6 @@ RED.workspaces = (function() {
 | 
			
		||||
            }
 | 
			
		||||
            if (workspace_tabs.contains(id)) {
 | 
			
		||||
                workspace_tabs.hideTab(id);
 | 
			
		||||
                var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
                hiddenTabs[id] = true;
 | 
			
		||||
                RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        isHidden: function(id) {
 | 
			
		||||
@@ -572,14 +582,11 @@ RED.workspaces = (function() {
 | 
			
		||||
                }
 | 
			
		||||
                workspace_tabs.activateTab(id);
 | 
			
		||||
            }
 | 
			
		||||
            var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
            delete hiddenTabs[id];
 | 
			
		||||
            RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
 | 
			
		||||
        },
 | 
			
		||||
        refresh: function() {
 | 
			
		||||
            RED.nodes.eachWorkspace(function(ws) {
 | 
			
		||||
                workspace_tabs.renameTab(ws.id,ws.label);
 | 
			
		||||
 | 
			
		||||
                $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
 | 
			
		||||
            })
 | 
			
		||||
            RED.nodes.eachSubflow(function(sf) {
 | 
			
		||||
                if (workspace_tabs.contains(sf.id)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -360,6 +360,7 @@ button.red-ui-button-small
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: -3000px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-row .red-ui-editor-node-label-form-row {
 | 
			
		||||
    margin: 5px 0 0 50px;
 | 
			
		||||
    label {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,8 +73,8 @@ export default {
 | 
			
		||||
            },
 | 
			
		||||
            element: "#red-ui-workspace-tabs > li.active",
 | 
			
		||||
            description: {
 | 
			
		||||
                "en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-times"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.',
 | 
			
		||||
                "ja": '<p><i class="fa fa-times"></i> アイコンをクリックすることで、タブを非表示にできます。</p><p>情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。'
 | 
			
		||||
                "en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-eye-slash"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.',
 | 
			
		||||
                "ja": '<p><i class="fa fa-eye-slash"></i> アイコンをクリックすることで、タブを非表示にできます。</p><p>情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。'
 | 
			
		||||
            },
 | 
			
		||||
            interactive: false,
 | 
			
		||||
            prepare() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										711
									
								
								packages/node_modules/@node-red/nodes/core/common/20-inject.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										711
									
								
								packages/node_modules/@node-red/nodes/core/common/20-inject.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,711 @@
 | 
			
		||||
<!--
 | 
			
		||||
  Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
  http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-template-name="inject">
 | 
			
		||||
    <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-property-container-row">
 | 
			
		||||
        <ol id="node-input-property-container"></ol>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-row" id="node-once">
 | 
			
		||||
        <label for="node-input-once"> </label>
 | 
			
		||||
        <input type="checkbox" id="node-input-once" style="display:inline-block; width:15px; vertical-align:baseline;">
 | 
			
		||||
        <span data-i18n="inject.onstart"></span> 
 | 
			
		||||
        <input type="text" id="node-input-onceDelay" placeholder="0.1" style="width:45px; height:28px;"> 
 | 
			
		||||
        <span data-i18n="inject.onceDelay"></span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-row">
 | 
			
		||||
        <label for=""><i class="fa fa-repeat"></i> <span data-i18n="inject.label.repeat"></span></label>
 | 
			
		||||
        <select id="inject-time-type-select">
 | 
			
		||||
            <option value="none" data-i18n="inject.none"></option>
 | 
			
		||||
            <option value="interval" data-i18n="inject.interval"></option>
 | 
			
		||||
            <option value="interval-time" data-i18n="inject.interval-time"></option>
 | 
			
		||||
            <option value="time" data-i18n="inject.time"></option>
 | 
			
		||||
        </select>
 | 
			
		||||
        <input type="hidden" id="node-input-repeat">
 | 
			
		||||
        <input type="hidden" id="node-input-crontab">
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-row inject-time-row hidden" id="inject-time-row-interval">
 | 
			
		||||
        <span data-i18n="inject.every"></span>
 | 
			
		||||
        <input id="inject-time-interval-count" class="inject-time-count" value="1"></input>
 | 
			
		||||
        <select style="width:100px" id="inject-time-interval-units">
 | 
			
		||||
            <option value="s" data-i18n="inject.seconds"></option>
 | 
			
		||||
            <option value="m" data-i18n="inject.minutes"></option>
 | 
			
		||||
            <option value="h" data-i18n="inject.hours"></option>
 | 
			
		||||
        </select><br/>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-row inject-time-row hidden" id="inject-time-row-interval-time">
 | 
			
		||||
        <span data-i18n="inject.every"></span> <select style="width:90px; margin-left:20px;" id="inject-time-interval-time-units" class="inject-time-int-count" value="1">
 | 
			
		||||
            <option value="1">1</option>
 | 
			
		||||
            <option value="2">2</option>
 | 
			
		||||
            <option value="3">3</option>
 | 
			
		||||
            <option value="4">4</option>
 | 
			
		||||
            <option value="5">5</option>
 | 
			
		||||
            <option value="6">6</option>
 | 
			
		||||
            <option value="10">10</option>
 | 
			
		||||
            <option value="12">12</option>
 | 
			
		||||
            <option value="15">15</option>
 | 
			
		||||
            <option value="20">20</option>
 | 
			
		||||
            <option value="30">30</option>
 | 
			
		||||
            <option value="0">60</option>
 | 
			
		||||
        </select> <span data-i18n="inject.minutes"></span><br/>
 | 
			
		||||
        <span data-i18n="inject.between"></span> <select id="inject-time-interval-time-start" class="inject-time-times"></select>
 | 
			
		||||
        <span data-i18n="inject.and"></span> <select id="inject-time-interval-time-end" class="inject-time-times"></select><br/>
 | 
			
		||||
        <div id="inject-time-interval-time-days" class="inject-time-days" style="margin-top:5px">
 | 
			
		||||
            <div style="display:inline-block; vertical-align:top; margin-right:5px;" data-i18n="inject.on">on</div>
 | 
			
		||||
            <div style="display:inline-block;">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-row inject-time-row hidden" id="inject-time-row-time">
 | 
			
		||||
        <span data-i18n="inject.at"></span> <input type="text" id="inject-time-time" value="12:00"></input><br/>
 | 
			
		||||
        <div id="inject-time-time-days" class="inject-time-days">
 | 
			
		||||
            <div style="display:inline-block; vertical-align:top; margin-right:5px;" data-i18n="inject.on"></div>
 | 
			
		||||
            <div style="display:inline-block;">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
 | 
			
		||||
                    <label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
<style>
 | 
			
		||||
    .inject-time-row {
 | 
			
		||||
        padding-left: 110px;
 | 
			
		||||
    }
 | 
			
		||||
    .inject-time-row select {
 | 
			
		||||
        margin: 3px 0;
 | 
			
		||||
    }
 | 
			
		||||
    .inject-time-days label {
 | 
			
		||||
        -webkit-user-select: none;
 | 
			
		||||
        -khtml-user-select: none;
 | 
			
		||||
        -moz-user-select: none;
 | 
			
		||||
        -ms-user-select: none;
 | 
			
		||||
        user-select: none;
 | 
			
		||||
        vertical-align: baseline;
 | 
			
		||||
        width: 100px;
 | 
			
		||||
    }
 | 
			
		||||
    .inject-time-days input {
 | 
			
		||||
        width: auto !important;
 | 
			
		||||
        vertical-align: baseline !important;
 | 
			
		||||
    }
 | 
			
		||||
    .inject-time-times {
 | 
			
		||||
        width: 90px !important;
 | 
			
		||||
    }
 | 
			
		||||
    #inject-time-time {
 | 
			
		||||
        width: 75px;
 | 
			
		||||
        margin-left: 8px;
 | 
			
		||||
        margin-bottom: 8px;
 | 
			
		||||
    }
 | 
			
		||||
    .inject-time-count {
 | 
			
		||||
        padding-left: 3px !important;
 | 
			
		||||
        width: 80px !important;
 | 
			
		||||
    }
 | 
			
		||||
</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);
 | 
			
		||||
    }
 | 
			
		||||
    /** Retrieve editableList items (refactored for re-use in the form inject button)*/
 | 
			
		||||
    function getProps(el, legacy) {
 | 
			
		||||
        var result = {
 | 
			
		||||
            props: []
 | 
			
		||||
        }
 | 
			
		||||
        el.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(legacy) {
 | 
			
		||||
                    if (p.p === "payload") { // save payload to old "legacy" property
 | 
			
		||||
                        result.payloadType = p.vt;
 | 
			
		||||
                        result.payload = p.v;
 | 
			
		||||
                        delete p.v;
 | 
			
		||||
                        delete p.vt;
 | 
			
		||||
                    } else if (p.p === "topic" && p.vt === "str") {
 | 
			
		||||
                        result.topic = p.v;
 | 
			
		||||
                        delete p.v;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                result.props.push(p);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
    /** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
 | 
			
		||||
    function doInject(node, customMsg) {
 | 
			
		||||
        var label = node._def.label.call(node,customMsg?customMsg.__user_inject_props__:undefined);
 | 
			
		||||
        if (label.length > 30) {
 | 
			
		||||
            label = label.substring(0, 50) + "...";
 | 
			
		||||
        }
 | 
			
		||||
        label = label.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            url: "inject/" + node.id,
 | 
			
		||||
            type: "POST",
 | 
			
		||||
            data: JSON.stringify(customMsg||{}),
 | 
			
		||||
            contentType: "application/json; charset=utf-8",
 | 
			
		||||
            success: function (resp) {
 | 
			
		||||
                RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
 | 
			
		||||
            },
 | 
			
		||||
            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 == 500) {
 | 
			
		||||
                    RED.notify(node._("common.notification.error", { message: node._("inject.errors.failed") }), "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: jqXHR.status, message: textStatus }) }), "error");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType('inject',{  
 | 
			
		||||
        category: 'common',
 | 
			
		||||
        color:"#a6bbcf",
 | 
			
		||||
        defaults: {
 | 
			
		||||
            name: {value:""},
 | 
			
		||||
            props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v) {
 | 
			
		||||
                    if (!v || v.length === 0) { return true }
 | 
			
		||||
                    for (var i=0;i<v.length;i++) {
 | 
			
		||||
                        if (/msg|flow|global/.test(v[i].vt)) {
 | 
			
		||||
                            if (!RED.utils.validatePropertyExpression(v[i].v)) {
 | 
			
		||||
                                return false;
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (v[i].vt === "jsonata") {
 | 
			
		||||
                            try{jsonata(v[i].v);}catch(e){return false;}
 | 
			
		||||
                        } else if (v[i].vt === "json") {
 | 
			
		||||
                            try{JSON.parse(v[i].v);}catch(e){return false;}
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 2147483))) }},
 | 
			
		||||
            crontab: {value:""},
 | 
			
		||||
            once: {value:false},
 | 
			
		||||
            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 = '';
 | 
			
		||||
 | 
			
		||||
            // 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" }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
            if (props) {
 | 
			
		||||
                for (var i=0,l=props.length; i<l; i++) {
 | 
			
		||||
                    if (i > 0) lab += "\n";
 | 
			
		||||
                    if (i === 5) {
 | 
			
		||||
                        lab += "... +"+(props.length-5);
 | 
			
		||||
                        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;
 | 
			
		||||
        },
 | 
			
		||||
        label: function(customProps) {
 | 
			
		||||
            var suffix = "";
 | 
			
		||||
            // if fire once then add small indication
 | 
			
		||||
            if (this.once) {
 | 
			
		||||
                suffix = " ¹";
 | 
			
		||||
            }
 | 
			
		||||
            // but replace with repeat one if set to repeat
 | 
			
		||||
            if ((this.repeat && this.repeat != 0) || this.crontab) {
 | 
			
		||||
                suffix = " ↻";
 | 
			
		||||
            }
 | 
			
		||||
            if (this.name) {
 | 
			
		||||
                return this.name+suffix;
 | 
			
		||||
            }
 | 
			
		||||
            var payload = "";
 | 
			
		||||
            var payloadType = "str";
 | 
			
		||||
            var topic = "";
 | 
			
		||||
            if (customProps) {
 | 
			
		||||
                for (var i=0;i<customProps.length;i++) {
 | 
			
		||||
                    if (customProps[i].p === "payload") {
 | 
			
		||||
                        payload = customProps[i].v;
 | 
			
		||||
                        payloadType = customProps[i].vt;
 | 
			
		||||
                    } else if (customProps[i].p === "topic") {
 | 
			
		||||
                        topic = customProps[i].v;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                payload = this.payload || "";
 | 
			
		||||
                payloadType = this.payloadType || "str";
 | 
			
		||||
                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 (payloadType === 'date' || payloadType === 'bin' || payloadType === 'env') {
 | 
			
		||||
                if ((topic !== "") && (topic.length <= 16)) {
 | 
			
		||||
                    return topic + ":" + this._('inject.label.'+payloadType)+suffix;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return this._('inject.label.'+payloadType)+suffix;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (payloadType === 'flow' || payloadType === 'global') {
 | 
			
		||||
                var key = RED.utils.parseContextKey(payload);
 | 
			
		||||
                return payloadType+"."+key.key+suffix;
 | 
			
		||||
            } else {
 | 
			
		||||
                return this._("inject.inject")+suffix;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        labelStyle: function() {
 | 
			
		||||
            return this.name?"node_label_italic":"";
 | 
			
		||||
        },
 | 
			
		||||
        oneditprepare: function() {
 | 
			
		||||
            var node = this;
 | 
			
		||||
            var payloadType = node.payloadType;
 | 
			
		||||
 | 
			
		||||
            if (node.payloadType == null) {
 | 
			
		||||
                if (node.payload == "") {
 | 
			
		||||
                    payloadType = "date";
 | 
			
		||||
                } else {
 | 
			
		||||
                    payloadType = "str";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (node.payloadType === 'string' || node.payloadType === 'none') {
 | 
			
		||||
                payloadType = "str";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $("#inject-time-type-select").on("change", function() {
 | 
			
		||||
                $("#node-input-crontab").val('');
 | 
			
		||||
                var id = $("#inject-time-type-select").val();
 | 
			
		||||
                $(".inject-time-row").hide();
 | 
			
		||||
                $("#inject-time-row-"+id).show();
 | 
			
		||||
                if ((id == "none") || (id == "interval") || (id == "interval-time")) {
 | 
			
		||||
                    $("#node-once").show();
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    $("#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() {
 | 
			
		||||
                $("#node-input-onceDelay").attr('disabled', !$("#node-input-once").prop('checked'));
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            $(".inject-time-times").each(function() {
 | 
			
		||||
                for (var i=0; i<24; i++) {
 | 
			
		||||
                    var l = (i<10?"0":"")+i+":00";
 | 
			
		||||
                    $(this).append($("<option></option>").val(i).text(l));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            $("<option></option>").val(24).text("00:00").appendTo("#inject-time-interval-time-end");
 | 
			
		||||
            $("#inject-time-interval-time-start").on("change", function() {
 | 
			
		||||
                var start = Number($("#inject-time-interval-time-start").val());
 | 
			
		||||
                var end = Number($("#inject-time-interval-time-end").val());
 | 
			
		||||
                $("#inject-time-interval-time-end option").remove();
 | 
			
		||||
                for (var i=start+1; i<25; i++) {
 | 
			
		||||
                    var l = (i<10?"0":"")+i+":00";
 | 
			
		||||
                    if (i==24) {
 | 
			
		||||
                        l = "00:00";
 | 
			
		||||
                    }
 | 
			
		||||
                    var opt = $("<option></option>").val(i).text(l).appendTo("#inject-time-interval-time-end");
 | 
			
		||||
                    if (i === end) {
 | 
			
		||||
                        opt.attr("selected","selected");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            $(".inject-time-count").spinner({
 | 
			
		||||
                //max:60,
 | 
			
		||||
                min:1
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            var repeattype = "none";
 | 
			
		||||
            if (node.repeat != "" && node.repeat != 0) {
 | 
			
		||||
                repeattype = "interval";
 | 
			
		||||
                var r = "s";
 | 
			
		||||
                var c = node.repeat;
 | 
			
		||||
                if (node.repeat % 60 === 0) { r = "m"; c = c/60; }
 | 
			
		||||
                if (node.repeat % 1440 === 0) { r = "h"; c = c/60; }
 | 
			
		||||
                $("#inject-time-interval-count").val(c);
 | 
			
		||||
                $("#inject-time-interval-units").val(r);
 | 
			
		||||
                $("#inject-time-interval-days").prop("disabled","disabled");
 | 
			
		||||
            } else if (node.crontab) {
 | 
			
		||||
                var cronparts = node.crontab.split(" ");
 | 
			
		||||
                var days = cronparts[4];
 | 
			
		||||
                if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
 | 
			
		||||
                    repeattype = "time";
 | 
			
		||||
                    // Fixed time
 | 
			
		||||
                    var time = cronparts[1]+":"+cronparts[0];
 | 
			
		||||
                    $("#inject-time-time").val(time);
 | 
			
		||||
                    $("#inject-time-type-select").val("s");
 | 
			
		||||
                    if (days == "*") {
 | 
			
		||||
                        $("#inject-time-time-days input[type=checkbox]").prop("checked",true);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $("#inject-time-time-days input[type=checkbox]").removeAttr("checked");
 | 
			
		||||
                        days.split(",").forEach(function(v) {
 | 
			
		||||
                            $("#inject-time-time-days [value=" + v + "]").prop("checked", true);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    repeattype = "interval-time";
 | 
			
		||||
                    // interval - time period
 | 
			
		||||
                    var minutes = cronparts[0].slice(2);
 | 
			
		||||
                    if (minutes === "") { minutes = "0"; }
 | 
			
		||||
                    $("#inject-time-interval-time-units").val(minutes);
 | 
			
		||||
                    if (days == "*") {
 | 
			
		||||
                        $("#inject-time-interval-time-days input[type=checkbox]").prop("checked",true);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $("#inject-time-interval-time-days input[type=checkbox]").removeAttr("checked");
 | 
			
		||||
                        days.split(",").forEach(function(v) {
 | 
			
		||||
                            $("#inject-time-interval-time-days [value=" + v + "]").prop("checked", true);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    var time = cronparts[1];
 | 
			
		||||
                    var timeparts = time.split(",");
 | 
			
		||||
                    var start;
 | 
			
		||||
                    var end;
 | 
			
		||||
                    if (timeparts.length == 1) {
 | 
			
		||||
                        // 0 or 0-10
 | 
			
		||||
                        var hours = timeparts[0].split("-");
 | 
			
		||||
                        if (hours.length == 1) {
 | 
			
		||||
                            if (hours[0] === "") {
 | 
			
		||||
                                start = "0";
 | 
			
		||||
                                end = "0";
 | 
			
		||||
                            }
 | 
			
		||||
                            else {
 | 
			
		||||
                                start = hours[0];
 | 
			
		||||
                                end = Number(hours[0])+1;
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            start = hours[0];
 | 
			
		||||
                            end = Number(hours[1])+1;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // 23,0 or 17-23,0-10 or 23,0-2 or 17-23,0
 | 
			
		||||
                        var startparts = timeparts[0].split("-");
 | 
			
		||||
                        start = startparts[0];
 | 
			
		||||
 | 
			
		||||
                        var endparts = timeparts[1].split("-");
 | 
			
		||||
                        if (endparts.length == 1) {
 | 
			
		||||
                            end = Number(endparts[0])+1;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            end = Number(endparts[1])+1;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    $("#inject-time-interval-time-end").val(end);
 | 
			
		||||
                    $("#inject-time-interval-time-start").val(start);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $("#inject-time-type-select").val("none");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $(".inject-time-row").hide();
 | 
			
		||||
            $("#inject-time-type-select").val(repeattype);
 | 
			
		||||
            $("#inject-time-row-"+repeattype).show();
 | 
			
		||||
 | 
			
		||||
            /* */
 | 
			
		||||
 | 
			
		||||
            var eList = $('#node-input-property-container').css('min-height','120px').css('min-width','450px');
 | 
			
		||||
 | 
			
		||||
            eList.editableList({
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: "node-inject-test-inject-button",
 | 
			
		||||
                        label: node._("inject.injectNow"),
 | 
			
		||||
                        click: function(e) {
 | 
			
		||||
                            var items = eList.editableList('items');
 | 
			
		||||
                            var props = getProps(items);
 | 
			
		||||
                            var m = {__user_inject_props__: props.props};
 | 
			
		||||
                            doInject(node, m);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                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:prop.vt || 'str',types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']});
 | 
			
		||||
 | 
			
		||||
                    propertyName.typedInput('value',prop.p);
 | 
			
		||||
                    propertyValue.typedInput('value',prop.v);
 | 
			
		||||
                },
 | 
			
		||||
                removable: true,
 | 
			
		||||
                sortable: true
 | 
			
		||||
            });
 | 
			
		||||
            $('#node-inject-test-inject-button').css("float", "right").css("margin-right", "unset");
 | 
			
		||||
 | 
			
		||||
            if (RED.nodes.subflow(node.z)) {
 | 
			
		||||
                $('#node-inject-test-inject-button').attr("disabled",true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!node.props) {
 | 
			
		||||
                var payload = {
 | 
			
		||||
                    p:'payload',
 | 
			
		||||
                    v: node.payload ? node.payload : '',
 | 
			
		||||
                    vt:payloadType ? payloadType : 'date'
 | 
			
		||||
                };
 | 
			
		||||
                var topic = {
 | 
			
		||||
                    p:'topic',
 | 
			
		||||
                    v: node.topic ? node.topic : '',
 | 
			
		||||
                    vt:'str'
 | 
			
		||||
                }
 | 
			
		||||
                node.props = [payload,topic];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (var i=0; i<node.props.length; i++) {
 | 
			
		||||
                var prop = node.props[i];
 | 
			
		||||
                var newProp = { p: prop.p, v: prop.v, vt: prop.vt };
 | 
			
		||||
                if (newProp.v === undefined) {
 | 
			
		||||
                    if (prop.p === 'payload') {
 | 
			
		||||
                        newProp.v = node.payload ? node.payload : '';
 | 
			
		||||
                        newProp.vt = payloadType ? payloadType : 'date';
 | 
			
		||||
                    } else if (prop.p === 'topic' && prop.vt === "str") {
 | 
			
		||||
                        newProp.v =  node.topic ? node.topic : '';
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (newProp.vt === "string") {
 | 
			
		||||
                    // Fix bug in pre 2.1 where an old Inject node might have
 | 
			
		||||
                    // a migrated rule with type 'string' not 'str'
 | 
			
		||||
                    newProp.vt = "str";
 | 
			
		||||
                }
 | 
			
		||||
                eList.editableList('addItem',newProp);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $("#inject-time-type-select").trigger("change");
 | 
			
		||||
            $("#inject-time-interval-time-start").trigger("change");
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        oneditsave: function() {
 | 
			
		||||
            var repeat = "";
 | 
			
		||||
            var crontab = "";
 | 
			
		||||
            var type = $("#inject-time-type-select").val();
 | 
			
		||||
            if (type == "none") {
 | 
			
		||||
                // nothing
 | 
			
		||||
            } else if (type == "interval") {
 | 
			
		||||
                var count = $("#inject-time-interval-count").val();
 | 
			
		||||
                var units = $("#inject-time-interval-units").val();
 | 
			
		||||
                if (units == "s") {
 | 
			
		||||
                    repeat = count;
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (units == "m") {
 | 
			
		||||
                        //crontab = "*/"+count+" * * * "+days;
 | 
			
		||||
                        repeat = count * 60;
 | 
			
		||||
                    } else if (units == "h") {
 | 
			
		||||
                        //crontab = "0 */"+count+" * * "+days;
 | 
			
		||||
                        repeat = count * 60 * 60;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (type == "interval-time") {
 | 
			
		||||
                repeat = "";
 | 
			
		||||
                var count = $("#inject-time-interval-time-units").val();
 | 
			
		||||
                var startTime = Number($("#inject-time-interval-time-start").val());
 | 
			
		||||
                var endTime = Number($("#inject-time-interval-time-end").val());
 | 
			
		||||
                var days = $('#inject-time-interval-time-days input[type=checkbox]:checked').map(function(_, el) {
 | 
			
		||||
                    return $(el).val()
 | 
			
		||||
                }).get();
 | 
			
		||||
                if (days.length == 0) {
 | 
			
		||||
                    crontab = "";
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (days.length == 7) {
 | 
			
		||||
                        days="*";
 | 
			
		||||
                    } else {
 | 
			
		||||
                        days = days.join(",");
 | 
			
		||||
                    }
 | 
			
		||||
                    var timerange = "";
 | 
			
		||||
                    if (endTime == 0) {
 | 
			
		||||
                        timerange = startTime+"-23";
 | 
			
		||||
                    } else if (startTime+1 < endTime) {
 | 
			
		||||
                        timerange = startTime+"-"+(endTime-1);
 | 
			
		||||
                    } else if (startTime+1 == endTime) {
 | 
			
		||||
                        timerange = startTime;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        var startpart = "";
 | 
			
		||||
                        var endpart = "";
 | 
			
		||||
                        if (startTime == 23) {
 | 
			
		||||
                            startpart = "23";
 | 
			
		||||
                        } else {
 | 
			
		||||
                            startpart = startTime+"-23";
 | 
			
		||||
                        }
 | 
			
		||||
                        if (endTime == 1) {
 | 
			
		||||
                            endpart = "0";
 | 
			
		||||
                        } else {
 | 
			
		||||
                            endpart = "0-"+(endTime-1);
 | 
			
		||||
                        }
 | 
			
		||||
                        timerange = startpart+","+endpart;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (count === "0") {
 | 
			
		||||
                        crontab = count+" "+timerange+" * * "+days;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        crontab = "*/"+count+" "+timerange+" * * "+days;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (type == "time") {
 | 
			
		||||
                var time = $("#inject-time-time").val();
 | 
			
		||||
                var days = $('#inject-time-time-days  input[type=checkbox]:checked').map(function(_, el) {
 | 
			
		||||
                    return $(el).val()
 | 
			
		||||
                }).get();
 | 
			
		||||
                if (days.length == 0) {
 | 
			
		||||
                    crontab = "";
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (days.length == 7) {
 | 
			
		||||
                        days="*";
 | 
			
		||||
                    } else {
 | 
			
		||||
                        days = days.join(",");
 | 
			
		||||
                    }
 | 
			
		||||
                    var parts = time.split(":");
 | 
			
		||||
                    if (parts.length === 2) {
 | 
			
		||||
                        repeat = "";
 | 
			
		||||
                        parts[1] = ("00" + (parseInt(parts[1]) % 60)).substr(-2);
 | 
			
		||||
                        parts[0] = ("00" + (parseInt(parts[0]) % 24)).substr(-2);
 | 
			
		||||
                        crontab = parts[1]+" "+parts[0]+" * * "+days;
 | 
			
		||||
                    }
 | 
			
		||||
                    else { crontab = ""; }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $("#node-input-repeat").val(repeat);
 | 
			
		||||
            $("#node-input-crontab").val(crontab);
 | 
			
		||||
 | 
			
		||||
            /* Gather the properties */
 | 
			
		||||
            var items = $("#node-input-property-container").editableList('items');
 | 
			
		||||
            delete this.payloadType;
 | 
			
		||||
            delete this.payload;
 | 
			
		||||
            this.topic = "";
 | 
			
		||||
            var result = getProps(items, true);
 | 
			
		||||
            this.props = result.props;
 | 
			
		||||
            if(result.hasOwnProperty('payloadType')) { this.payloadType = result.payloadType; };
 | 
			
		||||
            if(result.hasOwnProperty('payload')) { this.payload = result.payload; };
 | 
			
		||||
            if(result.hasOwnProperty('topic')) { this.topic = result.topic; };
 | 
			
		||||
        },
 | 
			
		||||
        button: {
 | 
			
		||||
            enabled: function() {
 | 
			
		||||
                return !this.changed
 | 
			
		||||
            },
 | 
			
		||||
            onclick: function () {
 | 
			
		||||
                if (this.changed) {
 | 
			
		||||
                    return RED.notify(RED._("notification.warning", { message: RED._("notification.warnings.undeployedChanges") }), "warning");
 | 
			
		||||
                }
 | 
			
		||||
                doInject(this);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        oneditresize: resizeDialog
 | 
			
		||||
    });
 | 
			
		||||
})();
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										611
									
								
								packages/node_modules/@node-red/nodes/core/function/10-function.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										611
									
								
								packages/node_modules/@node-red/nodes/core/function/10-function.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,611 @@
 | 
			
		||||
<script type="text/html" data-template-name="function">
 | 
			
		||||
    <style>
 | 
			
		||||
        .func-tabs-row {
 | 
			
		||||
            margin-bottom: 0;
 | 
			
		||||
        }
 | 
			
		||||
        #node-input-libs-container-row .red-ui-editableList-container {
 | 
			
		||||
            padding: 0px;
 | 
			
		||||
        }
 | 
			
		||||
        #node-input-libs-container-row .red-ui-editableList-container li {
 | 
			
		||||
            padding:0px;
 | 
			
		||||
        }
 | 
			
		||||
        #node-input-libs-container-row .red-ui-editableList-item-remove {
 | 
			
		||||
            right: 5px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #node-input-libs-container-row .red-ui-editableList-header {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            background: var(--red-ui-tertiary-background);
 | 
			
		||||
            padding-right: 75px;
 | 
			
		||||
        }
 | 
			
		||||
        #node-input-libs-container-row .red-ui-editableList-header > div {
 | 
			
		||||
            flex-grow: 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .node-libs-entry {
 | 
			
		||||
            display: flex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .node-libs-entry .red-ui-typedInput-container {
 | 
			
		||||
            border-radius: 0;
 | 
			
		||||
            border: none;
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry .red-ui-typedInput-type-select {
 | 
			
		||||
            border-radius: 0 !important;
 | 
			
		||||
            height: 34px;
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry > span > input[type=text] {
 | 
			
		||||
            border-radius: 0;
 | 
			
		||||
            border-top-color: var(--red-ui-form-background);
 | 
			
		||||
            border-bottom-color: var(--red-ui-form-background);
 | 
			
		||||
            border-right-color: var(--red-ui-form-background);
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry > span > input[type=text].input-error {
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry > span {
 | 
			
		||||
            flex-grow: 1;
 | 
			
		||||
            width: 50%;
 | 
			
		||||
            position: relative;
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry span .node-input-libs-var, .node-libs-entry span .red-ui-typedInput-container {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry > span > span > i {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        .node-libs-entry > span > span.input-error > i {
 | 
			
		||||
            display: inline;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    </style>
 | 
			
		||||
    <input type="hidden" id="node-input-func">
 | 
			
		||||
    <input type="hidden" id="node-input-noerr">
 | 
			
		||||
    <input type="hidden" id="node-input-finalize">
 | 
			
		||||
    <input type="hidden" id="node-input-initialize">
 | 
			
		||||
 | 
			
		||||
    <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 func-tabs-row">
 | 
			
		||||
        <ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div id="func-tabs-content" style="min-height: calc(100% - 95px);">
 | 
			
		||||
 | 
			
		||||
        <div id="func-tab-config" style="display:none">
 | 
			
		||||
            <div class="form-row">
 | 
			
		||||
                <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 class="form-row node-input-libs-row hide" style="margin-bottom: 0px;">
 | 
			
		||||
                <label><i class="fa fa-cubes"></i> <span data-i18n="function.label.modules"></span></label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-row node-input-libs-row hide" id="node-input-libs-container-row">
 | 
			
		||||
                <ol id="node-input-libs-container"></ol>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div id="func-tab-init" style="display:none">
 | 
			
		||||
            <div class="form-row node-text-editor-row" style="position:relative">
 | 
			
		||||
                <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
 | 
			
		||||
                <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div id="func-tab-body" style="display:none">
 | 
			
		||||
            <div class="form-row node-text-editor-row" style="position:relative">
 | 
			
		||||
                <div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
 | 
			
		||||
                <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div id="func-tab-finalize" style="display:none">
 | 
			
		||||
            <div class="form-row node-text-editor-row" style="position:relative">
 | 
			
		||||
                <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
 | 
			
		||||
                <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
 | 
			
		||||
    var invalidModuleVNames = [
 | 
			
		||||
        'console',
 | 
			
		||||
        'util',
 | 
			
		||||
        'Buffer',
 | 
			
		||||
        'Date',
 | 
			
		||||
        'RED',
 | 
			
		||||
        'node',
 | 
			
		||||
        '__node__',
 | 
			
		||||
        'context',
 | 
			
		||||
        'flow',
 | 
			
		||||
        'global',
 | 
			
		||||
        'env',
 | 
			
		||||
        'setTimeout',
 | 
			
		||||
        'clearTimeout',
 | 
			
		||||
        'setInterval',
 | 
			
		||||
        'clearInterval',
 | 
			
		||||
        'promisify'
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    var knownFunctionNodes = {};
 | 
			
		||||
    RED.events.on("nodes:add", function(n) {
 | 
			
		||||
        if (n.type === "function") {
 | 
			
		||||
            knownFunctionNodes[n.id] = n;
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    RED.events.on("nodes:remove", function(n) {
 | 
			
		||||
        if (n.type === "function") {
 | 
			
		||||
            delete knownFunctionNodes[n.id];
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    var missingModules = [];
 | 
			
		||||
    var missingModuleReasons = {};
 | 
			
		||||
    RED.events.on("runtime-state", function(event) {
 | 
			
		||||
        if (event.error === "missing-modules") {
 | 
			
		||||
            missingModules = event.modules.map(function(m) { missingModuleReasons[m.module] = m.error; return m.module });
 | 
			
		||||
            for (var id in knownFunctionNodes) {
 | 
			
		||||
                if (knownFunctionNodes.hasOwnProperty(id) && knownFunctionNodes[id].libs && knownFunctionNodes[id].libs.length > 0) {
 | 
			
		||||
                    RED.editor.validateNode(knownFunctionNodes[id])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!event.text) {
 | 
			
		||||
            missingModuleReasons = {};
 | 
			
		||||
            missingModules = [];
 | 
			
		||||
            for (var id in knownFunctionNodes) {
 | 
			
		||||
                if (knownFunctionNodes.hasOwnProperty(id) && knownFunctionNodes[id].libs && knownFunctionNodes[id].libs.length > 0) {
 | 
			
		||||
                    RED.editor.validateNode(knownFunctionNodes[id])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        RED.view.redraw();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var installAllowList = ['*'];
 | 
			
		||||
    var installDenyList = [];
 | 
			
		||||
 | 
			
		||||
    var modulesEnabled = true;
 | 
			
		||||
    if (RED.settings.get('externalModules.modules.allowInstall', true) === false) {
 | 
			
		||||
        modulesEnabled = false;
 | 
			
		||||
    }
 | 
			
		||||
    var settingsAllowList = RED.settings.get("externalModules.modules.allowList")
 | 
			
		||||
    var settingsDenyList = RED.settings.get("externalModules.modules.denyList")
 | 
			
		||||
    if (settingsAllowList || settingsDenyList) {
 | 
			
		||||
        installAllowList = settingsAllowList;
 | 
			
		||||
        installDenyList = settingsDenyList
 | 
			
		||||
    }
 | 
			
		||||
    installAllowList = RED.utils.parseModuleList(installAllowList);
 | 
			
		||||
    installDenyList = RED.utils.parseModuleList(installDenyList);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // object that maps from library name to its descriptor
 | 
			
		||||
    var allLibs = [];
 | 
			
		||||
 | 
			
		||||
    function moduleName(module) {
 | 
			
		||||
        var match = /^([^@]+)@(.+)/.exec(module);
 | 
			
		||||
        if (match) {
 | 
			
		||||
            return [match[1], match[2]];
 | 
			
		||||
        }
 | 
			
		||||
        return [module, undefined];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getAllUsedModules() {
 | 
			
		||||
        var moduleSet = new Set();
 | 
			
		||||
        for (var id in knownFunctionNodes) {
 | 
			
		||||
            if (knownFunctionNodes.hasOwnProperty(id)) {
 | 
			
		||||
                if (knownFunctionNodes[id].libs) {
 | 
			
		||||
                    for (var i=0, l=knownFunctionNodes[id].libs.length; i<l; i++) {
 | 
			
		||||
                        if (RED.utils.checkModuleAllowed(knownFunctionNodes[id].libs[i].module,null,installAllowList,installDenyList)) {
 | 
			
		||||
                            moduleSet.add(knownFunctionNodes[id].libs[i].module);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        var modules = Array.from(moduleSet);
 | 
			
		||||
        modules.sort();
 | 
			
		||||
        return modules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function prepareLibraryConfig(node) {
 | 
			
		||||
        $(".node-input-libs-row").show();
 | 
			
		||||
        var usedModules = getAllUsedModules();
 | 
			
		||||
        var typedModules = usedModules.map(function(l) {
 | 
			
		||||
            return {icon:"fa fa-cube", value:l,label:l,hasValue:false}
 | 
			
		||||
        })
 | 
			
		||||
        typedModules.push({
 | 
			
		||||
            value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        var libList = $("#node-input-libs-container").css('min-height','100px').css('min-width','450px').editableList({
 | 
			
		||||
            header: $('<div><div data-i18n="node-red:function.require.moduleName"></div><div data-i18n="node-red:function.require.importAs"></div></div>'),
 | 
			
		||||
            addItem: function(container,i,opt) {
 | 
			
		||||
                var parent = container.parent();
 | 
			
		||||
                var row0 = $("<div/>").addClass("node-libs-entry").appendTo(container);
 | 
			
		||||
                var fmoduleSpan = $("<span>").appendTo(row0);
 | 
			
		||||
                var fmodule = $("<input/>", {
 | 
			
		||||
                    class: "node-input-libs-val",
 | 
			
		||||
                    placeholder: RED._("node-red:function.require.module"),
 | 
			
		||||
                    type: "text"
 | 
			
		||||
                }).css({
 | 
			
		||||
                }).appendTo(fmoduleSpan).typedInput({
 | 
			
		||||
                    types: typedModules,
 | 
			
		||||
                    default: usedModules.indexOf(opt.module) > -1 ? opt.module : "_custom_"
 | 
			
		||||
                });
 | 
			
		||||
                if (usedModules.indexOf(opt.module) === -1) {
 | 
			
		||||
                    fmodule.typedInput('value', opt.module);
 | 
			
		||||
                }
 | 
			
		||||
                var moduleWarning = $('<span style="position: absolute;right:2px;top:7px; display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(fmoduleSpan);
 | 
			
		||||
                RED.popover.tooltip(moduleWarning.find("i"),function() {
 | 
			
		||||
                    var val = fmodule.typedInput("type");
 | 
			
		||||
                    if (val === "_custom_") {
 | 
			
		||||
                        val = fmodule.val();
 | 
			
		||||
                    }
 | 
			
		||||
                    var errors = [];
 | 
			
		||||
 | 
			
		||||
                    if (!RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList)) {
 | 
			
		||||
                        return RED._("node-red:function.error.moduleNotAllowed",{module:val});
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return RED._("node-red:function.error.moduleLoadError",{module:val,error:missingModuleReasons[val]});
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
                var fvarSpan = $("<span>").appendTo(row0);
 | 
			
		||||
 | 
			
		||||
                var fvar = $("<input/>", {
 | 
			
		||||
                    class: "node-input-libs-var red-ui-font-code",
 | 
			
		||||
                    placeholder: RED._("node-red:function.require.var"),
 | 
			
		||||
                    type: "text"
 | 
			
		||||
                }).css({
 | 
			
		||||
                }).appendTo(fvarSpan).val(opt.var);
 | 
			
		||||
                var vnameWarning = $('<span style="position: absolute; right:2px;top:7px;display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(fvarSpan);
 | 
			
		||||
                RED.popover.tooltip(vnameWarning.find("i"),function() {
 | 
			
		||||
                    var val = fvar.val();
 | 
			
		||||
                    if (invalidModuleVNames.indexOf(val) !== -1) {
 | 
			
		||||
                        return RED._("node-red:function.error.moduleNameReserved",{name:val})
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return RED._("node-red:function.error.moduleNameError",{name:val})
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                fvar.on("change keyup paste", function (e) {
 | 
			
		||||
                    var v = $(this).val().trim();
 | 
			
		||||
                    if (v === "" || / /.test(v) || invalidModuleVNames.indexOf(v) !== -1) {
 | 
			
		||||
                        fvar.addClass("input-error");
 | 
			
		||||
                        vnameWarning.addClass("input-error");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        fvar.removeClass("input-error");
 | 
			
		||||
                        vnameWarning.removeClass("input-error");
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                fmodule.on("change keyup paste", function (e) {
 | 
			
		||||
                    var val = $(this).typedInput("type");
 | 
			
		||||
                    if (val === "_custom_") {
 | 
			
		||||
                        val = $(this).val();
 | 
			
		||||
                    }
 | 
			
		||||
                    var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
 | 
			
		||||
                    fvar.val(varName);
 | 
			
		||||
                    fvar.trigger("change");
 | 
			
		||||
 | 
			
		||||
                    if (RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList) && (missingModules.indexOf(val) === -1)) {
 | 
			
		||||
                        fmodule.removeClass("input-error");
 | 
			
		||||
                        moduleWarning.removeClass("input-error");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        fmodule.addClass("input-error");
 | 
			
		||||
                        moduleWarning.addClass("input-error");
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                if (RED.utils.checkModuleAllowed(opt.module,null,installAllowList,installDenyList) && (missingModules.indexOf(opt.module) === -1)) {
 | 
			
		||||
                    fmodule.removeClass("input-error");
 | 
			
		||||
                    moduleWarning.removeClass("input-error");
 | 
			
		||||
                } else {
 | 
			
		||||
                    fmodule.addClass("input-error");
 | 
			
		||||
                    moduleWarning.addClass("input-error");
 | 
			
		||||
                }
 | 
			
		||||
                if (opt.var) {
 | 
			
		||||
                    fvar.trigger("change");
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            removable: true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        var libs = node.libs || [];
 | 
			
		||||
        for (var i=0,l=libs.length;i<l; i++) {
 | 
			
		||||
            libList.editableList('addItem',libs[i])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getLibsList() {
 | 
			
		||||
        var _libs = [];
 | 
			
		||||
        if (RED.settings.functionExternalModules !== false) {
 | 
			
		||||
            var libs = $("#node-input-libs-container").editableList("items");
 | 
			
		||||
            libs.each(function(i) {
 | 
			
		||||
                var item = $(this);
 | 
			
		||||
                var v = item.find(".node-input-libs-var").val();
 | 
			
		||||
                var n = item.find(".node-input-libs-val").typedInput("type");
 | 
			
		||||
                if (n === "_custom_") {
 | 
			
		||||
                    n = item.find(".node-input-libs-val").val();
 | 
			
		||||
                }
 | 
			
		||||
                if ((!v || (v === "")) ||
 | 
			
		||||
                    (!n || (n === ""))) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                _libs.push({
 | 
			
		||||
                    var: v,
 | 
			
		||||
                    module: n
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return _libs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RED.nodes.registerType('function',{
 | 
			
		||||
        color:"#fdd0a2",
 | 
			
		||||
        category: 'function',
 | 
			
		||||
        defaults: {
 | 
			
		||||
            name: {value:""},
 | 
			
		||||
            func: {value:"\nreturn msg;"},
 | 
			
		||||
            outputs: {value:1},
 | 
			
		||||
            noerr: {value:0,required:true,validate:function(v) { return !v; }},
 | 
			
		||||
            initialize: {value:""},
 | 
			
		||||
            finalize: {value:""},
 | 
			
		||||
            libs: {value: [], validate: function(v) {
 | 
			
		||||
                if (!v) { return true; }
 | 
			
		||||
                for (var i=0,l=v.length;i<l;i++) {
 | 
			
		||||
                    var m = v[i];
 | 
			
		||||
                    if (!RED.utils.checkModuleAllowed(m.module,null,installAllowList,installDenyList)) {
 | 
			
		||||
                        return false
 | 
			
		||||
                    }
 | 
			
		||||
                    if (m.var === "" || / /.test(m.var)) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (missingModules.indexOf(m.module) > -1) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (invalidModuleVNames.indexOf(m.var) !== -1){
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
            }}
 | 
			
		||||
        },
 | 
			
		||||
        inputs:1,
 | 
			
		||||
        outputs:1,
 | 
			
		||||
        icon: "function.svg",
 | 
			
		||||
        label: function() {
 | 
			
		||||
            return this.name||this._("function.function");
 | 
			
		||||
        },
 | 
			
		||||
        labelStyle: function() {
 | 
			
		||||
            return this.name?"node_label_italic":"";
 | 
			
		||||
        },
 | 
			
		||||
        oneditprepare: function() {
 | 
			
		||||
            var that = this;
 | 
			
		||||
 | 
			
		||||
            var tabs = RED.tabs.create({
 | 
			
		||||
                id: "func-tabs",
 | 
			
		||||
                onchange: function(tab) {
 | 
			
		||||
                    $("#func-tabs-content").children().hide();
 | 
			
		||||
                    $("#" + tab.id).show();
 | 
			
		||||
                    let editor = $("#" + tab.id).find('.monaco-editor').first();
 | 
			
		||||
                   	if(editor.length) {
 | 
			
		||||
                        if(that.editor.nodered && that.editor.type == "monaco") {
 | 
			
		||||
                            that.editor.nodered.refreshModuleLibs(getLibsList());
 | 
			
		||||
                        }
 | 
			
		||||
                        RED.tray.resize();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            tabs.addTab({
 | 
			
		||||
                id: "func-tab-config",
 | 
			
		||||
                iconClass: "fa fa-cog",
 | 
			
		||||
                label: that._("function.label.setup")
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            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) {
 | 
			
		||||
                    var value = this.value;
 | 
			
		||||
                    if (!value.match(/^\d+$/)) { value = 1;  }
 | 
			
		||||
                    else if (value < this.min) { value = this.min; }
 | 
			
		||||
                    if (value !== this.value) { $(this).spinner("value", value); }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            var buildEditor = function(id, value, defaultValue, extraLibs) {
 | 
			
		||||
                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
 | 
			
		||||
                    },
 | 
			
		||||
                    extraLibs: extraLibs
 | 
			
		||||
                });
 | 
			
		||||
                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(), undefined, that.libs || [])
 | 
			
		||||
            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',
 | 
			
		||||
                    {
 | 
			
		||||
                        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();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var expandButtonClickHandler = function(editor) {
 | 
			
		||||
                return function(e) {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    var value = editor.getValue();
 | 
			
		||||
                    var extraLibs = that.libs || [];
 | 
			
		||||
                    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);
 | 
			
		||||
                        },
 | 
			
		||||
                        extraLibs: extraLibs
 | 
			
		||||
                    })
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $("#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"));
 | 
			
		||||
 | 
			
		||||
            if (RED.settings.functionExternalModules !== false) {
 | 
			
		||||
                prepareLibraryConfig(that);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        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) {
 | 
			
		||||
                    if (val.trim() == defaultValue.trim()) {
 | 
			
		||||
                        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;
 | 
			
		||||
            node.libs = getLibsList();
 | 
			
		||||
        },
 | 
			
		||||
        oneditcancel: function() {
 | 
			
		||||
            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)");
 | 
			
		||||
            var height = $("#dialog-form").height();
 | 
			
		||||
            for (var i=0; i<rows.length; i++) {
 | 
			
		||||
                height -= $(rows[i]).outerHeight(true);
 | 
			
		||||
            }
 | 
			
		||||
            var editorRow = $("#dialog-form>div.node-text-editor-row");
 | 
			
		||||
            height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
 | 
			
		||||
            $("#dialog-form .node-text-editor").css("height",height+"px");
 | 
			
		||||
 | 
			
		||||
            var height = size.height;
 | 
			
		||||
            $("#node-input-init-editor").css("height", (height - 83)+"px");
 | 
			
		||||
            $("#node-input-func-editor").css("height", (height - 83)+"px");
 | 
			
		||||
            $("#node-input-finalize-editor").css("height", (height - 83)+"px");
 | 
			
		||||
 | 
			
		||||
            this.initEditor.resize();
 | 
			
		||||
            this.editor.resize();
 | 
			
		||||
            this.finalizeEditor.resize();
 | 
			
		||||
 | 
			
		||||
            $("#node-input-libs-container").css("height", (height - 192)+"px");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
})();
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										506
									
								
								packages/node_modules/@node-red/nodes/core/function/10-function.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								packages/node_modules/@node-red/nodes/core/function/10-function.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,506 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
module.exports = function(RED) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    var util = require("util");
 | 
			
		||||
    var vm = require("vm");
 | 
			
		||||
    var acorn = require("acorn");
 | 
			
		||||
    var acornWalk = require("acorn-walk");
 | 
			
		||||
 | 
			
		||||
    function sendResults(node,send,_msgid,msgs,cloneFirstMessage) {
 | 
			
		||||
        if (msgs == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (!util.isArray(msgs)) {
 | 
			
		||||
            msgs = [msgs];
 | 
			
		||||
        }
 | 
			
		||||
        var msgCount = 0;
 | 
			
		||||
        for (var m=0; m<msgs.length; m++) {
 | 
			
		||||
            if (msgs[m]) {
 | 
			
		||||
                if (!util.isArray(msgs[m])) {
 | 
			
		||||
                    msgs[m] = [msgs[m]];
 | 
			
		||||
                }
 | 
			
		||||
                for (var n=0; n < msgs[m].length; n++) {
 | 
			
		||||
                    var msg = msgs[m][n];
 | 
			
		||||
                    if (msg !== null && msg !== undefined) {
 | 
			
		||||
                        if (typeof msg === 'object' && !Buffer.isBuffer(msg) && !util.isArray(msg)) {
 | 
			
		||||
                            if (msgCount === 0 && cloneFirstMessage !== false) {
 | 
			
		||||
                                msgs[m][n] = RED.util.cloneMessage(msgs[m][n]);
 | 
			
		||||
                                msg = msgs[m][n];
 | 
			
		||||
                            }
 | 
			
		||||
                            msg._msgid = _msgid;
 | 
			
		||||
                            msgCount++;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            var type = typeof msg;
 | 
			
		||||
                            if (type === 'object') {
 | 
			
		||||
                                type = Buffer.isBuffer(msg)?'Buffer':(util.isArray(msg)?'Array':'Date');
 | 
			
		||||
                            }
 | 
			
		||||
                            node.error(RED._("function.error.non-message-returned",{ type: type }));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (msgCount>0) {
 | 
			
		||||
            send(msgs);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        node.name = n.name;
 | 
			
		||||
        node.func = n.func;
 | 
			
		||||
        node.outputs = n.outputs;
 | 
			
		||||
        node.ini = n.initialize ? n.initialize.trim() : "";
 | 
			
		||||
        node.fin = n.finalize ? n.finalize.trim() : "";
 | 
			
		||||
        node.libs = n.libs || [];
 | 
			
		||||
 | 
			
		||||
        if (RED.settings.functionExternalModules === false && node.libs.length > 0) {
 | 
			
		||||
            throw new Error(RED._("function.error.externalModuleNotAllowed"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var functionText = "var results = null;"+
 | 
			
		||||
            "results = (async function(msg,__send__,__done__){ "+
 | 
			
		||||
                "var __msgid__ = msg._msgid;"+
 | 
			
		||||
                "var node = {"+
 | 
			
		||||
                    "id:__node__.id,"+
 | 
			
		||||
                    "name:__node__.name,"+
 | 
			
		||||
                    "outputCount:__node__.outputCount,"+
 | 
			
		||||
                    "log:__node__.log,"+
 | 
			
		||||
                    "error:__node__.error,"+
 | 
			
		||||
                    "warn:__node__.warn,"+
 | 
			
		||||
                    "debug:__node__.debug,"+
 | 
			
		||||
                    "trace:__node__.trace,"+
 | 
			
		||||
                    "on:__node__.on,"+
 | 
			
		||||
                    "status:__node__.status,"+
 | 
			
		||||
                    "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
 | 
			
		||||
                    "done:__done__"+
 | 
			
		||||
                "};\n"+
 | 
			
		||||
                node.func+"\n"+
 | 
			
		||||
            "})(msg,__send__,__done__);";
 | 
			
		||||
 | 
			
		||||
        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(functionText)) {
 | 
			
		||||
            // We have spotted the code contains `node.done`. It could be in a comment
 | 
			
		||||
            // so need to do the extra work to parse the AST and examine it properly.
 | 
			
		||||
            acornWalk.simple(acorn.parse(functionText,{ecmaVersion: "latest"} ), {
 | 
			
		||||
                CallExpression(astNode) {
 | 
			
		||||
                    if (astNode.callee && astNode.callee.object) {
 | 
			
		||||
                        if (astNode.callee.object.name === "node" && astNode.callee.property.name === "done") {
 | 
			
		||||
                            handleNodeDoneCall = false;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var finScript = null;
 | 
			
		||||
        var finOpt = null;
 | 
			
		||||
        node.topic = n.topic;
 | 
			
		||||
        node.outstandingTimers = [];
 | 
			
		||||
        node.outstandingIntervals = [];
 | 
			
		||||
        node.clearStatus = false;
 | 
			
		||||
 | 
			
		||||
        var sandbox = {
 | 
			
		||||
            console:console,
 | 
			
		||||
            util:util,
 | 
			
		||||
            Buffer:Buffer,
 | 
			
		||||
            Date: Date,
 | 
			
		||||
            RED: {
 | 
			
		||||
                util: RED.util
 | 
			
		||||
            },
 | 
			
		||||
            __node__: {
 | 
			
		||||
                id: node.id,
 | 
			
		||||
                name: node.name,
 | 
			
		||||
                outputCount: node.outputs,
 | 
			
		||||
                log: function() {
 | 
			
		||||
                    node.log.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                error: function() {
 | 
			
		||||
                    node.error.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                warn: function() {
 | 
			
		||||
                    node.warn.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                debug: function() {
 | 
			
		||||
                    node.debug.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                trace: function() {
 | 
			
		||||
                    node.trace.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                send: function(send, id, msgs, cloneMsg) {
 | 
			
		||||
                    sendResults(node, send, id, msgs, cloneMsg);
 | 
			
		||||
                },
 | 
			
		||||
                on: function() {
 | 
			
		||||
                    if (arguments[0] === "input") {
 | 
			
		||||
                        throw new Error(RED._("function.error.inputListener"));
 | 
			
		||||
                    }
 | 
			
		||||
                    node.on.apply(node, arguments);
 | 
			
		||||
                },
 | 
			
		||||
                status: function() {
 | 
			
		||||
                    node.clearStatus = true;
 | 
			
		||||
                    node.status.apply(node, arguments);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            context: {
 | 
			
		||||
                set: function() {
 | 
			
		||||
                    node.context().set.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                get: function() {
 | 
			
		||||
                    return node.context().get.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                keys: function() {
 | 
			
		||||
                    return node.context().keys.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                get global() {
 | 
			
		||||
                    return node.context().global;
 | 
			
		||||
                },
 | 
			
		||||
                get flow() {
 | 
			
		||||
                    return node.context().flow;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            flow: {
 | 
			
		||||
                set: function() {
 | 
			
		||||
                    node.context().flow.set.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                get: function() {
 | 
			
		||||
                    return node.context().flow.get.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                keys: function() {
 | 
			
		||||
                    return node.context().flow.keys.apply(node,arguments);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            global: {
 | 
			
		||||
                set: function() {
 | 
			
		||||
                    node.context().global.set.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                get: function() {
 | 
			
		||||
                    return node.context().global.get.apply(node,arguments);
 | 
			
		||||
                },
 | 
			
		||||
                keys: function() {
 | 
			
		||||
                    return node.context().global.keys.apply(node,arguments);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            env: {
 | 
			
		||||
                get: function(envVar) {
 | 
			
		||||
                    return RED.util.getSetting(node, envVar);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            setTimeout: function () {
 | 
			
		||||
                var func = arguments[0];
 | 
			
		||||
                var timerId;
 | 
			
		||||
                arguments[0] = function() {
 | 
			
		||||
                    sandbox.clearTimeout(timerId);
 | 
			
		||||
                    try {
 | 
			
		||||
                        func.apply(node,arguments);
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
                        node.error(err,{});
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                timerId = setTimeout.apply(node,arguments);
 | 
			
		||||
                node.outstandingTimers.push(timerId);
 | 
			
		||||
                return timerId;
 | 
			
		||||
            },
 | 
			
		||||
            clearTimeout: function(id) {
 | 
			
		||||
                clearTimeout(id);
 | 
			
		||||
                var index = node.outstandingTimers.indexOf(id);
 | 
			
		||||
                if (index > -1) {
 | 
			
		||||
                    node.outstandingTimers.splice(index,1);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            setInterval: function() {
 | 
			
		||||
                var func = arguments[0];
 | 
			
		||||
                var timerId;
 | 
			
		||||
                arguments[0] = function() {
 | 
			
		||||
                    try {
 | 
			
		||||
                        func.apply(node,arguments);
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
                        node.error(err,{});
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                timerId = setInterval.apply(node,arguments);
 | 
			
		||||
                node.outstandingIntervals.push(timerId);
 | 
			
		||||
                return timerId;
 | 
			
		||||
            },
 | 
			
		||||
            clearInterval: function(id) {
 | 
			
		||||
                clearInterval(id);
 | 
			
		||||
                var index = node.outstandingIntervals.indexOf(id);
 | 
			
		||||
                if (index > -1) {
 | 
			
		||||
                    node.outstandingIntervals.splice(index,1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        if (util.hasOwnProperty('promisify')) {
 | 
			
		||||
            sandbox.setTimeout[util.promisify.custom] = function(after, value) {
 | 
			
		||||
                return new Promise(function(resolve, reject) {
 | 
			
		||||
                    sandbox.setTimeout(function(){ resolve(value); }, after);
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
            sandbox.promisify = util.promisify;
 | 
			
		||||
        }
 | 
			
		||||
        const moduleLoadPromises = [];
 | 
			
		||||
 | 
			
		||||
        if (node.hasOwnProperty("libs")) {
 | 
			
		||||
            let moduleErrors = false;
 | 
			
		||||
            var modules = node.libs;
 | 
			
		||||
            modules.forEach(module => {
 | 
			
		||||
                var vname = module.hasOwnProperty("var") ? module.var : null;
 | 
			
		||||
                if (vname && (vname !== "")) {
 | 
			
		||||
                    if (sandbox.hasOwnProperty(vname) || vname === 'node') {
 | 
			
		||||
                        node.error(RED._("function.error.moduleNameError",{name:vname}))
 | 
			
		||||
                        moduleErrors = true;
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    sandbox[vname] = null;
 | 
			
		||||
                    var spec = module.module;
 | 
			
		||||
                    if (spec && (spec !== "")) {
 | 
			
		||||
                        moduleLoadPromises.push(RED.import(module.module).then(lib => {
 | 
			
		||||
                            sandbox[vname] = lib.default;
 | 
			
		||||
                        }).catch(err => {
 | 
			
		||||
                            node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()}))
 | 
			
		||||
                            throw err;
 | 
			
		||||
                        }));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (moduleErrors) {
 | 
			
		||||
               throw new Error(RED._("function.error.externalModuleLoadError"));
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
        const RESOLVING = 0;
 | 
			
		||||
        const RESOLVED = 1;
 | 
			
		||||
        const ERROR = 2;
 | 
			
		||||
        var state = RESOLVING;
 | 
			
		||||
        var messages = [];
 | 
			
		||||
        var processMessage = (() => {});
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        Promise.all(moduleLoadPromises).then(() => {
 | 
			
		||||
            var context = vm.createContext(sandbox);
 | 
			
		||||
            try {
 | 
			
		||||
                var iniScript = null;
 | 
			
		||||
                var iniOpt = null;
 | 
			
		||||
                if (node.ini && (node.ini !== "")) {
 | 
			
		||||
                    var iniText = `
 | 
			
		||||
                    (async function(__send__) {
 | 
			
		||||
                        var node = {
 | 
			
		||||
                            id:__node__.id,
 | 
			
		||||
                            name:__node__.name,
 | 
			
		||||
                            outputCount:__node__.outputCount,
 | 
			
		||||
                            log:__node__.log,
 | 
			
		||||
                            error:__node__.error,
 | 
			
		||||
                            warn:__node__.warn,
 | 
			
		||||
                            debug:__node__.debug,
 | 
			
		||||
                            trace:__node__.trace,
 | 
			
		||||
                            status:__node__.status,
 | 
			
		||||
                            send: function(msgs, cloneMsg) {
 | 
			
		||||
                                __node__.send(__send__, RED.util.generateId(), msgs, cloneMsg);
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        `+ node.ini +`
 | 
			
		||||
                    })(__initSend__);`;
 | 
			
		||||
                    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 () {
 | 
			
		||||
                        var node = {
 | 
			
		||||
                            id:__node__.id,
 | 
			
		||||
                            name:__node__.name,
 | 
			
		||||
                            outputCount:__node__.outputCount,
 | 
			
		||||
                            log:__node__.log,
 | 
			
		||||
                            error:__node__.error,
 | 
			
		||||
                            warn:__node__.warn,
 | 
			
		||||
                            debug:__node__.debug,
 | 
			
		||||
                            trace:__node__.trace,
 | 
			
		||||
                            status:__node__.status,
 | 
			
		||||
                            send: function(msgs, cloneMsg) {
 | 
			
		||||
                                __node__.error("Cannot send from close function");
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        `+node.fin +`
 | 
			
		||||
                    })();`;
 | 
			
		||||
                    finOpt = createVMOpt(node, " cleanup");
 | 
			
		||||
                    finScript = new vm.Script(finText, finOpt);
 | 
			
		||||
                }
 | 
			
		||||
                var promise = Promise.resolve();
 | 
			
		||||
                if (iniScript) {
 | 
			
		||||
                    context.__initSend__ = function(msgs) { node.send(msgs); };
 | 
			
		||||
                    promise = iniScript.runInContext(context, iniOpt);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                processMessage = function (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;
 | 
			
		||||
                        node.metric("duration", msg, converted);
 | 
			
		||||
                        if (process.env.NODE_RED_FUNCTION_TIME) {
 | 
			
		||||
                            node.status({fill:"yellow",shape:"dot",text:""+converted});
 | 
			
		||||
                        }
 | 
			
		||||
                    }).catch(err => {
 | 
			
		||||
                        if ((typeof err === "object") && err.hasOwnProperty("stack")) {
 | 
			
		||||
                            //remove unwanted part
 | 
			
		||||
                            var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/);
 | 
			
		||||
                            err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n');
 | 
			
		||||
                            var stack = err.stack.split(/\r?\n/);
 | 
			
		||||
 | 
			
		||||
                            //store the error in msg to be used in flows
 | 
			
		||||
                            msg.error = err;
 | 
			
		||||
 | 
			
		||||
                            var line = 0;
 | 
			
		||||
                            var errorMessage;
 | 
			
		||||
                            if (stack.length > 0) {
 | 
			
		||||
                                while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
 | 
			
		||||
                                    line++;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                if (line < stack.length) {
 | 
			
		||||
                                    errorMessage = stack[line];
 | 
			
		||||
                                    var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
 | 
			
		||||
                                    if (m) {
 | 
			
		||||
                                        var lineno = Number(m[1])-1;
 | 
			
		||||
                                        var cha = m[2];
 | 
			
		||||
                                        errorMessage += " (line "+lineno+", col "+cha+")";
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            if (!errorMessage) {
 | 
			
		||||
                                errorMessage = err.toString();
 | 
			
		||||
                            }
 | 
			
		||||
                            done(errorMessage);
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (typeof err === "string") {
 | 
			
		||||
                            done(err);
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            done(JSON.stringify(err));
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                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());
 | 
			
		||||
                    }
 | 
			
		||||
                    if (node.clearStatus) {
 | 
			
		||||
                        node.status({});
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                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
 | 
			
		||||
                updateErrorInfo(err);
 | 
			
		||||
                node.error(err);
 | 
			
		||||
            }
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
            node.error(RED._("function.error.externalModuleLoadError"));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType("function",FunctionNode, {
 | 
			
		||||
        dynamicModuleList: "libs",
 | 
			
		||||
        settings: {
 | 
			
		||||
            functionExternalModules: { value: true, exportable: true }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    RED.library.register("functions");
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										284
									
								
								packages/node_modules/@node-red/nodes/core/function/89-delay.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								packages/node_modules/@node-red/nodes/core/function/89-delay.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
			
		||||
<!--
 | 
			
		||||
  Copyright JS Foundation and other contributors, http://js.foundation
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
  http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-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">
 | 
			
		||||
            <option value="delay" data-i18n="delay.delaymsg"></option>
 | 
			
		||||
            <option value="rate" data-i18n="delay.limitrate"></option>
 | 
			
		||||
        </select>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div id="delay-details">
 | 
			
		||||
        <div class="form-row">
 | 
			
		||||
            <label></label>
 | 
			
		||||
            <select id="node-input-delay-type" style="width:270px !important">
 | 
			
		||||
                <option value="delay" data-i18n="delay.delayfixed"></option>
 | 
			
		||||
                <option value="random" data-i18n="delay.randomdelay"></option>
 | 
			
		||||
                <option value="delayv" data-i18n="delay.delayvarmsg"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-row" id="delay-details-for">
 | 
			
		||||
            <label for="node-input-timeout"><i class="fa fa-clock-o"></i> <span data-i18n="delay.for"></span></label>
 | 
			
		||||
            <input type="text" id="node-input-timeout" style="text-align:end; width:50px !important">
 | 
			
		||||
            <select id="node-input-timeoutUnits" style="width:200px !important">
 | 
			
		||||
              <option value="milliseconds" data-i18n="delay.milisecs"></option>
 | 
			
		||||
              <option value="seconds" data-i18n="delay.secs"></option>
 | 
			
		||||
              <option value="minutes" data-i18n="delay.mins"></option>
 | 
			
		||||
              <option value="hours" data-i18n="delay.hours"></option>
 | 
			
		||||
              <option value="days" data-i18n="delay.days"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="random-details" class="form-row">
 | 
			
		||||
            <label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> <span data-i18n="delay.between"></span></label>
 | 
			
		||||
            <input type="text" id="node-input-randomFirst" placeholder="" style="text-align:end; width:50px !important">
 | 
			
		||||
             <span data-i18n="delay.and"></span> 
 | 
			
		||||
            <input type="text" id="node-input-randomLast" placeholder="" style="text-align:end; width:50px !important">
 | 
			
		||||
            <select id="node-input-randomUnits" style="width:140px !important">
 | 
			
		||||
              <option value="milliseconds" data-i18n="delay.milisecs"></option>
 | 
			
		||||
              <option value="seconds" data-i18n="delay.secs"></option>
 | 
			
		||||
              <option value="minutes" data-i18n="delay.mins"></option>
 | 
			
		||||
              <option value="hours" data-i18n="delay.hours"></option>
 | 
			
		||||
              <option value="days" data-i18n="delay.days"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div id="rate-details">
 | 
			
		||||
        <div class="form-row">
 | 
			
		||||
            <label></label>
 | 
			
		||||
            <select id="node-input-rate-type" style="width:270px !important">
 | 
			
		||||
                <option value="all" data-i18n="delay.limitall"></option>
 | 
			
		||||
                <option value="topic" data-i18n="delay.limittopic"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-row">
 | 
			
		||||
            <label for="node-input-rate"><i class="fa fa-clock-o"></i> <span data-i18n="delay.rate"></span></label>
 | 
			
		||||
            <input type="text" id="node-input-rate" placeholder="1" style="text-align:end; width:40px !important">
 | 
			
		||||
            <label for="node-input-rateUnits"><span data-i18n="delay.msgper"></span></label>
 | 
			
		||||
            <input type="text" id="node-input-nbRateUnits" placeholder="1" style="text-align:end; width:40px !important">
 | 
			
		||||
            <select id="node-input-rateUnits" style="width:90px !important">
 | 
			
		||||
              <option value="second" data-i18n="delay.label.units.second.singular"></option>
 | 
			
		||||
              <option value="minute" data-i18n="delay.label.units.minute.singular"></option>
 | 
			
		||||
              <option value="hour" data-i18n="delay.label.units.hour.singular"></option>
 | 
			
		||||
              <option value="day" data-i18n="delay.label.units.day.singular"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-row" id="rate-override" style="display: flex; align-items: center">
 | 
			
		||||
            <label></label><input style="width:30px; margin:0" type="checkbox" id="node-input-allowrate"><label style="margin:0;width: auto;" for="node-input-allowrate" data-i18n="delay.allowrate"></label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-row" id="rate-details-drop">
 | 
			
		||||
            <input type="hidden" id="node-input-outputs" value="1">
 | 
			
		||||
            <label></label>
 | 
			
		||||
            <select id="node-input-drop-select" style="width: 70%">
 | 
			
		||||
                <option id="node-input-drop-select-queue" value="queue" data-i18n="delay.queuemsg"></option>
 | 
			
		||||
                <option value="drop" data-i18n="delay.dropmsg"></option>
 | 
			
		||||
                <option value="emit" data-i18n="delay.sendmsg"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-row" id="rate-details-per-topic">
 | 
			
		||||
            <label></label>
 | 
			
		||||
            <select id="node-input-rate-topic-type" style="width:270px !important">
 | 
			
		||||
                <option value="queue" data-i18n="delay.fairqueue"></option>
 | 
			
		||||
                <option value="timed" data-i18n="delay.timedqueue"></option>
 | 
			
		||||
            </select>
 | 
			
		||||
        </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>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
    RED.nodes.registerType('delay',{
 | 
			
		||||
        category: 'function',
 | 
			
		||||
        color:"#E6E0F8",
 | 
			
		||||
        defaults: {
 | 
			
		||||
            name: {value:""},
 | 
			
		||||
            pauseType: {value:"delay", required:true},
 | 
			
		||||
            timeout: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
 | 
			
		||||
            timeoutUnits: {value:"seconds"},
 | 
			
		||||
            rate: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
 | 
			
		||||
            nbRateUnits: {value:"1", required:false,
 | 
			
		||||
                          validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
 | 
			
		||||
            rateUnits: {value: "second"},
 | 
			
		||||
            randomFirst: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
 | 
			
		||||
            randomLast: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
 | 
			
		||||
            randomUnits: {value: "seconds"},
 | 
			
		||||
            drop: {value:false},
 | 
			
		||||
            allowrate: {value:false},
 | 
			
		||||
            outputs: { value: 1},
 | 
			
		||||
        },
 | 
			
		||||
        inputs:1,
 | 
			
		||||
        outputs:1,
 | 
			
		||||
        icon: "timer.svg",
 | 
			
		||||
        label: function() {
 | 
			
		||||
            if (this.name) {
 | 
			
		||||
                return this.name;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.pauseType == "delayv") {
 | 
			
		||||
                return this._("delay.label.variable");
 | 
			
		||||
            } else if (this.pauseType == "delay") {
 | 
			
		||||
                var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
 | 
			
		||||
                if (this.timeoutUnits == "milliseconds") { units = "ms"; }
 | 
			
		||||
                return this._("delay.label.delay")+" "+this.timeout+units;
 | 
			
		||||
            } else if (this.pauseType == "random") {
 | 
			
		||||
                return this._("delay.label.random");
 | 
			
		||||
            } else {
 | 
			
		||||
                var rate = this.rate+" msg/"+(this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s");
 | 
			
		||||
                if (this.pauseType == "rate") {
 | 
			
		||||
                    return this._("delay.label.limit")+" "+rate;
 | 
			
		||||
                } else if (this.pauseType == "timed") {
 | 
			
		||||
                    return this._("delay.label.limitTopic")+" "+rate;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return this._("delay.label.limitTopic")+" "+rate;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        labelStyle: function() {
 | 
			
		||||
            return this.name?"node_label_italic":"";
 | 
			
		||||
        },
 | 
			
		||||
        oneditprepare: function() {
 | 
			
		||||
            var node = this;
 | 
			
		||||
            $( "#node-input-timeout" ).spinner({min:1});
 | 
			
		||||
            $( "#node-input-rate" ).spinner({min:1});
 | 
			
		||||
            $( "#node-input-nbRateUnits" ).spinner({min:1});
 | 
			
		||||
 | 
			
		||||
            $( "#node-input-randomFirst" ).spinner({min:0});
 | 
			
		||||
            $( "#node-input-randomLast" ).spinner({min:1});
 | 
			
		||||
 | 
			
		||||
            $('.ui-spinner-button').on("click", function() {
 | 
			
		||||
                $(this).siblings('input').trigger("change");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            $( "#node-input-nbRateUnits" ).on('change keyup', function() {
 | 
			
		||||
                var $this = $(this);
 | 
			
		||||
                var val = parseInt($this.val());
 | 
			
		||||
                var type = "singular";
 | 
			
		||||
                if (val > 1) {
 | 
			
		||||
                    type = "plural";
 | 
			
		||||
                }
 | 
			
		||||
                if ($this.attr("data-type") == type) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $this.attr("data-type", type);
 | 
			
		||||
                $("#node-input-rateUnits option").each(function () {
 | 
			
		||||
                    var $option = $(this);
 | 
			
		||||
                    var key = "delay.label.units." + $option.val() + "." + type;
 | 
			
		||||
                    $option.attr('data-i18n', 'node-red:' + key);
 | 
			
		||||
                    $option.html(node._(key));
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (this.pauseType == "delay") {
 | 
			
		||||
                $("#node-input-delay-action").val('delay');
 | 
			
		||||
                $("#node-input-delay-type").val('delay');
 | 
			
		||||
            } else if (this.pauseType == "delayv") {
 | 
			
		||||
                $("#node-input-delay-action").val('delay');
 | 
			
		||||
                $("#node-input-delay-type").val('delayv');
 | 
			
		||||
            } else if (this.pauseType == "random") {
 | 
			
		||||
                $("#node-input-delay-action").val('delay');
 | 
			
		||||
                $("#node-input-delay-type").val('random');
 | 
			
		||||
            } else if (this.pauseType == "rate") {
 | 
			
		||||
                $("#node-input-delay-action").val('rate');
 | 
			
		||||
                $("#node-input-rate-type").val('all');
 | 
			
		||||
            } else if (this.pauseType == "queue") {
 | 
			
		||||
                $("#node-input-delay-action").val('rate');
 | 
			
		||||
                $("#node-input-rate-type").val('topic');
 | 
			
		||||
                $("#node-input-rate-topic-type").val('queue');
 | 
			
		||||
            } else if (this.pauseType == "timed") {
 | 
			
		||||
                $("#node-input-delay-action").val('rate');
 | 
			
		||||
                $("#node-input-rate-type").val('topic');
 | 
			
		||||
                $("#node-input-rate-topic-type").val('timed');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.timeoutUnits) {
 | 
			
		||||
                $("#node-input-timeoutUnits option").filter(function() {
 | 
			
		||||
                    return $(this).val() == 'seconds';
 | 
			
		||||
                }).attr('selected', true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.randomUnits) {
 | 
			
		||||
                $("#node-input-randomUnits option").filter(function() {
 | 
			
		||||
                    return $(this).val() == 'seconds';
 | 
			
		||||
                }).attr('selected', true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $("#node-input-delay-action").on("change",function() {
 | 
			
		||||
                if (this.value === "delay") {
 | 
			
		||||
                    $("#delay-details").show();
 | 
			
		||||
                    $("#rate-details").hide();
 | 
			
		||||
                } else if (this.value === "rate") {
 | 
			
		||||
                    $("#delay-details").hide();
 | 
			
		||||
                    $("#rate-details").show();
 | 
			
		||||
                }
 | 
			
		||||
            }).trigger("change");
 | 
			
		||||
 | 
			
		||||
            $("#node-input-delay-type").on("change", function() {
 | 
			
		||||
                if (this.value === "delay") {
 | 
			
		||||
                    $("#delay-details-for").show();
 | 
			
		||||
                    $("#random-details").hide();
 | 
			
		||||
                } else if (this.value === "delayv") {
 | 
			
		||||
                    $("#delay-details-for").show();
 | 
			
		||||
                    $("#random-details").hide();
 | 
			
		||||
                } else if (this.value === "random") {
 | 
			
		||||
                    $("#delay-details-for").hide();
 | 
			
		||||
                    $("#random-details").show();
 | 
			
		||||
                }
 | 
			
		||||
            }).trigger("change");
 | 
			
		||||
 | 
			
		||||
            if (this.outputs === 2) {
 | 
			
		||||
                $("#node-input-drop-select").val("emit");
 | 
			
		||||
            } else if (this.drop) {
 | 
			
		||||
                $("#node-input-drop-select").val("drop");
 | 
			
		||||
            } else {
 | 
			
		||||
                $("#node-input-drop-select").val("queue");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $("#node-input-rate-type").on("change", function() {
 | 
			
		||||
                if (this.value === "all") {
 | 
			
		||||
                    $("#rate-details-per-topic").hide();
 | 
			
		||||
                    $("#node-input-drop-select-queue").attr('disabled', false);
 | 
			
		||||
                } else if (this.value === "topic") {
 | 
			
		||||
                    if ($("#node-input-drop-select").val() === "queue") {
 | 
			
		||||
                        $("#node-input-drop-select").val("drop");
 | 
			
		||||
                    }
 | 
			
		||||
                    $("#node-input-drop-select-queue").attr('disabled', true);
 | 
			
		||||
                    $("#rate-details-per-topic").show();
 | 
			
		||||
                }
 | 
			
		||||
            }).trigger("change");
 | 
			
		||||
        },
 | 
			
		||||
        oneditsave: function() {
 | 
			
		||||
            var action = $("#node-input-delay-action").val();
 | 
			
		||||
            if (action === "delay") {
 | 
			
		||||
                this.pauseType = $("#node-input-delay-type").val();
 | 
			
		||||
                $("#node-input-outputs").val(1);
 | 
			
		||||
            } else if (action === "rate") {
 | 
			
		||||
                action = $("#node-input-rate-type").val();
 | 
			
		||||
                if (action === "all") {
 | 
			
		||||
                    this.pauseType = "rate";
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.pauseType = $("#node-input-rate-topic-type").val();
 | 
			
		||||
                }
 | 
			
		||||
                var dropType = $("#node-input-drop-select").val();
 | 
			
		||||
                this.drop = dropType !== "queue";
 | 
			
		||||
                $("#node-input-outputs").val(dropType === "emit"?2:1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										97
									
								
								packages/node_modules/@node-red/nodes/core/function/rbe.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								packages/node_modules/@node-red/nodes/core/function/rbe.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
 | 
			
		||||
module.exports = function(RED) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
    function RbeNode(n) {
 | 
			
		||||
        RED.nodes.createNode(this,n);
 | 
			
		||||
        this.func = n.func || "rbe";
 | 
			
		||||
        this.gap = n.gap || "0";
 | 
			
		||||
        this.start = n.start || '';
 | 
			
		||||
        this.inout = n.inout || "out";
 | 
			
		||||
        this.pc = false;
 | 
			
		||||
        if (this.gap.substr(-1) === "%") {
 | 
			
		||||
            this.pc = true;
 | 
			
		||||
            this.gap = parseFloat(this.gap);
 | 
			
		||||
        }
 | 
			
		||||
        this.g = this.gap;
 | 
			
		||||
        this.property = n.property || "payload";
 | 
			
		||||
        this.topi = n.topi || "topic";
 | 
			
		||||
        this.septopics = true;
 | 
			
		||||
        if (n.septopics !== undefined && n.septopics === false) {
 | 
			
		||||
            this.septopics = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var node = this;
 | 
			
		||||
 | 
			
		||||
        node.previous = {};
 | 
			
		||||
        this.on("input",function(msg) {
 | 
			
		||||
            var topic;
 | 
			
		||||
            try {
 | 
			
		||||
                topic = RED.util.getMessageProperty(msg,node.topi);
 | 
			
		||||
            }
 | 
			
		||||
            catch(e) { }
 | 
			
		||||
            if (msg.hasOwnProperty("reset")) {
 | 
			
		||||
                if (node.septopics && topic && (typeof topic === "string") && (topic !== "")) {
 | 
			
		||||
                    delete node.previous[msg.topic];
 | 
			
		||||
                }
 | 
			
		||||
                else { node.previous = {}; }
 | 
			
		||||
            }
 | 
			
		||||
            var value = RED.util.getMessageProperty(msg,node.property);
 | 
			
		||||
            if (value !== undefined) {
 | 
			
		||||
                var t = "_no_topic";
 | 
			
		||||
                if (node.septopics) { t = topic || t; }
 | 
			
		||||
                if ((this.func === "rbe") || (this.func === "rbei")) {
 | 
			
		||||
                    var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false;
 | 
			
		||||
                    if (typeof(value) === "object") {
 | 
			
		||||
                        if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; }
 | 
			
		||||
                        if (!RED.util.compareObjects(value, node.previous[t])) {
 | 
			
		||||
                            node.previous[t] = RED.util.cloneMessage(value);
 | 
			
		||||
                            if (doSend) { node.send(msg); }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        if (value !== node.previous[t]) {
 | 
			
		||||
                            node.previous[t] = RED.util.cloneMessage(value);
 | 
			
		||||
                            if (doSend) { node.send(msg); }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    var n = parseFloat(value);
 | 
			
		||||
                    if (!isNaN(n)) {
 | 
			
		||||
                        if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband" || this.func === "narrowbandEq")) {
 | 
			
		||||
                            if (node.start === '') { node.previous[t] = n; }
 | 
			
		||||
                            else { node.previous[t] = node.start; }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (node.pc) { node.gap = Math.abs(node.previous[t] * node.g / 100) || 0; }
 | 
			
		||||
                        else { node.gap = Number(node.gap); }
 | 
			
		||||
                        if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; }
 | 
			
		||||
                        if (node.previous[t] === undefined) { node.previous[t] = n - node.gap - 1; }
 | 
			
		||||
                        if (Math.abs(n - node.previous[t]) === node.gap) {
 | 
			
		||||
                            if ((this.func === "deadbandEq")||(this.func === "narrowband")) {
 | 
			
		||||
                                if (node.inout === "out") { node.previous[t] = n; }
 | 
			
		||||
                                node.send(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (Math.abs(n - node.previous[t]) > node.gap) {
 | 
			
		||||
                            if (this.func === "deadband" || this.func === "deadbandEq") {
 | 
			
		||||
                                if (node.inout === "out") { node.previous[t] = n; }
 | 
			
		||||
                                node.send(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (Math.abs(n - node.previous[t]) < node.gap) {
 | 
			
		||||
                            if ((this.func === "narrowband")||(this.func === "narrowbandEq")) {
 | 
			
		||||
                                if (node.inout === "out") { node.previous[t] = n; }
 | 
			
		||||
                                node.send(msg);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (node.inout === "in") { node.previous[t] = n; }
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        node.warn(RED._("rbe.warn.nonumber"));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } // ignore msg with no payload property.
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType("rbe",RbeNode);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										675
									
								
								packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										675
									
								
								packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,675 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
module.exports = function(RED) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
    const got = require("got");
 | 
			
		||||
    const {CookieJar} = require("tough-cookie");
 | 
			
		||||
    const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
 | 
			
		||||
    const FormData = require('form-data');
 | 
			
		||||
    const { v4: uuid } = require('uuid');
 | 
			
		||||
    const crypto = require('crypto');
 | 
			
		||||
    const URL = require("url").URL
 | 
			
		||||
    var mustache = require("mustache");
 | 
			
		||||
    var querystring = require("querystring");
 | 
			
		||||
    var cookie = require("cookie");
 | 
			
		||||
    var hashSum = require("hash-sum");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Cache a reference to the existing https.request function
 | 
			
		||||
    // so we can compare later to see if an old agent-base instance
 | 
			
		||||
    // has been required.
 | 
			
		||||
    // This is generally okay as the core nodes are required before
 | 
			
		||||
    // any contrib nodes. Where it will fail is if the agent-base module
 | 
			
		||||
    // is required via the settings file or outside of Node-RED before it
 | 
			
		||||
    // is started.
 | 
			
		||||
    // If there are other modules that patch the function, they will get undone
 | 
			
		||||
    // as well. Not much we can do about that right now. Patching core
 | 
			
		||||
    // functions is bad.
 | 
			
		||||
    const HTTPS_MODULE = require("https");
 | 
			
		||||
    const HTTPS_REQUEST = HTTPS_MODULE.request;
 | 
			
		||||
 | 
			
		||||
    function checkNodeAgentPatch() {
 | 
			
		||||
        if (HTTPS_MODULE.request !== HTTPS_REQUEST && HTTPS_MODULE.request.length === 2) {
 | 
			
		||||
            RED.log.warn(`
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------------
 | 
			
		||||
Patched https.request function detected. This will break the
 | 
			
		||||
HTTP Request node. The original code has now been restored.
 | 
			
		||||
 | 
			
		||||
This is likely caused by a contrib node including an old version of
 | 
			
		||||
the 'agent-base@<5.0.0' module.
 | 
			
		||||
 | 
			
		||||
You can identify what node is at fault by running:
 | 
			
		||||
   npm list agent-base
 | 
			
		||||
in your Node-RED user directory (${RED.settings.userDir}).
 | 
			
		||||
---------------------------------------------------------------------
 | 
			
		||||
`);
 | 
			
		||||
            HTTPS_MODULE.request = HTTPS_REQUEST
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function HTTPRequest(n) {
 | 
			
		||||
        RED.nodes.createNode(this,n);
 | 
			
		||||
        checkNodeAgentPatch();
 | 
			
		||||
        var node = this;
 | 
			
		||||
        var nodeUrl = n.url;
 | 
			
		||||
        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
 | 
			
		||||
        var nodeMethod = n.method || "GET";
 | 
			
		||||
        var paytoqs = false;
 | 
			
		||||
        var paytobody = false;
 | 
			
		||||
        var redirectList = [];
 | 
			
		||||
        var sendErrorsToCatch = n.senderr;
 | 
			
		||||
 | 
			
		||||
        var nodeHTTPPersistent = n["persist"];
 | 
			
		||||
        if (n.tls) {
 | 
			
		||||
            var tlsNode = RED.nodes.getNode(n.tls);
 | 
			
		||||
        }
 | 
			
		||||
        this.ret = n.ret || "txt";
 | 
			
		||||
        this.authType = n.authType || "basic";
 | 
			
		||||
        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; }
 | 
			
		||||
        if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); }
 | 
			
		||||
        if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); }
 | 
			
		||||
 | 
			
		||||
        var proxyConfig = null;
 | 
			
		||||
        if (n.proxy) {
 | 
			
		||||
            proxyConfig = RED.nodes.getNode(n.proxy);
 | 
			
		||||
            prox = proxyConfig.url;
 | 
			
		||||
            noprox = proxyConfig.noproxy;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let timingLog = false;
 | 
			
		||||
        if (RED.settings.hasOwnProperty("httpRequestTimingLog")) {
 | 
			
		||||
            timingLog = RED.settings.httpRequestTimingLog;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.on("input",function(msg,nodeSend,nodeDone) {
 | 
			
		||||
            checkNodeAgentPatch();
 | 
			
		||||
            //reset redirectList on each request
 | 
			
		||||
            redirectList = [];
 | 
			
		||||
            var preRequestTimestamp = process.hrtime();
 | 
			
		||||
            node.status({fill:"blue",shape:"dot",text:"httpin.status.requesting"});
 | 
			
		||||
            var url = nodeUrl || msg.url;
 | 
			
		||||
            if (msg.url && nodeUrl && (nodeUrl !== msg.url)) {  // revert change below when warning is finally removed
 | 
			
		||||
                node.warn(RED._("common.errors.nooverride"));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (isTemplatedUrl) {
 | 
			
		||||
                url = mustache.render(nodeUrl,msg);
 | 
			
		||||
            }
 | 
			
		||||
            if (!url) {
 | 
			
		||||
                node.error(RED._("httpin.errors.no-url"),msg);
 | 
			
		||||
                nodeDone();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // url must start http:// or https:// so assume http:// if not set
 | 
			
		||||
            if (url.indexOf("://") !== -1 && url.indexOf("http") !== 0) {
 | 
			
		||||
                node.warn(RED._("httpin.errors.invalid-transport"));
 | 
			
		||||
                node.status({fill:"red",shape:"ring",text:"httpin.errors.invalid-transport"});
 | 
			
		||||
                nodeDone();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) {
 | 
			
		||||
                if (tlsNode) {
 | 
			
		||||
                    url = "https://"+url;
 | 
			
		||||
                } else {
 | 
			
		||||
                    url = "http://"+url;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // The Request module used in Node-RED 1.x was tolerant of query strings that
 | 
			
		||||
            // were partially encoded. For example - "?a=hello%20there&b=20%"
 | 
			
		||||
            // The GOT module doesn't like that.
 | 
			
		||||
            // The following is an attempt to normalise the url to ensure it is properly
 | 
			
		||||
            // encoded. We cannot just encode it directly as we don't want any valid
 | 
			
		||||
            // encoded entity to end up doubly encoded.
 | 
			
		||||
            if (url.indexOf("?") > -1) {
 | 
			
		||||
                // Only do this if there is a query string to deal with
 | 
			
		||||
                const [hostPath, ...queryString] = url.split("?")
 | 
			
		||||
                const query = queryString.join("?");
 | 
			
		||||
                if (query) {
 | 
			
		||||
                    // Look for any instance of % not followed by two hex chars.
 | 
			
		||||
                    // Replace any we find with %25.
 | 
			
		||||
                    const escapedQueryString = query.replace(/(%.?.?)/g, function(v) {
 | 
			
		||||
                        if (/^%[a-f0-9]{2}/i.test(v)) {
 | 
			
		||||
                            return v;
 | 
			
		||||
                        }
 | 
			
		||||
                        return v.replace(/%/,"%25")
 | 
			
		||||
                    })
 | 
			
		||||
                    url = hostPath+"?"+escapedQueryString;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var method = nodeMethod.toUpperCase() || "GET";
 | 
			
		||||
            if (msg.method && n.method && (n.method !== "use")) {     // warn if override option not set
 | 
			
		||||
                node.warn(RED._("common.errors.nooverride"));
 | 
			
		||||
            }
 | 
			
		||||
            if (msg.method && n.method && (n.method === "use")) {
 | 
			
		||||
                method = msg.method.toUpperCase();          // use the msg parameter
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // var isHttps = (/^https/i.test(url));
 | 
			
		||||
 | 
			
		||||
            var opts = {};
 | 
			
		||||
            // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(.
 | 
			
		||||
            // Had to remove this to get http->https redirect to work
 | 
			
		||||
            // opts.defaultPort = isHttps?443:80;
 | 
			
		||||
            opts.timeout = node.reqTimeout;
 | 
			
		||||
            opts.throwHttpErrors = false;
 | 
			
		||||
            // TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
 | 
			
		||||
            opts.decompress = false;
 | 
			
		||||
            opts.method = method;
 | 
			
		||||
            opts.headers = {};
 | 
			
		||||
            opts.retry = 0;
 | 
			
		||||
            opts.responseType = 'buffer';
 | 
			
		||||
            opts.maxRedirects = 21;
 | 
			
		||||
            opts.cookieJar = new CookieJar();
 | 
			
		||||
            opts.ignoreInvalidCookies = true;
 | 
			
		||||
            opts.forever = nodeHTTPPersistent;
 | 
			
		||||
            if (msg.requestTimeout !== undefined) {
 | 
			
		||||
                if (isNaN(msg.requestTimeout)) {
 | 
			
		||||
                    node.warn(RED._("httpin.errors.timeout-isnan"));
 | 
			
		||||
                } else if (msg.requestTimeout < 1) {
 | 
			
		||||
                    node.warn(RED._("httpin.errors.timeout-isnegative"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    opts.timeout = msg.requestTimeout;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            const originalHeaderMap = {};
 | 
			
		||||
 | 
			
		||||
            opts.hooks = {
 | 
			
		||||
                beforeRequest: [
 | 
			
		||||
                    options => {
 | 
			
		||||
                        // Whilst HTTP headers are meant to be case-insensitive,
 | 
			
		||||
                        // in the real world, there are servers that aren't so compliant.
 | 
			
		||||
                        // GOT will lower case all headers given a chance, so we need
 | 
			
		||||
                        // to restore the case of any headers the user has set.
 | 
			
		||||
                        Object.keys(options.headers).forEach(h => {
 | 
			
		||||
                            if (originalHeaderMap[h] && originalHeaderMap[h] !== h) {
 | 
			
		||||
                                options.headers[originalHeaderMap[h]] = options.headers[h];
 | 
			
		||||
                                delete options.headers[h];
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                beforeRedirect: [
 | 
			
		||||
                    (options, response) => {
 | 
			
		||||
                        let redirectInfo = {
 | 
			
		||||
                            location: response.headers.location
 | 
			
		||||
                        }
 | 
			
		||||
                        if (response.headers.hasOwnProperty('set-cookie')) {
 | 
			
		||||
                            redirectInfo.cookies = extractCookies(response.headers['set-cookie']);
 | 
			
		||||
                        }
 | 
			
		||||
                        redirectList.push(redirectInfo)
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ctSet = "Content-Type"; // set default camel case
 | 
			
		||||
            var clSet = "Content-Length";
 | 
			
		||||
            if (msg.headers) {
 | 
			
		||||
                if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
 | 
			
		||||
                    var headerHash = msg.headers['x-node-red-request-node'];
 | 
			
		||||
                    delete msg.headers['x-node-red-request-node'];
 | 
			
		||||
                    var hash = hashSum(msg.headers);
 | 
			
		||||
                    if (hash === headerHash) {
 | 
			
		||||
                        delete msg.headers;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (msg.headers) {
 | 
			
		||||
                    for (var v in msg.headers) {
 | 
			
		||||
                        if (msg.headers.hasOwnProperty(v)) {
 | 
			
		||||
                            var name = v.toLowerCase();
 | 
			
		||||
                            if (name !== "content-type" && name !== "content-length") {
 | 
			
		||||
                                // only normalise the known headers used later in this
 | 
			
		||||
                                // function. Otherwise leave them alone.
 | 
			
		||||
                                name = v;
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (name === 'content-type') { ctSet = v; }
 | 
			
		||||
                            else { clSet = v; }
 | 
			
		||||
                            opts.headers[name] = msg.headers[v];
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (msg.hasOwnProperty('followRedirects')) {
 | 
			
		||||
                opts.followRedirect = !!msg.followRedirects;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (opts.headers.hasOwnProperty('cookie')) {
 | 
			
		||||
                var cookies = cookie.parse(opts.headers.cookie, {decode:String});
 | 
			
		||||
                for (var name in cookies) {
 | 
			
		||||
                    opts.cookieJar.setCookie(cookie.serialize(name, cookies[name], {encode:String}), url, {ignoreError: true});
 | 
			
		||||
                }
 | 
			
		||||
                delete opts.headers.cookie;
 | 
			
		||||
            }
 | 
			
		||||
            if (msg.cookies) {
 | 
			
		||||
                for (var name in msg.cookies) {
 | 
			
		||||
                    if (msg.cookies.hasOwnProperty(name)) {
 | 
			
		||||
                        if (msg.cookies[name] === null || msg.cookies[name].value === null) {
 | 
			
		||||
                            // This case clears a cookie for HTTP In/Response nodes.
 | 
			
		||||
                            // Ignore for this node.
 | 
			
		||||
                        } else if (typeof msg.cookies[name] === 'object') {
 | 
			
		||||
                            if(msg.cookies[name].encode === false){
 | 
			
		||||
                                // If the encode option is false, the value is not encoded.
 | 
			
		||||
                                opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value, {encode: String}), url, {ignoreError: true});
 | 
			
		||||
                            } else {
 | 
			
		||||
                                // The value is encoded by encodeURIComponent().
 | 
			
		||||
                                opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value), url, {ignoreError: true});
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true});
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var parsedURL = new URL(url)
 | 
			
		||||
            this.credentials = this.credentials || {}
 | 
			
		||||
            if (parsedURL.username && !this.credentials.user) {
 | 
			
		||||
                this.credentials.user = parsedURL.username
 | 
			
		||||
            }
 | 
			
		||||
            if (parsedURL.password && !this.credentials.password) {
 | 
			
		||||
                this.credentials.password = parsedURL.password
 | 
			
		||||
            }
 | 
			
		||||
            if (Object.keys(this.credentials).length != 0) {
 | 
			
		||||
                if (this.authType === "basic") {
 | 
			
		||||
                    // Workaround for https://github.com/sindresorhus/got/issues/1169 (fixed in got v12)
 | 
			
		||||
                    // var cred = ""
 | 
			
		||||
                    if (this.credentials.user || this.credentials.password) {
 | 
			
		||||
                        // cred = `${this.credentials.user}:${this.credentials.password}`;
 | 
			
		||||
                        if (this.credentials.user === undefined) { this.credentials.user = ""}
 | 
			
		||||
                        if (this.credentials.password === undefined) { this.credentials.password = ""}
 | 
			
		||||
                        opts.headers.Authorization = "Basic " + Buffer.from(`${this.credentials.user}:${this.credentials.password}`).toString("base64");
 | 
			
		||||
                    }
 | 
			
		||||
                    // build own basic auth header
 | 
			
		||||
                    // opts.headers.Authorization = "Basic " + Buffer.from(cred).toString("base64");
 | 
			
		||||
                } else if (this.authType === "digest") {
 | 
			
		||||
                    let digestCreds = this.credentials;
 | 
			
		||||
                    let sentCreds = false;
 | 
			
		||||
                    opts.hooks.afterResponse = [(response, retry) => {
 | 
			
		||||
                        if (response.statusCode === 401) {
 | 
			
		||||
                            if (sentCreds) {
 | 
			
		||||
                                return response
 | 
			
		||||
                            }
 | 
			
		||||
                            const requestUrl = new URL(response.request.requestUrl);
 | 
			
		||||
                            const options = response.request.options;
 | 
			
		||||
                            const normalisedHeaders = {};
 | 
			
		||||
                            Object.keys(response.headers).forEach(k => {
 | 
			
		||||
                                normalisedHeaders[k.toLowerCase()] = response.headers[k]
 | 
			
		||||
                            })
 | 
			
		||||
                            if (normalisedHeaders['www-authenticate']) {
 | 
			
		||||
                                let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, options.method, requestUrl.pathname, normalisedHeaders['www-authenticate'])
 | 
			
		||||
                                options.headers.Authorization = authHeader;
 | 
			
		||||
                            }
 | 
			
		||||
                            sentCreds = true;
 | 
			
		||||
                            return retry(options);
 | 
			
		||||
                        }
 | 
			
		||||
                        return response
 | 
			
		||||
                    }];
 | 
			
		||||
                } else if (this.authType === "bearer") {
 | 
			
		||||
                    opts.headers.Authorization = `Bearer ${this.credentials.password||""}`
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var payload = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (method !== 'GET' && method !== 'HEAD' && typeof msg.payload !== "undefined") {
 | 
			
		||||
                if (opts.headers['content-type'] == 'multipart/form-data' && typeof msg.payload === "object") {
 | 
			
		||||
                    let formData = new FormData();
 | 
			
		||||
                    for (var opt in msg.payload) {
 | 
			
		||||
                        if (msg.payload.hasOwnProperty(opt)) {
 | 
			
		||||
                            var val = msg.payload[opt];
 | 
			
		||||
                            if (val !== undefined && val !== null) {
 | 
			
		||||
                                if (typeof val === 'string' || Buffer.isBuffer(val)) {
 | 
			
		||||
                                    formData.append(opt, val);
 | 
			
		||||
                                } else if (typeof val === 'object' && val.hasOwnProperty('value')) {
 | 
			
		||||
                                    formData.append(opt,val.value,val.options || {});
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    formData.append(opt,JSON.stringify(val));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    // GOT will only set the content-type header with the correct boundary
 | 
			
		||||
                    // if the header isn't set. So we delete it here, for GOT to reset it.
 | 
			
		||||
                    delete opts.headers['content-type'];
 | 
			
		||||
                    opts.body = formData;
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
 | 
			
		||||
                        payload = msg.payload;
 | 
			
		||||
                    } else if (typeof msg.payload == "number") {
 | 
			
		||||
                        payload = msg.payload+"";
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (opts.headers['content-type'] == 'application/x-www-form-urlencoded') {
 | 
			
		||||
                            payload = querystring.stringify(msg.payload);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            payload = JSON.stringify(msg.payload);
 | 
			
		||||
                            if (opts.headers['content-type'] == null) {
 | 
			
		||||
                                opts.headers[ctSet] = "application/json";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (opts.headers['content-length'] == null) {
 | 
			
		||||
                        if (Buffer.isBuffer(payload)) {
 | 
			
		||||
                            opts.headers[clSet] = payload.length;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            opts.headers[clSet] = Buffer.byteLength(payload);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    opts.body = payload;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) {
 | 
			
		||||
                if (typeof msg.payload === "object") {
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (url.indexOf("?") !== -1) {
 | 
			
		||||
                            url += (url.endsWith("?")?"":"&") + querystring.stringify(msg.payload);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            url += "?" + querystring.stringify(msg.payload);
 | 
			
		||||
                        }
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
 | 
			
		||||
                        node.error(RED._("httpin.errors.invalid-payload"),msg);
 | 
			
		||||
                        nodeDone();
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
 | 
			
		||||
                    node.error(RED._("httpin.errors.invalid-payload"),msg);
 | 
			
		||||
                    nodeDone();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) {
 | 
			
		||||
                opts.allowGetBody = true;
 | 
			
		||||
                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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // revert to user supplied Capitalisation if needed.
 | 
			
		||||
            if (opts.headers.hasOwnProperty('content-type') && (ctSet !== 'content-type')) {
 | 
			
		||||
                opts.headers[ctSet] = opts.headers['content-type'];
 | 
			
		||||
                delete opts.headers['content-type'];
 | 
			
		||||
            }
 | 
			
		||||
            if (opts.headers.hasOwnProperty('content-length') && (clSet !== 'content-length')) {
 | 
			
		||||
                opts.headers[clSet] = opts.headers['content-length'];
 | 
			
		||||
                delete opts.headers['content-length'];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var noproxy;
 | 
			
		||||
            if (noprox) {
 | 
			
		||||
                for (var i = 0; i < noprox.length; i += 1) {
 | 
			
		||||
                    if (url.indexOf(noprox[i]) !== -1) { noproxy=true; }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (prox && !noproxy) {
 | 
			
		||||
                var match = prox.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i);
 | 
			
		||||
                if (match) {
 | 
			
		||||
                    let proxyAgent;
 | 
			
		||||
                    let proxyURL = new URL(prox);
 | 
			
		||||
                    //set username/password to null to stop empty creds header
 | 
			
		||||
                    let proxyOptions = {
 | 
			
		||||
                        proxy: {
 | 
			
		||||
                            protocol: proxyURL.protocol,
 | 
			
		||||
                            hostname: proxyURL.hostname,
 | 
			
		||||
                            port: proxyURL.port,
 | 
			
		||||
                            username: null,
 | 
			
		||||
                            password: null
 | 
			
		||||
                        },
 | 
			
		||||
                        maxFreeSockets: 256,
 | 
			
		||||
                        maxSockets: 256,
 | 
			
		||||
                        keepAlive: true
 | 
			
		||||
                    }
 | 
			
		||||
                    if (proxyConfig && proxyConfig.credentials) {
 | 
			
		||||
                        let proxyUsername = proxyConfig.credentials.username || '';
 | 
			
		||||
                        let proxyPassword = proxyConfig.credentials.password || '';
 | 
			
		||||
                        if (proxyUsername || proxyPassword) {
 | 
			
		||||
                            proxyOptions.proxy.username = proxyUsername;
 | 
			
		||||
                            proxyOptions.proxy.password = proxyPassword;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (proxyURL.username || proxyURL.password){
 | 
			
		||||
                        proxyOptions.proxy.username = proxyURL.username;
 | 
			
		||||
                        proxyOptions.proxy.password = proxyURL.password;
 | 
			
		||||
                    }
 | 
			
		||||
                    //need both incase of http -> https redirect
 | 
			
		||||
                    opts.agent = {
 | 
			
		||||
                        http: new HttpProxyAgent(proxyOptions),
 | 
			
		||||
                        https: new HttpsProxyAgent(proxyOptions)
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    node.warn("Bad proxy url: "+ prox);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (tlsNode) {
 | 
			
		||||
                opts.https = {};
 | 
			
		||||
                tlsNode.addTLSOptions(opts.https);
 | 
			
		||||
                if (opts.https.ca) {
 | 
			
		||||
                    opts.https.certificateAuthority = opts.https.ca;
 | 
			
		||||
                    delete opts.https.ca;
 | 
			
		||||
                }
 | 
			
		||||
                if (opts.https.cert) {
 | 
			
		||||
                    opts.https.certificate = opts.https.cert;
 | 
			
		||||
                    delete opts.https.cert;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (msg.hasOwnProperty('rejectUnauthorized')) {
 | 
			
		||||
                    opts.https = { rejectUnauthorized: msg.rejectUnauthorized };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Now we have established all of our own headers, take a snapshot
 | 
			
		||||
            // of their case so we can restore it prior to the request being sent.
 | 
			
		||||
            if (opts.headers) {
 | 
			
		||||
                Object.keys(opts.headers).forEach(h => {
 | 
			
		||||
                    originalHeaderMap[h.toLowerCase()] = h
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            got(url,opts).then(res => {
 | 
			
		||||
                msg.statusCode = res.statusCode;
 | 
			
		||||
                msg.headers = res.headers;
 | 
			
		||||
                msg.responseUrl = res.url;
 | 
			
		||||
                msg.payload = res.body;
 | 
			
		||||
                msg.redirectList = redirectList;
 | 
			
		||||
                msg.retry = 0;
 | 
			
		||||
 | 
			
		||||
                if (msg.headers.hasOwnProperty('set-cookie')) {
 | 
			
		||||
                    msg.responseCookies = extractCookies(msg.headers['set-cookie']);
 | 
			
		||||
                }
 | 
			
		||||
                msg.headers['x-node-red-request-node'] = hashSum(msg.headers);
 | 
			
		||||
                // msg.url = url;   // revert when warning above finally removed
 | 
			
		||||
                if (node.metric()) {
 | 
			
		||||
                    // Calculate request time
 | 
			
		||||
                    var diff = process.hrtime(preRequestTimestamp);
 | 
			
		||||
                    var ms = diff[0] * 1e3 + diff[1] * 1e-6;
 | 
			
		||||
                    var metricRequestDurationMillis = ms.toFixed(3);
 | 
			
		||||
                    node.metric("duration.millis", msg, metricRequestDurationMillis);
 | 
			
		||||
                    if (res.client && res.client.bytesRead) {
 | 
			
		||||
                        node.metric("size.bytes", msg, res.client.bytesRead);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (timingLog) {
 | 
			
		||||
                        emitTimingMetricLog(res.timings, msg);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Convert the payload to the required return type
 | 
			
		||||
                if (node.ret !== "bin") {
 | 
			
		||||
                    msg.payload = msg.payload.toString('utf8'); // txt
 | 
			
		||||
 | 
			
		||||
                    if (node.ret === "obj") {
 | 
			
		||||
                        try { msg.payload = JSON.parse(msg.payload); } // obj
 | 
			
		||||
                        catch(e) { node.warn(RED._("httpin.errors.json-error")); }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                node.status({});
 | 
			
		||||
                nodeSend(msg);
 | 
			
		||||
                nodeDone();
 | 
			
		||||
            }).catch(err => {
 | 
			
		||||
                // Pre 2.1, any errors would be sent to both Catch node and sent on as normal.
 | 
			
		||||
                // This is not ideal but is the legacy behaviour of the node.
 | 
			
		||||
                // 2.1 adds the 'senderr' option, if set to true, will *only* send errors
 | 
			
		||||
                // to Catch nodes. If false, it still does both behaviours.
 | 
			
		||||
                // TODO: 3.0 - make it one or the other.
 | 
			
		||||
 | 
			
		||||
                if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') {
 | 
			
		||||
                    node.error(RED._("common.notification.errors.no-response"), msg);
 | 
			
		||||
                    node.status({fill:"red", shape:"ring", text:"common.notification.errors.no-response"});
 | 
			
		||||
                } else {
 | 
			
		||||
                    node.error(err,msg);
 | 
			
		||||
                    node.status({fill:"red", shape:"ring", text:err.code});
 | 
			
		||||
                }
 | 
			
		||||
                msg.payload = err.toString() + " : " + url;
 | 
			
		||||
                msg.statusCode = err.code || (err.response?err.response.statusCode:undefined);
 | 
			
		||||
                if (node.metric() && timingLog) {
 | 
			
		||||
                    emitTimingMetricLog(err.timings, msg);
 | 
			
		||||
                }
 | 
			
		||||
                if (!sendErrorsToCatch) {
 | 
			
		||||
                    nodeSend(msg);
 | 
			
		||||
                }
 | 
			
		||||
                nodeDone();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.on("close",function() {
 | 
			
		||||
            node.status({});
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        function emitTimingMetricLog(timings, msg) {
 | 
			
		||||
            const props = [
 | 
			
		||||
                "start",
 | 
			
		||||
                "socket",
 | 
			
		||||
                "lookup",
 | 
			
		||||
                "connect",
 | 
			
		||||
                "secureConnect",
 | 
			
		||||
                "upload",
 | 
			
		||||
                "response",
 | 
			
		||||
                "end",
 | 
			
		||||
                "error",
 | 
			
		||||
                "abort"
 | 
			
		||||
            ];
 | 
			
		||||
            if (timings) {
 | 
			
		||||
                props.forEach(p => {
 | 
			
		||||
                    if (timings[p]) {
 | 
			
		||||
                        node.metric(`timings.${p}`, msg, timings[p]);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function extractCookies(setCookie) {
 | 
			
		||||
            var cookies = {};
 | 
			
		||||
            setCookie.forEach(function(c) {
 | 
			
		||||
                var parsedCookie = cookie.parse(c);
 | 
			
		||||
                var eq_idx = c.indexOf('=');
 | 
			
		||||
                var key = c.substr(0, eq_idx).trim()
 | 
			
		||||
                parsedCookie.value = parsedCookie[key];
 | 
			
		||||
                delete parsedCookie[key];
 | 
			
		||||
                cookies[key] = parsedCookie;
 | 
			
		||||
            });
 | 
			
		||||
            return cookies;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RED.nodes.registerType("http request",HTTPRequest,{
 | 
			
		||||
        credentials: {
 | 
			
		||||
            user: {type:"text"},
 | 
			
		||||
            password: {type: "password"}
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const md5 = (value) => { return crypto.createHash('md5').update(value).digest('hex') }
 | 
			
		||||
 | 
			
		||||
    function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
 | 
			
		||||
        /**
 | 
			
		||||
        * RFC 2617: handle both MD5 and MD5-sess algorithms.
 | 
			
		||||
        *
 | 
			
		||||
        * If the algorithm directive's value is "MD5" or unspecified, then HA1 is
 | 
			
		||||
        *   HA1=MD5(username:realm:password)
 | 
			
		||||
        * If the algorithm directive's value is "MD5-sess", then HA1 is
 | 
			
		||||
        *   HA1=MD5(MD5(username:realm:password):nonce:cnonce)
 | 
			
		||||
        */
 | 
			
		||||
        var ha1 = md5(user + ':' + realm + ':' + pass)
 | 
			
		||||
        if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
 | 
			
		||||
            return md5(ha1 + ':' + nonce + ':' + cnonce)
 | 
			
		||||
        } else {
 | 
			
		||||
            return ha1
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function buildDigestHeader(user, pass, method, path, authHeader) {
 | 
			
		||||
        var challenge = {}
 | 
			
		||||
        var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
 | 
			
		||||
        for (;;) {
 | 
			
		||||
            var match = re.exec(authHeader)
 | 
			
		||||
            if (!match) {
 | 
			
		||||
                break
 | 
			
		||||
            }
 | 
			
		||||
            challenge[match[1]] = match[2] || match[3]
 | 
			
		||||
        }
 | 
			
		||||
        var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
 | 
			
		||||
        var nc = qop && '00000001'
 | 
			
		||||
        var cnonce = qop && uuid().replace(/-/g, '')
 | 
			
		||||
        var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce)
 | 
			
		||||
        var ha2 = md5(method + ':' + path)
 | 
			
		||||
        var digestResponse = qop
 | 
			
		||||
        ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
 | 
			
		||||
        : md5(ha1 + ':' + challenge.nonce + ':' + ha2)
 | 
			
		||||
        var authValues = {
 | 
			
		||||
            username: user,
 | 
			
		||||
            realm: challenge.realm,
 | 
			
		||||
            nonce: challenge.nonce,
 | 
			
		||||
            uri: path,
 | 
			
		||||
            qop: qop,
 | 
			
		||||
            response: digestResponse,
 | 
			
		||||
            nc: nc,
 | 
			
		||||
            cnonce: cnonce,
 | 
			
		||||
            algorithm: challenge.algorithm,
 | 
			
		||||
            opaque: challenge.opaque
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        authHeader = []
 | 
			
		||||
        for (var k in authValues) {
 | 
			
		||||
            if (authValues[k]) {
 | 
			
		||||
                if (k === 'qop' || k === 'nc' || k === 'algorithm') {
 | 
			
		||||
                    authHeader.push(k + '=' + authValues[k])
 | 
			
		||||
                } else {
 | 
			
		||||
                    authHeader.push(k + '="' + authValues[k] + '"')
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        authHeader = 'Digest ' + authHeader.join(', ')
 | 
			
		||||
        return authHeader
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										417
									
								
								packages/node_modules/@node-red/nodes/core/network/22-websocket.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										417
									
								
								packages/node_modules/@node-red/nodes/core/network/22-websocket.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,417 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
module.exports = function(RED) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
    var ws = require("ws");
 | 
			
		||||
    var inspect = require("util").inspect;
 | 
			
		||||
    var url = require("url");
 | 
			
		||||
    var HttpsProxyAgent = require('https-proxy-agent');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var serverUpgradeAdded = false;
 | 
			
		||||
    function handleServerUpgrade(request, socket, head) {
 | 
			
		||||
        const pathname = url.parse(request.url).pathname;
 | 
			
		||||
        if (listenerNodes.hasOwnProperty(pathname)) {
 | 
			
		||||
            listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done(ws) {
 | 
			
		||||
                listenerNodes[pathname].server.emit('connection', ws, request);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            // Don't destroy the socket as other listeners may want to handle the
 | 
			
		||||
            // event.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    var listenerNodes = {};
 | 
			
		||||
    var activeListenerNodes = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // A node red node that sets up a local websocket server
 | 
			
		||||
    function WebSocketListenerNode(n) {
 | 
			
		||||
        // Create a RED node
 | 
			
		||||
        RED.nodes.createNode(this,n);
 | 
			
		||||
        var node = this;
 | 
			
		||||
 | 
			
		||||
        // Store local copies of the node configuration (as defined in the .html)
 | 
			
		||||
        node.path = n.path;
 | 
			
		||||
        node.wholemsg = (n.wholemsg === "true");
 | 
			
		||||
 | 
			
		||||
        node._inputNodes = [];    // collection of nodes that want to receive events
 | 
			
		||||
        node._clients = {};
 | 
			
		||||
        // match absolute url
 | 
			
		||||
        node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
 | 
			
		||||
        node.closing = false;
 | 
			
		||||
        node.tls = n.tls;
 | 
			
		||||
 | 
			
		||||
        if (n.hb) {
 | 
			
		||||
            var heartbeat = parseInt(n.hb);
 | 
			
		||||
            if (heartbeat > 0) {
 | 
			
		||||
                node.heartbeat = heartbeat * 1000;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function startconn() {    // Connect to remote endpoint
 | 
			
		||||
            node.tout = null;
 | 
			
		||||
            var prox, noprox;
 | 
			
		||||
            if (process.env.http_proxy) { prox = process.env.http_proxy; }
 | 
			
		||||
            if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
 | 
			
		||||
            if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); }
 | 
			
		||||
            if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); }
 | 
			
		||||
 | 
			
		||||
            var noproxy = false;
 | 
			
		||||
            if (noprox) {
 | 
			
		||||
                for (var i in noprox) {
 | 
			
		||||
                    if (node.path.indexOf(noprox[i].trim()) !== -1) { noproxy=true; }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var agent = undefined;
 | 
			
		||||
            if (prox && !noproxy) {
 | 
			
		||||
                agent = new HttpsProxyAgent(prox);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var options = {};
 | 
			
		||||
            if (agent) {
 | 
			
		||||
                options.agent = agent;
 | 
			
		||||
            }
 | 
			
		||||
            if (node.tls) {
 | 
			
		||||
                var tlsNode = RED.nodes.getNode(node.tls);
 | 
			
		||||
                if (tlsNode) {
 | 
			
		||||
                    tlsNode.addTLSOptions(options);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            var socket = new ws(node.path,options);
 | 
			
		||||
            socket.setMaxListeners(0);
 | 
			
		||||
            node.server = socket; // keep for closing
 | 
			
		||||
            handleConnection(socket);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function handleConnection(/*socket*/socket) {
 | 
			
		||||
            var id = RED.util.generateId();
 | 
			
		||||
            socket.nrId = id;
 | 
			
		||||
            socket.nrPendingHeartbeat = false;
 | 
			
		||||
            if (node.isServer) {
 | 
			
		||||
                node._clients[id] = socket;
 | 
			
		||||
                node.emit('opened',{count:Object.keys(node._clients).length,id:id});
 | 
			
		||||
            }
 | 
			
		||||
            socket.on('open',function() {
 | 
			
		||||
                if (!node.isServer) {
 | 
			
		||||
                    if (node.heartbeat) {
 | 
			
		||||
                        clearInterval(node.heartbeatInterval);
 | 
			
		||||
                        node.heartbeatInterval = setInterval(function() {
 | 
			
		||||
                            if (socket.nrPendingHeartbeat) {
 | 
			
		||||
                                // No pong received
 | 
			
		||||
                                socket.terminate();
 | 
			
		||||
                                socket.nrErrorHandler(new Error("timeout"));
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                            socket.nrPendingHeartbeat = true;
 | 
			
		||||
                            try {
 | 
			
		||||
                                socket.ping();
 | 
			
		||||
                            } catch(err) {}
 | 
			
		||||
                        },node.heartbeat);
 | 
			
		||||
                    }
 | 
			
		||||
                    node.emit('opened',{count:'',id:id});
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            socket.on('close',function() {
 | 
			
		||||
                clearInterval(node.heartbeatInterval);
 | 
			
		||||
                if (node.isServer) {
 | 
			
		||||
                    delete node._clients[id];
 | 
			
		||||
                    node.emit('closed',{count:Object.keys(node._clients).length,id:id});
 | 
			
		||||
                } else {
 | 
			
		||||
                    node.emit('closed',{count:'',id:id});
 | 
			
		||||
                }
 | 
			
		||||
                if (!node.closing && !node.isServer) {
 | 
			
		||||
                    clearTimeout(node.tout);
 | 
			
		||||
                    node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            socket.on('message',function(data,flags) {
 | 
			
		||||
                node.handleEvent(id,socket,'message',data,flags);
 | 
			
		||||
            });
 | 
			
		||||
            socket.nrErrorHandler = function(err) {
 | 
			
		||||
                clearInterval(node.heartbeatInterval);
 | 
			
		||||
                node.emit('erro',{err:err,id:id});
 | 
			
		||||
                if (!node.closing && !node.isServer) {
 | 
			
		||||
                    clearTimeout(node.tout);
 | 
			
		||||
                    node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            socket.on('error',socket.nrErrorHandler);
 | 
			
		||||
            socket.on('ping', function() {
 | 
			
		||||
                socket.nrPendingHeartbeat = false;
 | 
			
		||||
            })
 | 
			
		||||
            socket.on('pong', function() {
 | 
			
		||||
                socket.nrPendingHeartbeat = false;
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (node.isServer) {
 | 
			
		||||
            activeListenerNodes++;
 | 
			
		||||
            if (!serverUpgradeAdded) {
 | 
			
		||||
                RED.server.on('upgrade', handleServerUpgrade);
 | 
			
		||||
                serverUpgradeAdded = true
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var path = RED.settings.httpNodeRoot || "/";
 | 
			
		||||
            path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
 | 
			
		||||
            node.fullPath = path;
 | 
			
		||||
 | 
			
		||||
            if (listenerNodes.hasOwnProperty(path)) {
 | 
			
		||||
                node.error(RED._("websocket.errors.duplicate-path",{path: node.path}));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            listenerNodes[node.fullPath] = node;
 | 
			
		||||
            var serverOptions = {
 | 
			
		||||
                noServer: true
 | 
			
		||||
            }
 | 
			
		||||
            if (RED.settings.webSocketNodeVerifyClient) {
 | 
			
		||||
                serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient;
 | 
			
		||||
            }
 | 
			
		||||
            // Create a WebSocket Server
 | 
			
		||||
            node.server = new ws.Server(serverOptions);
 | 
			
		||||
            node.server.setMaxListeners(0);
 | 
			
		||||
            node.server.on('connection', handleConnection);
 | 
			
		||||
            // Not adding server-initiated heartbeats yet
 | 
			
		||||
            // node.heartbeatInterval = setInterval(function() {
 | 
			
		||||
            //     node.server.clients.forEach(function(ws) {
 | 
			
		||||
            //         if (ws.nrPendingHeartbeat) {
 | 
			
		||||
            //             // No pong received
 | 
			
		||||
            //             ws.terminate();
 | 
			
		||||
            //             ws.nrErrorHandler(new Error("timeout"));
 | 
			
		||||
            //             return;
 | 
			
		||||
            //         }
 | 
			
		||||
            //         ws.nrPendingHeartbeat = true;
 | 
			
		||||
            //         ws.ping();
 | 
			
		||||
            //     });
 | 
			
		||||
            // })
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            node.closing = false;
 | 
			
		||||
            startconn(); // start outbound connection
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        node.on("close", function() {
 | 
			
		||||
            if (node.heartbeatInterval) {
 | 
			
		||||
                clearInterval(node.heartbeatInterval);
 | 
			
		||||
            }
 | 
			
		||||
            if (node.isServer) {
 | 
			
		||||
                delete listenerNodes[node.fullPath];
 | 
			
		||||
                node.server.close();
 | 
			
		||||
                node._inputNodes = [];
 | 
			
		||||
                activeListenerNodes--;
 | 
			
		||||
                // if (activeListenerNodes === 0 && serverUpgradeAdded) {
 | 
			
		||||
                //     RED.server.removeListener('upgrade', handleServerUpgrade);
 | 
			
		||||
                //     serverUpgradeAdded = false;
 | 
			
		||||
                // }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                node.closing = true;
 | 
			
		||||
                node.server.close();
 | 
			
		||||
                if (node.tout) {
 | 
			
		||||
                    clearTimeout(node.tout);
 | 
			
		||||
                    node.tout = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
 | 
			
		||||
    RED.nodes.registerType("websocket-client",WebSocketListenerNode);
 | 
			
		||||
 | 
			
		||||
    WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) {
 | 
			
		||||
        this._inputNodes.push(handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketListenerNode.prototype.removeInputNode = function(/*Node*/handler) {
 | 
			
		||||
        this._inputNodes.forEach(function(node, i, inputNodes) {
 | 
			
		||||
            if (node === handler) {
 | 
			
		||||
                inputNodes.splice(i, 1);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags) {
 | 
			
		||||
        var msg;
 | 
			
		||||
        if (this.wholemsg) {
 | 
			
		||||
            try {
 | 
			
		||||
                msg = JSON.parse(data);
 | 
			
		||||
                if (typeof msg !== "object" && !Array.isArray(msg) && (msg !== null)) {
 | 
			
		||||
                    msg = { payload:msg };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch(err) {
 | 
			
		||||
                msg = { payload:data };
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            msg = {
 | 
			
		||||
                payload:data
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        msg._session = {type:"websocket",id:id};
 | 
			
		||||
        for (var i = 0; i < this._inputNodes.length; i++) {
 | 
			
		||||
            this._inputNodes[i].send(msg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketListenerNode.prototype.broadcast = function(data) {
 | 
			
		||||
        if (this.isServer) {
 | 
			
		||||
            for (let client in this._clients) {
 | 
			
		||||
                if (this._clients.hasOwnProperty(client)) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        this._clients[client].send(data);
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
                        this.warn(RED._("websocket.errors.send-error")+" "+client+" "+err.toString())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            try {
 | 
			
		||||
                this.server.send(data);
 | 
			
		||||
            } catch(err) {
 | 
			
		||||
                this.warn(RED._("websocket.errors.send-error")+" "+err.toString())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketListenerNode.prototype.reply = function(id,data) {
 | 
			
		||||
        var session = this._clients[id];
 | 
			
		||||
        if (session) {
 | 
			
		||||
            try {
 | 
			
		||||
                session.send(data);
 | 
			
		||||
            }
 | 
			
		||||
            catch(e) { // swallow any errors
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function WebSocketInNode(n) {
 | 
			
		||||
        RED.nodes.createNode(this,n);
 | 
			
		||||
        this.server = (n.client)?n.client:n.server;
 | 
			
		||||
        var node = this;
 | 
			
		||||
        this.serverConfig = RED.nodes.getNode(this.server);
 | 
			
		||||
        if (this.serverConfig) {
 | 
			
		||||
            this.serverConfig.registerInputNode(this);
 | 
			
		||||
            // TODO: nls
 | 
			
		||||
            this.serverConfig.on('opened', function(event) {
 | 
			
		||||
                node.status({
 | 
			
		||||
                    fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}),
 | 
			
		||||
                    event:"connect",
 | 
			
		||||
                    _session: {type:"websocket",id:event.id}
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            this.serverConfig.on('erro', function(event) {
 | 
			
		||||
                node.status({
 | 
			
		||||
                    fill:"red",shape:"ring",text:"common.status.error",
 | 
			
		||||
                    event:"error",
 | 
			
		||||
                    _session: {type:"websocket",id:event.id}
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            this.serverConfig.on('closed', function(event) {
 | 
			
		||||
                var status;
 | 
			
		||||
                if (event.count > 0) {
 | 
			
		||||
                    status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})};
 | 
			
		||||
                } else {
 | 
			
		||||
                    status = {fill:"red",shape:"ring",text:"common.status.disconnected"};
 | 
			
		||||
                }
 | 
			
		||||
                status.event = "disconnect";
 | 
			
		||||
                status._session = {type:"websocket",id:event.id}
 | 
			
		||||
                node.status(status);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.error(RED._("websocket.errors.missing-conf"));
 | 
			
		||||
        }
 | 
			
		||||
        this.on('close', function() {
 | 
			
		||||
            if (node.serverConfig) {
 | 
			
		||||
                node.serverConfig.removeInputNode(node);
 | 
			
		||||
            }
 | 
			
		||||
            node.status({});
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType("websocket in",WebSocketInNode);
 | 
			
		||||
 | 
			
		||||
    function WebSocketOutNode(n) {
 | 
			
		||||
        RED.nodes.createNode(this,n);
 | 
			
		||||
        var node = this;
 | 
			
		||||
        this.server = (n.client)?n.client:n.server;
 | 
			
		||||
        this.serverConfig = RED.nodes.getNode(this.server);
 | 
			
		||||
        if (!this.serverConfig) {
 | 
			
		||||
            return this.error(RED._("websocket.errors.missing-conf"));
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // TODO: nls
 | 
			
		||||
            this.serverConfig.on('opened', function(event) {
 | 
			
		||||
                node.status({
 | 
			
		||||
                    fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}),
 | 
			
		||||
                    event:"connect",
 | 
			
		||||
                    _session: {type:"websocket",id:event.id}
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            this.serverConfig.on('erro', function(event) {
 | 
			
		||||
                node.status({
 | 
			
		||||
                    fill:"red",shape:"ring",text:"common.status.error",
 | 
			
		||||
                    event:"error",
 | 
			
		||||
                    _session: {type:"websocket",id:event.id}
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
            this.serverConfig.on('closed', function(event) {
 | 
			
		||||
                var status;
 | 
			
		||||
                if (event.count > 0) {
 | 
			
		||||
                    status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})};
 | 
			
		||||
                } else {
 | 
			
		||||
                    status = {fill:"red",shape:"ring",text:"common.status.disconnected"};
 | 
			
		||||
                }
 | 
			
		||||
                status.event = "disconnect";
 | 
			
		||||
                status._session = {type:"websocket",id:event.id}
 | 
			
		||||
                node.status(status);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        this.on("input", function(msg, nodeSend, nodeDone) {
 | 
			
		||||
            var payload;
 | 
			
		||||
            if (this.serverConfig.wholemsg) {
 | 
			
		||||
                var sess;
 | 
			
		||||
                if (msg._session) { sess = JSON.stringify(msg._session); }
 | 
			
		||||
                delete msg._session;
 | 
			
		||||
                payload = JSON.stringify(msg);
 | 
			
		||||
                if (sess) { msg._session = JSON.parse(sess); }
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg.hasOwnProperty("payload")) {
 | 
			
		||||
                if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string.
 | 
			
		||||
                    payload = RED.util.ensureString(msg.payload);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    payload = msg.payload;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (payload) {
 | 
			
		||||
                if (msg._session && msg._session.type == "websocket") {
 | 
			
		||||
                    node.serverConfig.reply(msg._session.id,payload);
 | 
			
		||||
                } else {
 | 
			
		||||
                    node.serverConfig.broadcast(payload,function(error) {
 | 
			
		||||
                        if (!!error) {
 | 
			
		||||
                            node.warn(RED._("websocket.errors.send-error")+inspect(error));
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            nodeDone();
 | 
			
		||||
        });
 | 
			
		||||
        this.on('close', function() {
 | 
			
		||||
            node.status({});
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    RED.nodes.registerType("websocket out",WebSocketOutNode);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								packages/node_modules/@node-red/nodes/examples/common/link/03 - Link call.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								packages/node_modules/@node-red/nodes/examples/common/link/03 - Link call.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "62ea32aa.d73aac",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Example: Link Call Node",
 | 
			
		||||
        "info": "Link call node can call link in node then get result from link out node.",
 | 
			
		||||
        "x": 230,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "c588bc36.87fec",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓ call link in node",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 440,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "cd31efb4d2c6967e",
 | 
			
		||||
        "type": "link call",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "links": [
 | 
			
		||||
            "dbc46892c8d14c37"
 | 
			
		||||
        ],
 | 
			
		||||
        "timeout": "30",
 | 
			
		||||
        "x": 420,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "c3db64d1d2260340"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "dbc46892c8d14c37",
 | 
			
		||||
        "type": "link in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "links": [],
 | 
			
		||||
        "x": 315,
 | 
			
		||||
        "y": 340,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "e10575d73f2e5352"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "6b61792143b3b0a3",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "",
 | 
			
		||||
        "payloadType": "date",
 | 
			
		||||
        "x": 240,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "cd31efb4d2c6967e"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "e10575d73f2e5352",
 | 
			
		||||
        "type": "change",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "rules": [
 | 
			
		||||
            {
 | 
			
		||||
                "t": "set",
 | 
			
		||||
                "p": "payload",
 | 
			
		||||
                "pt": "msg",
 | 
			
		||||
                "to": "Hello, World!",
 | 
			
		||||
                "tot": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "action": "",
 | 
			
		||||
        "property": "",
 | 
			
		||||
        "from": "",
 | 
			
		||||
        "to": "",
 | 
			
		||||
        "reg": false,
 | 
			
		||||
        "x": 450,
 | 
			
		||||
        "y": 340,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "cf8438e7137bc0f0"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "cf8438e7137bc0f0",
 | 
			
		||||
        "type": "link out",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "mode": "return",
 | 
			
		||||
        "links": [],
 | 
			
		||||
        "x": 595,
 | 
			
		||||
        "y": 340,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "c3db64d1d2260340",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 600,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "6d077dfa0987febb",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑called from link call node",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 410,
 | 
			
		||||
        "y": 380,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "53b9a0adfd8c4217",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑return to link call node",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 680,
 | 
			
		||||
        "y": 380,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										113
									
								
								packages/node_modules/@node-red/nodes/examples/storage/read file/01 - Read string from a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								packages/node_modules/@node-red/nodes/examples/storage/read file/01 - Read string from a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "84222b92.d65d18",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "Hello, World!",
 | 
			
		||||
        "payloadType": "str",
 | 
			
		||||
        "x": 190,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "b4b9f603.739598"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "7b014430.dfd94c",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Write string to a file, then read from the file",
 | 
			
		||||
        "info": "Read file node can read string from a file.",
 | 
			
		||||
        "x": 220,
 | 
			
		||||
        "y": 100,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "b4b9f603.739598",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 380,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "6dc01cac.5c4bf4"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "2587adb9.7e60f2",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 770,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "6dc01cac.5c4bf4",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "utf8",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 580,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "2587adb9.7e60f2"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "f4b4309a.3b78a",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read result from file",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 590,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "672d3693.3cabd8",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write to /tmp/hello.txt",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 400,
 | 
			
		||||
        "y": 140,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -0,0 +1,114 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "8997398f.c5d628",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "😀",
 | 
			
		||||
        "payloadType": "str",
 | 
			
		||||
        "x": 170,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "56e32d23.050f44"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "4e598e65.1799d",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Read data in specified encoding",
 | 
			
		||||
        "info": "Read file node can specify encoding of data read from a file.",
 | 
			
		||||
        "x": 190,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "56e32d23.050f44",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 340,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "38fa0579.f2cd8a"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "d28c8994.99c0a8",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 730,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "38fa0579.f2cd8a",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "utf8",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "base64",
 | 
			
		||||
        "allProps": false,
 | 
			
		||||
        "x": 540,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "d28c8994.99c0a8"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "fa22ca20.ae4528",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read data from file as base64 string",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 600,
 | 
			
		||||
        "y": 300,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "148e25ad.98891a",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write to /tmp/hello.txt",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 360,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -0,0 +1,132 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "6a0b1d03.d4cee4",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "",
 | 
			
		||||
        "payloadType": "date",
 | 
			
		||||
        "x": 160,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "d4b00cb7.a5a23"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "f17ea1d1.8ecc3",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Read data breaking lines into individual messages",
 | 
			
		||||
        "info": "Read file node can break read text into messages with individual lines",
 | 
			
		||||
        "x": 230,
 | 
			
		||||
        "y": 140,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "99ae7806.1d6428",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 480,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "70d7892f.d27db8"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "7ed8282c.92b338",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 750,
 | 
			
		||||
        "y": 280,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "70d7892f.d27db8",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "lines",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 560,
 | 
			
		||||
        "y": 280,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "7ed8282c.92b338"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "c1b7e05.1d94b2",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read data from file breaking lines into messages",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 660,
 | 
			
		||||
        "y": 320,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "a5f647b2.cf27a8",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write to /tmp/hello.txt",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 500,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "d4b00cb7.a5a23",
 | 
			
		||||
        "type": "template",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "data",
 | 
			
		||||
        "field": "payload",
 | 
			
		||||
        "fieldType": "msg",
 | 
			
		||||
        "format": "handlebars",
 | 
			
		||||
        "syntax": "plain",
 | 
			
		||||
        "template": "one\ntwo\nthree!",
 | 
			
		||||
        "output": "str",
 | 
			
		||||
        "x": 310,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "99ae7806.1d6428"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "bdd57acc.2edc48",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "",
 | 
			
		||||
        "payloadType": "date",
 | 
			
		||||
        "x": 220,
 | 
			
		||||
        "y": 1040,
 | 
			
		||||
        "x": 180,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "7a069b01.0c2324"
 | 
			
		||||
@@ -31,25 +31,25 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "1fd12220.33953e",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Creating a message stream from lines of data",
 | 
			
		||||
        "info": "File-in node can break read text into messages with individual lines.  The messages creates a stream of messages.",
 | 
			
		||||
        "x": 270,
 | 
			
		||||
        "y": 960,
 | 
			
		||||
        "info": "Read file node can break read text into messages with individual lines.  The messages creates a stream of messages.",
 | 
			
		||||
        "x": 230,
 | 
			
		||||
        "y": 140,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "ab6eb213.2a08d",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 540,
 | 
			
		||||
        "y": 1040,
 | 
			
		||||
        "x": 500,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "b7ed49b0.649fb8"
 | 
			
		||||
@@ -59,7 +59,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "c48d8ae0.9ff3a8",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
@@ -68,22 +68,22 @@
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 810,
 | 
			
		||||
        "y": 1140,
 | 
			
		||||
        "x": 770,
 | 
			
		||||
        "y": 320,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "b7ed49b0.649fb8",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "lines",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 280,
 | 
			
		||||
        "y": 1140,
 | 
			
		||||
        "x": 240,
 | 
			
		||||
        "y": 320,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "83073ebe.fcce4"
 | 
			
		||||
@@ -93,27 +93,27 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "3c33e69f.6a04ba",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read data from file breaking lines into messages",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 380,
 | 
			
		||||
        "y": 1180,
 | 
			
		||||
        "x": 340,
 | 
			
		||||
        "y": 360,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "3598bf7d.5712a",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write to /tmp/hello.txt",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 560,
 | 
			
		||||
        "y": 1000,
 | 
			
		||||
        "x": 520,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "7a069b01.0c2324",
 | 
			
		||||
        "type": "template",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "data",
 | 
			
		||||
        "field": "payload",
 | 
			
		||||
        "fieldType": "msg",
 | 
			
		||||
@@ -121,8 +121,8 @@
 | 
			
		||||
        "syntax": "plain",
 | 
			
		||||
        "template": "Apple\nBanana\nGrape\nOrange",
 | 
			
		||||
        "output": "str",
 | 
			
		||||
        "x": 370,
 | 
			
		||||
        "y": 1040,
 | 
			
		||||
        "x": 330,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "ab6eb213.2a08d"
 | 
			
		||||
@@ -132,7 +132,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "8d4ed1d0.821fe",
 | 
			
		||||
        "type": "join",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "mode": "auto",
 | 
			
		||||
        "build": "string",
 | 
			
		||||
@@ -145,8 +145,8 @@
 | 
			
		||||
        "timeout": "",
 | 
			
		||||
        "count": "",
 | 
			
		||||
        "reduceRight": false,
 | 
			
		||||
        "x": 630,
 | 
			
		||||
        "y": 1140,
 | 
			
		||||
        "x": 590,
 | 
			
		||||
        "y": 320,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "c48d8ae0.9ff3a8"
 | 
			
		||||
@@ -156,7 +156,7 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "83073ebe.fcce4",
 | 
			
		||||
        "type": "switch",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "< D",
 | 
			
		||||
        "property": "payload",
 | 
			
		||||
        "propertyType": "msg",
 | 
			
		||||
@@ -170,8 +170,8 @@
 | 
			
		||||
        "checkall": "true",
 | 
			
		||||
        "repair": true,
 | 
			
		||||
        "outputs": 1,
 | 
			
		||||
        "x": 470,
 | 
			
		||||
        "y": 1140,
 | 
			
		||||
        "x": 430,
 | 
			
		||||
        "y": 320,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "8d4ed1d0.821fe"
 | 
			
		||||
@@ -181,21 +181,21 @@
 | 
			
		||||
    {
 | 
			
		||||
        "id": "2088e195.f7aebe",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓filter data before \"D\"",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 520,
 | 
			
		||||
        "y": 1100,
 | 
			
		||||
        "x": 480,
 | 
			
		||||
        "y": 280,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "b848cdc7.61e06",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "194a3e4f.a92772",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑join to single string",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 670,
 | 
			
		||||
        "y": 1180,
 | 
			
		||||
        "x": 630,
 | 
			
		||||
        "y": 360,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										113
									
								
								packages/node_modules/@node-red/nodes/examples/storage/write file/01 - Write string to a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								packages/node_modules/@node-red/nodes/examples/storage/write file/01 - Write string to a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "84222b92.d65d18",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "Hello, World!",
 | 
			
		||||
        "payloadType": "str",
 | 
			
		||||
        "x": 150,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "b4b9f603.739598"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "7b014430.dfd94c",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "Write string to a file, then read from the file",
 | 
			
		||||
        "info": "Write file node can write string from a file.",
 | 
			
		||||
        "x": 180,
 | 
			
		||||
        "y": 140,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "b4b9f603.739598",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 340,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "6dc01cac.5c4bf4"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "2587adb9.7e60f2",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 730,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "6dc01cac.5c4bf4",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "utf8",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 540,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "2587adb9.7e60f2"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "f4b4309a.3b78a",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "↑read result from file",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 550,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "672d3693.3cabd8",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "5132b95f037524f9",
 | 
			
		||||
        "name": "↓write to /tmp/hello.txt",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 360,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -0,0 +1,118 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "704479e1.399388",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "filename",
 | 
			
		||||
                "v": "/tmp/hello.txt",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "Hello, World!",
 | 
			
		||||
        "payloadType": "str",
 | 
			
		||||
        "x": 190,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "402f3b7e.988014"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "8e876a75.e9beb8",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Write string to a file specied by filename property, the read from the file",
 | 
			
		||||
        "info": "Write file node can target file using `filename` property.",
 | 
			
		||||
        "x": 310,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "402f3b7e.988014",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 350,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "26e077d6.bbcd98"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "97b6b6b2.a54b38",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 730,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "26e077d6.bbcd98",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "utf8",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 540,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "97b6b6b2.a54b38"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "85062297.da79",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read result from file",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 550,
 | 
			
		||||
        "y": 300,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "7316c4fc.b1dcdc",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write to file specified by filename property",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 460,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										85
									
								
								packages/node_modules/@node-red/nodes/examples/storage/write file/03 - Delete a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								packages/node_modules/@node-red/nodes/examples/storage/write file/03 - Delete a file.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "4ac00fb0.d5f52",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "",
 | 
			
		||||
        "payloadType": "date",
 | 
			
		||||
        "x": 180,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "542cc2f4.92857c"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "671f8295.0e6f6c",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Delete a file",
 | 
			
		||||
        "info": "Write file node can delete a file.",
 | 
			
		||||
        "x": 130,
 | 
			
		||||
        "y": 160,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "542cc2f4.92857c",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "delete",
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 380,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "a24da523.5babe8"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "a24da523.5babe8",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 590,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "51157051.2f62",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓delete a file",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 350,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -0,0 +1,113 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "id": "e4ef1f5e.7cd82",
 | 
			
		||||
        "type": "inject",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Base64 encoded string",
 | 
			
		||||
        "props": [
 | 
			
		||||
            {
 | 
			
		||||
                "p": "payload"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "p": "topic",
 | 
			
		||||
                "vt": "str"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "repeat": "",
 | 
			
		||||
        "crontab": "",
 | 
			
		||||
        "once": false,
 | 
			
		||||
        "onceDelay": 0.1,
 | 
			
		||||
        "topic": "",
 | 
			
		||||
        "payload": "8J+YgA==",
 | 
			
		||||
        "payloadType": "str",
 | 
			
		||||
        "x": 200,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "72b37cc8.177054"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "f5997af4.5a9298",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "Specify encoding of written data",
 | 
			
		||||
        "info": "Write file node can specify encoding of data.",
 | 
			
		||||
        "x": 170,
 | 
			
		||||
        "y": 140,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "72b37cc8.177054",
 | 
			
		||||
        "type": "file",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "appendNewline": true,
 | 
			
		||||
        "createDir": false,
 | 
			
		||||
        "overwriteFile": "true",
 | 
			
		||||
        "encoding": "base64",
 | 
			
		||||
        "x": 420,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "2da33ec.f45cac2"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "2e814354.278c8c",
 | 
			
		||||
        "type": "debug",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "active": true,
 | 
			
		||||
        "tosidebar": true,
 | 
			
		||||
        "console": false,
 | 
			
		||||
        "tostatus": false,
 | 
			
		||||
        "complete": "false",
 | 
			
		||||
        "statusVal": "",
 | 
			
		||||
        "statusType": "auto",
 | 
			
		||||
        "x": 810,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "2da33ec.f45cac2",
 | 
			
		||||
        "type": "file in",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "",
 | 
			
		||||
        "filename": "/tmp/hello.txt",
 | 
			
		||||
        "format": "utf8",
 | 
			
		||||
        "chunk": false,
 | 
			
		||||
        "sendError": false,
 | 
			
		||||
        "encoding": "none",
 | 
			
		||||
        "x": 620,
 | 
			
		||||
        "y": 220,
 | 
			
		||||
        "wires": [
 | 
			
		||||
            [
 | 
			
		||||
                "2e814354.278c8c"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "ec754c99.84bfd",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↓write string with base64 encoding",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 480,
 | 
			
		||||
        "y": 180,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "id": "3e6704ff.4ce25c",
 | 
			
		||||
        "type": "comment",
 | 
			
		||||
        "z": "6312c0588348b2d4",
 | 
			
		||||
        "name": "↑read result from file",
 | 
			
		||||
        "info": "",
 | 
			
		||||
        "x": 630,
 | 
			
		||||
        "y": 260,
 | 
			
		||||
        "wires": []
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
        <dt>msg <span class="property-type">object</span></dt>
 | 
			
		||||
        <dd>A msg object containing information to populate the template.</dd>
 | 
			
		||||
        <dt class="optional">template <span class="property-type">string</span></dt>
 | 
			
		||||
        <dd>A template to be populated from msg.payload. If not configured in the edit panel,
 | 
			
		||||
        <dd>A template to be populated from <code>msg.payload</code>. If not configured in the edit panel,
 | 
			
		||||
         this can be set as a property of msg.</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
    <h3>Outputs</h3>
 | 
			
		||||
 
 | 
			
		||||
@@ -60,5 +60,5 @@
 | 
			
		||||
        for the next topic.
 | 
			
		||||
    </p>
 | 
			
		||||
    <p><b>Note</b>: In rate limit mode the maximum queue depth can be set by a property in your
 | 
			
		||||
        <i>settings.js</i> file. For example <code>nodeMessageBufferMaxLength: 1000,</code>
 | 
			
		||||
        <i>settings.js</i> file. For example <code>nodeMessageBufferMaxLength: 1000,</code></p>
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
    <h3>Details</h3>
 | 
			
		||||
    <p>In RBE mode this node will block until the <code>msg.payload</code>,
 | 
			
		||||
       (or selected property) value is different to the previous one.
 | 
			
		||||
       If required it can ignore the intial value, so as not to send anything at start.</p>
 | 
			
		||||
       If required it can ignore the initial value, so as not to send anything at start.</p>
 | 
			
		||||
    <p>The <a href="https://en.wikipedia.org/wiki/Deadband" target="_blank">Deadband</a> modes will block the incoming value
 | 
			
		||||
       <i>unless</i> its change is greater or greater-equal than ± the band gap away from a previous value.</p>
 | 
			
		||||
    <p>The Narrowband modes will block the incoming value,
 | 
			
		||||
@@ -37,5 +37,5 @@
 | 
			
		||||
    ignoring any values out of range, or the previous input value, which resets the set point, thus allowing
 | 
			
		||||
    gradual drift (deadband), or a step change (narrowband).</p>
 | 
			
		||||
    <p><b>Note:</b> This works on a per <code>msg.topic</code> basis, though this can be changed to another property if desired.
 | 
			
		||||
       This means that a single rbe node can handle multiple different topics at the same time.</p>
 | 
			
		||||
       This means that a single filter node can handle multiple different topics at the same time.</p>
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
       <dt class="optional">topic <span class="property-type">string|object|array</span></dt>
 | 
			
		||||
       <dd>For the <code>"subscribe"</code> and <code>"unsubscribe"</code> actions, this property
 | 
			
		||||
           provides the topic. It can be set as either:<ul>
 | 
			
		||||
           <li>a String continaing the topic filter</li>
 | 
			
		||||
           <li>a String containing the topic filter</li>
 | 
			
		||||
           <li>an Object containing <code>topic</code> and <code>qos</code> properties</li>
 | 
			
		||||
           <li>an array of either strings or objects to handle multiple topics in one</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
        <dd>In case any redirects occurred while processing the request, this property is the final redirected url.
 | 
			
		||||
            Otherwise, the url of the original request.</dd>
 | 
			
		||||
        <dt>responseCookies <span class="property-type">object</span></dt>
 | 
			
		||||
        <dd>If the response includes cookies, this propery is an object of name/value pairs for each cookie.</dd>
 | 
			
		||||
        <dd>If the response includes cookies, this property is an object of name/value pairs for each cookie.</dd>
 | 
			
		||||
        <dt>redirectList <span class="property-type">array</span></dt>
 | 
			
		||||
        <dd>If the request was redirected one or more times, the accumulated information will be added to this property. `location` is the next redirect destination. `cookies` is the cookies returned from the redirect source.</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>When operating in this mode, the node will not set the <code>msg.parts.count</code>
 | 
			
		||||
    property as it does not know how many messages to expect in the stream. This
 | 
			
		||||
    means it cannot be used with the <b>join</b> node in its automatic mode</p>
 | 
			
		||||
    means it cannot be used with the <b>join</b> node in its automatic mode.</p>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-help-name="join">
 | 
			
		||||
 
 | 
			
		||||
@@ -46,4 +46,6 @@
 | 
			
		||||
    <code>{{global[store].名前}}</code>を用います。
 | 
			
		||||
</p>
 | 
			
		||||
    <p><b>注: </b>デフォルトでは、<i>mustache</i>形式は置換対象のHTML要素をエスケープします。これを抑止するには<code>{{{三重}}}</code>括弧形式を使います。</p>
 | 
			
		||||
    <p>もし、コンテンツの中で<code>{{ }}</code>を出力する必要がある場合は、テンプレートで使われる記号文字を変えることもできます。例えば、<code>[[ ]]</code>を代わりに用いるには、テンプレートの先頭に以下の行を追加します。</p>
 | 
			
		||||
    <pre>{{=[[ ]]=}}</pre>
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,14 @@
 | 
			
		||||
        <dt class="optional">reset</dt>
 | 
			
		||||
        <dd>受信メッセージでこのプロパティを任意の値に設定すると、ノードが保持する全ての未送信メッセージをクリアします。</dd>
 | 
			
		||||
        <dt class="optional">flush</dt>
 | 
			
		||||
        <dd>受信メッセージでこのプロパティを任意の値に設定すると、ノードが保持する全ての未送信メッセージを直ちに送信します。</dd>
 | 
			
		||||
        <dd>本プロパティに数値が設定されたメッセージを受信すると、直ちに指定された数のメッセージを送信します。もし他の型(例えば真偽型)が設定されている場合は、ノードが保持している全ての未送信メッセージを直ちに送信します。</dd>
 | 
			
		||||
        <dt class="optional">toFront</dt>
 | 
			
		||||
        <dd>流量制御モードにおいて、本プロパティに真偽型<code>true</code>が設定されたメッセージを受け取ると、キューの先頭に追加され、その後に送信されます。<code>msg.flush=1</code>と組み合わせて用いると、すぐに再送信できます。</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
    <h3>詳細</h3>
 | 
			
		||||
    <p>メッセージを遅延させるように設定する場合、遅延時間は固定値、範囲内の乱数値、メッセージ毎の動的な指定値のいずれかを指定できます。</p>
 | 
			
		||||
    <p>メッセージを遅延させるように設定する場合、遅延時間は固定値、範囲内の乱数値、メッセージ毎の動的な指定値のいずれかを指定できます。各メッセージは、到着時刻に基づいて、他のメッセージとは独立して遅延されます。</p>
 | 
			
		||||
    <p>流量制御する場合、メッセージは指定した時間間隔内に分散して送信します。キューに残っているメッセージ数はノードのステータスに表示されます。受け取った中間メッセージを破棄することも可能です。</p>
 | 
			
		||||
    <p>流量値を上書きできるように設定されている場合、新しい流量値はすぐに適用されます。この流量値は、再度変更されるまで、本ノードがリセットされるまで、またはフローが再実行されるまで有効です。</p>
 | 
			
		||||
    <p>流量制御は全てのメッセージに適用することも、<code>msg.topic</code>値でグループ化して適用することも可能です。グループ化すると、中間メッセージは自動的に破棄されます。時間間隔毎に全てのトピックの最新メッセージを送信するか、次のトピックの最新メッセージを送信するかを指定できます。</p>
 | 
			
		||||
    <p><b>注</b>: 流量制御モードでは、キューの大きさの最大値を<i>settings.js</i>ファイルのプロパティに設定できます。例えば、次の様な設定です。<code>nodeMessageBufferMaxLength: 1000,</code></p>
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -27,5 +27,5 @@
 | 
			
		||||
    <p>不感帯モードでは%による指定もサポートしています。入力と前の値の差分がX%より大きな場合に出力を行います。</p>
 | 
			
		||||
    <p>狭帯域(narrowband)モードでは、前の値に対する差分が一定値より大きな場合に入力ペイロードをブロックします。このモードは、故障したセンサから発生する外れ値を無視する時などに有用です。</p>
 | 
			
		||||
    <p>不感帯モードと狭帯域モードでは、以前の有効出力値、もしくは、以前の入力値との比較ができます。有効出力値を用いると範囲外の値を無視することが、入力値を用いると設定点がリセットされるため漸次的変化(不感帯モード)もしくは段階的変化(狭帯域モード)が可能です。</p>
 | 
			
		||||
    <p><b>注:</b> このノードは<code>msg.topic</code>毎に動作します。そのため、ひとつのrbeノードで複数の異なるトピックを同時に扱うことができます。</p>
 | 
			
		||||
    <p><b>注:</b> このノードは<code>msg.topic</code>毎に動作します。そのため、ひとつのfilterノードで複数の異なるトピックを同時に扱うことができます。</p>
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,46 @@
 | 
			
		||||
       <dd>0: 最大1度到着, 1: 一度以上到着, 2: 1度のみ到着</dd>
 | 
			
		||||
       <dt>retain <span class="property-type">真偽値</span></dt>
 | 
			
		||||
       <dd>真の場合、メッセージを保持。メッセージが古い値の場合があります。</dd>
 | 
			
		||||
       <dt class="optional">responseTopic <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージのMQTT応答トピック</dd>
 | 
			
		||||
       <dt class="optional">correlationData <span class="property-type">バッファ</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージの相関データ</dd>
 | 
			
		||||
       <dt class="optional">contentType <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: ペイロードのコンテントタイプ</dd>
 | 
			
		||||
       <dt class="optional">userProperties <span class="property-type">オブジェクト</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージのユーザプロパティ</dd>
 | 
			
		||||
       <dt class="optional">messageExpiryInterval <span class="property-type">数値</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: 秒単位のメッセージの有効期限</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
    <h3>詳細</h3>
 | 
			
		||||
    <p>購読トピックにはMQTTのワイルドカード(+: 1レベル, #: 複数レベル)を含めることができます。</p>
 | 
			
		||||
    <p>このノードの利用のためには、MQTTブローカへの接続設定が必要です。この設定は鉛筆アイコンをクリックすることで行えます。</p>
 | 
			
		||||
    <p>MQTT(inおよびout)ノードはブローカへの接続設定を必要に応じて共有できます。</p>
 | 
			
		||||
    <h4>動的購読</h4>
 | 
			
		||||
    本ノードは、MQTTの接続と購読を動的に制御するよう設定できます。有効にすると、本ノードの入力にメッセージを渡すことで制御できます。
 | 
			
		||||
    <h3>入力</h3>
 | 
			
		||||
    <p>これらは、動的購読が設定されている場合のみ適用されます。</p>
 | 
			
		||||
    <dl class="message-properties">
 | 
			
		||||
       <dt>action <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd>本ノードが行う動作の名前。利用可能な動作は<code>"connect"</code>、<code>"disconnect"</code>、<code>"subscribe"</code>、<code>"unsubscribe"</code>です。</dd>
 | 
			
		||||
       <dt class="optional">topic <span class="property-type">文字列|オブジェクト|配列</span></dt>
 | 
			
		||||
       <dd><code>"subscribe"</code>と<code>"unsubscribe"</code>の動作に対して、本プロパティはトピックを提供します。次のいずれかを設定できます:<ul>
 | 
			
		||||
           <li>トピックフィルターを含む文字列</li>
 | 
			
		||||
           <li><code>topic</code>と<code>qos</code>プロパティを持つオブジェクト</li>
 | 
			
		||||
           <li>複数のトピックを扱う文字列やオブジェクトの配列</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </dd>
 | 
			
		||||
       <dt class="optional">broker <span class="property-type">broker</span> </dt>
 | 
			
		||||
       <dd><code>"connect"</code>の動作に対して、本プロパティは次の様な個々のブローカ設定を上書きします: <ul>
 | 
			
		||||
               <li><code>broker</code></li>
 | 
			
		||||
               <li><code>port</code></li>
 | 
			
		||||
               <li><code>url</code> - 完全な接続URLを提供するために、brokerとportを上書き</li>
 | 
			
		||||
               <li><code>username</code></li>
 | 
			
		||||
               <li><code>password</code></li>
 | 
			
		||||
           </ul>
 | 
			
		||||
           <p>本プロパティが設定され既にブローカが接続されている場合、<code>force</code>プロパティを設定しない限り、エラーがログに記録されます。設定された場合はブローカから切断され、新しい設定を適用して再接続します。</p>
 | 
			
		||||
       </dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-help-name="mqtt out">
 | 
			
		||||
@@ -39,15 +74,24 @@
 | 
			
		||||
    <dl class="message-properties">
 | 
			
		||||
       <dt>payload <span class="property-type">文字列 | バッファ</span></dt>
 | 
			
		||||
       <dd>発行するペイロード。プロパティが設定されていない場合には、メッセージは送信されません。空のメッセージを送信するには、プロパティに空文字列を設定します。</dd>
 | 
			
		||||
 | 
			
		||||
       <dt class="optional">topic <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd>発行対象のMQTTトピック</dd>
 | 
			
		||||
 | 
			
		||||
       <dt class="optional">qos <span class="property-type">数値</span></dt>
 | 
			
		||||
       <dd>0: 最大一度到着, 1: 一度以上到着, 2: 一度のみ到着。デフォルトは0です。</dd>
 | 
			
		||||
 | 
			
		||||
       <dt class="optional">retain <span class="property-type">真偽値</span></dt>
 | 
			
		||||
       <dd>真の場合、メッセージをブローカに保持します。デフォルトは偽です。</dd>
 | 
			
		||||
       <dt class="optional">responseTopic <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージのMQTT応答トピック</dd>
 | 
			
		||||
       <dt class="optional">correlationData <span class="property-type">バッファ</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージの相関データ</dd>
 | 
			
		||||
       <dt class="optional">contentType <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: ペイロードのコンテントタイプ</dd>
 | 
			
		||||
       <dt class="optional">userProperties <span class="property-type">オブジェクト</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: メッセージのユーザプロパティ</dd>
 | 
			
		||||
       <dt class="optional">messageExpiryInterval <span class="property-type">数値</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: 秒単位のメッセージの有効期限</dd>   
 | 
			
		||||
       <dt class="optional">topicAlias <span class="property-type">数値</span></dt>
 | 
			
		||||
       <dd><b>MQTTv5</b>: 使用するMQTTトピックエイリアス</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
    <h3>詳細</h3>
 | 
			
		||||
    <p><code>msg.payload</code>を発行するメッセージのペイロードとして用います。ペイロードがオブジェクトの場合、送信の際にJSON文字列に変換します。ペイロードがバイナリバッファの場合、そのまま送信します。</p>
 | 
			
		||||
@@ -55,6 +99,24 @@
 | 
			
		||||
    <p>同様に、QoSとretainもノードの設定、もしくは、ノードの設定が空の場合には、それぞれ<code>msg.qos</code>および<code>msg.retain</code>で指定できます。以前ブローカに保存したトピックをクリアするには、retainフラグを設定して当該トピックに空のメッセージを発行します。</p>
 | 
			
		||||
    <p>このノードの利用のためには、MQTTブローカへの接続設定が必要です。この設定は鉛筆アイコンをクリックすることで行えます。</p>
 | 
			
		||||
    <p>MQTT(inおよびout)ノードはブローカへの接続設定を必要に応じて共有できます。</p>
 | 
			
		||||
 | 
			
		||||
    <h4>動的制御</h4>
 | 
			
		||||
    本ノードによって接続を動的に制御できます。本ノードが以下の制御メッセージのいずれかを受け取った際は、ペイロードと同じ様にパブリッシュされることはありません。
 | 
			
		||||
    <h3>入力</h3>
 | 
			
		||||
    <dl class="message-properties">
 | 
			
		||||
       <dt>action <span class="property-type">文字列</span></dt>
 | 
			
		||||
       <dd>本ノードが行う動作の名前。利用可能な動作は<code>"connect"</code>、<code>"disconnect"</code>、<code>"subscribe"</code>、<code>"unsubscribe"</code>です。</dd>
 | 
			
		||||
       <dt class="optional">broker <span class="property-type">broker</span> </dt>
 | 
			
		||||
       <dd><code>"connect"</code>の動作に対して、本プロパティは次の様な個々のブローカ設定を上書きします: <ul>
 | 
			
		||||
           <li><code>broker</code></li>
 | 
			
		||||
           <li><code>port</code></li>
 | 
			
		||||
           <li><code>url</code> - 完全な接続URLを提供するために、brokerとportを上書き</li>
 | 
			
		||||
           <li><code>username</code></li>
 | 
			
		||||
           <li><code>password</code></li>
 | 
			
		||||
        </ul>
 | 
			
		||||
        <p>本プロパティが設定され既にブローカが接続されている場合、<code>force</code>プロパティを設定しない限り、エラーがログに記録されます。設定された場合はブローカから切断され、新しい設定を適用して再接続します。</p>
 | 
			
		||||
       </dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-help-name="mqtt-broker">
 | 
			
		||||
@@ -70,5 +132,4 @@
 | 
			
		||||
    <h4>WebSocket</h4>
 | 
			
		||||
    <p>WebSocketによる接続を行うように設定できます。WebSocketを利用するには、サーバフィールドに接続先のURIを完全な形式で記述します。以下に例を示します。</p>
 | 
			
		||||
    <pre>ws://example.com:4000/mqtt</pre>
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
    <h3>詳細</h3>
 | 
			
		||||
    <p>「列名」にカラム名のリストを指定することができます。CSVからオブジェクトに変換を行う際、カラム名をプロパティ名として使用します。「列名」の代わりに、CSVデータの1行目にカラム名を含めることもできます。</p>
 | 
			
		||||
    <p>CSVへの変換を行う際には、オブジェクトから取り出すべきプロパティとその順序を「列名」を参照して決めます。</p>
 | 
			
		||||
    <p>列名がない場合、本ノードは<code>msg.columns</code>プロパティの単純なコンマ区切りリストを使用して、何を抽出するかを決定します。もしそれが存在しない場合、すべてのオブジェクトプロパティを見つけた順序で出力します。</p>
 | 
			
		||||
    <p>列名がない場合、本ノードは<code>msg.columns</code>プロパティの単純なコンマ区切りリストを使用して、何をどの順序で抽出するかを決定します。もし存在しない場合、すべてのオブジェクトプロパティを見つけた順序で出力します。</p>
 | 
			
		||||
    <p>入力が配列の場合には、「列名」はカラム名を表す行の出力指定がされた場合だけ用います。</p>
 | 
			
		||||
    <p>「数値を変換する」オプションがチェックされている場合、文字列型の数値が数値として返されます。つまり「1,"1.5",2」の真ん中の値が数値になります。</p>
 | 
			
		||||
    <p>「空の文字を含む」オプションがチェックされている場合、空の文字列が結果に返されます。つまり「"1","",3」の真ん中の値が空の文字列になります。</p>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,6 @@
 | 
			
		||||
    <p>このモードで処理する際には、メッセージ数を予め知ることができないため、<code>msg.parts.count</code>プロパティは設定されません。従って、<b>join</b>ノードの「自動モード」と組み合わせることはできません。</p>
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<script type="text/html" data-help-name="join">
 | 
			
		||||
    <p>メッセージ列を結合して一つのメッセージにします。</p>
 | 
			
		||||
    <p>メッセージの結合には次の3つのモードが利用できます。</p>
 | 
			
		||||
@@ -80,6 +79,10 @@
 | 
			
		||||
        </dd>
 | 
			
		||||
        <dt class="optional">complete</dt>
 | 
			
		||||
        <dd>設定されている場合、本ノードはペイロードを追加し、保持しているメッセージを送信します。ペイロードを追加したくない場合は、msgから削除してください。</dd>
 | 
			
		||||
        <dt class="optional">reset</dt>
 | 
			
		||||
        <dd>設定されている場合、本ノードは部分的に完成したメッセージを送信せず、削除します。</dd>
 | 
			
		||||
        <dt class="optional">restartTimeout</dt>
 | 
			
		||||
        <dd>設定されている場合、本ノードにタイムアウトが設定され、そのタイムアウトを用いて処理が再開されます。</dd>
 | 
			
		||||
    </dl>
 | 
			
		||||
    <h3>詳細</h3>
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +99,7 @@
 | 
			
		||||
    </ul>
 | 
			
		||||
    <p>出力メッセージのその他のプロパティはメッセージを送信する直前のメッセージをコピーします。</p>
 | 
			
		||||
    <p>「<i>合計値</i>」には出力メッセージを送信する前に受信すべきメッセージ数を指定します。オブジェクト出力の場合、この合計値に達すると後続メッセージの到着毎にメッセージを出力するように設定することもできます。</p>
 | 
			
		||||
    <p>「<i>秒</i>」には新規メッセージを送信するまでの経過時間を設定します。</p>
 | 
			
		||||
    <p>「<i>秒</i>」には新規メッセージを送信するまでの経過時間を設定します。<code>msg.restartTimeout</code>プロパティを設定したメッセージを渡すことで、指定した時間で再開できます。</p>
 | 
			
		||||
    <p><code>msg.complete</code>プロパティを設定したメッセージを受信すると、出力メッセージを送信します。この時、メッセージ列の数をリセットします。</p>
 | 
			
		||||
    <p><code>msg.reset</code>プロパティを設定したメッセージを受信すると、部分的に受信済みのメッセージを破棄します。これらのメッセージは送信されません。この時、メッセージ列の数をリセットします。</p>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/nodes",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "repository": {
 | 
			
		||||
        "type": "git",
 | 
			
		||||
@@ -15,10 +15,10 @@
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "acorn": "8.6.0",
 | 
			
		||||
        "acorn": "8.7.0",
 | 
			
		||||
        "acorn-walk": "8.2.0",
 | 
			
		||||
        "ajv": "8.8.2",
 | 
			
		||||
        "body-parser": "1.19.0",
 | 
			
		||||
        "body-parser": "1.19.1",
 | 
			
		||||
        "cheerio": "1.0.0-rc.10",
 | 
			
		||||
        "content-type": "1.0.4",
 | 
			
		||||
        "cookie-parser": "1.4.6",
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
        "is-utf8": "0.2.1",
 | 
			
		||||
        "js-yaml": "3.14.1",
 | 
			
		||||
        "media-typer": "1.1.0",
 | 
			
		||||
        "mqtt": "4.2.8",
 | 
			
		||||
        "mqtt": "4.3.4",
 | 
			
		||||
        "multer": "1.4.3",
 | 
			
		||||
        "mustache": "4.2.0",
 | 
			
		||||
        "on-headers": "1.0.2",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/registry",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "main": "./lib/index.js",
 | 
			
		||||
    "repository": {
 | 
			
		||||
@@ -16,11 +16,11 @@
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@node-red/util": "2.1.4",
 | 
			
		||||
        "@node-red/util": "2.1.6",
 | 
			
		||||
        "clone": "2.1.2",
 | 
			
		||||
        "fs-extra": "10.0.0",
 | 
			
		||||
        "semver": "7.3.5",
 | 
			
		||||
        "tar": "6.1.11",
 | 
			
		||||
        "uglify-js": "3.14.4"
 | 
			
		||||
        "uglify-js": "3.14.5"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,7 @@ function createNode(flow,config) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
                Object.defineProperty(conf,'_module', {value: typeRegistry.getNodeInfo(type), enumerable: false, writable: true })
 | 
			
		||||
                Object.defineProperty(conf,'_flow', {value: flow, enumerable: false, writable: true })
 | 
			
		||||
                newNode = new nodeTypeConstructor(conf);
 | 
			
		||||
            } catch (err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,9 @@ function Node(n) {
 | 
			
		||||
        // which we can tolerate as they are the same object.
 | 
			
		||||
        Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true })
 | 
			
		||||
    }
 | 
			
		||||
    if (n._module) {
 | 
			
		||||
        Object.defineProperty(this,'_module', {value: n._module, enumerable: false, writable: true })
 | 
			
		||||
    }
 | 
			
		||||
    this.updateWires(n.wires);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -496,7 +499,12 @@ function log_helper(self, level, msg) {
 | 
			
		||||
    if (self.name) {
 | 
			
		||||
        o.name = self.name;
 | 
			
		||||
    }
 | 
			
		||||
    self._flow.log(o);
 | 
			
		||||
    // See https://github.com/node-red/node-red/issues/3327
 | 
			
		||||
    try {
 | 
			
		||||
        self._flow.log(o);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        logUnexpectedError(self, err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * Log an INFO level message
 | 
			
		||||
@@ -576,4 +584,59 @@ Node.prototype.status = function(status) {
 | 
			
		||||
    this._flow.handleStatus(this,status);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function inspectObject(flow) {
 | 
			
		||||
    try {
 | 
			
		||||
        let properties = new Set()
 | 
			
		||||
        let currentObj = flow
 | 
			
		||||
        do {
 | 
			
		||||
            if (!Object.getPrototypeOf(currentObj)) { break }
 | 
			
		||||
            Object.getOwnPropertyNames(currentObj).map(item => properties.add(item))
 | 
			
		||||
        } while ((currentObj = Object.getPrototypeOf(currentObj)))
 | 
			
		||||
        let propList = [...properties.keys()].map(item => `${item}[${(typeof flow[item])[0]}]`)
 | 
			
		||||
        propList.sort();
 | 
			
		||||
        let result = [];
 | 
			
		||||
        let line = "";
 | 
			
		||||
        while (propList.length > 0) {
 | 
			
		||||
            let prop = propList.shift()
 | 
			
		||||
            if (line.length+prop.length > 80) {
 | 
			
		||||
                result.push(line)
 | 
			
		||||
                line = "";
 | 
			
		||||
            } else {
 | 
			
		||||
                line += " "+prop
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (line.length > 0) {
 | 
			
		||||
            result.push(line);
 | 
			
		||||
        }
 | 
			
		||||
        return result.join("\n  ")
 | 
			
		||||
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        return "Failed to capture object properties: "+err.toString()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logUnexpectedError(node, error) {
 | 
			
		||||
    let moduleInfo = node._module?`${node._module.module}@${node._module.version}`:"undefined"
 | 
			
		||||
    Log.error(`
 | 
			
		||||
********************************************************************
 | 
			
		||||
Unexpected Node Error
 | 
			
		||||
${error.stack}
 | 
			
		||||
Node:
 | 
			
		||||
 Type: ${node.type}
 | 
			
		||||
 Module: ${moduleInfo}
 | 
			
		||||
 ID: ${node._alias||node.id}
 | 
			
		||||
 Properties:
 | 
			
		||||
  ${inspectObject(node)}
 | 
			
		||||
Flow: ${node._flow?node._flow.path:'undefined'}
 | 
			
		||||
 Type: ${node._flow?node._flow.TYPE:'undefined'}
 | 
			
		||||
 Properties:
 | 
			
		||||
  ${node._flow?inspectObject(node._flow):'undefined'}
 | 
			
		||||
 | 
			
		||||
Please report this issue, including the information logged above:
 | 
			
		||||
https://github.com/node-red/node-red/issues/
 | 
			
		||||
********************************************************************
 | 
			
		||||
`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Node;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/runtime",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "main": "./lib/index.js",
 | 
			
		||||
    "repository": {
 | 
			
		||||
@@ -16,11 +16,11 @@
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@node-red/registry": "2.1.4",
 | 
			
		||||
        "@node-red/util": "2.1.4",
 | 
			
		||||
        "@node-red/registry": "2.1.6",
 | 
			
		||||
        "@node-red/util": "2.1.6",
 | 
			
		||||
        "async-mutex": "0.3.2",
 | 
			
		||||
        "clone": "2.1.2",
 | 
			
		||||
        "express": "4.17.1",
 | 
			
		||||
        "express": "4.17.2",
 | 
			
		||||
        "fs-extra": "10.0.0",
 | 
			
		||||
        "json-stringify-safe": "5.0.1"
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,14 @@ function wrapEventFunction(obj,func) {
 | 
			
		||||
    return function(eventName, listener) {
 | 
			
		||||
        if (deprecatedEvents.hasOwnProperty(eventName)) {
 | 
			
		||||
            const log = require("@node-red/util").log;
 | 
			
		||||
            const stack = (new Error().stack).split("\n")[2].split("(")[1].slice(0,-1);
 | 
			
		||||
            log.warn(`[RED.events] Deprecated use of "${eventName}" event from "${stack}". Use "${deprecatedEvents[eventName]}" instead.`)
 | 
			
		||||
 | 
			
		||||
            const stack = (new Error().stack).split("\n");
 | 
			
		||||
            let location = "(unknown)"
 | 
			
		||||
            // See https://github.com/node-red/node-red/issues/3292
 | 
			
		||||
            if (stack.length > 2) {
 | 
			
		||||
                location = stack[2].split("(")[1].slice(0,-1);
 | 
			
		||||
            }
 | 
			
		||||
            log.warn(`[RED.events] Deprecated use of "${eventName}" event from "${location}". Use "${deprecatedEvents[eventName]}" instead.`)
 | 
			
		||||
        }
 | 
			
		||||
        return events["_"+func].call(events,eventName,listener)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -686,7 +686,7 @@ function prepareJSONataExpression(value,node) {
 | 
			
		||||
        return moment(arg1, arg2, arg3, arg4);
 | 
			
		||||
    });
 | 
			
		||||
    expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
 | 
			
		||||
    expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
 | 
			
		||||
    expr._legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(value);
 | 
			
		||||
    expr._node = node;
 | 
			
		||||
    return expr;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@node-red/util",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
    "repository": {
 | 
			
		||||
        "type": "git",
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "fs-extra": "10.0.0",
 | 
			
		||||
        "i18next": "21.5.4",
 | 
			
		||||
        "i18next": "21.6.6",
 | 
			
		||||
        "json-stringify-safe": "5.0.1",
 | 
			
		||||
        "jsonata": "1.8.5",
 | 
			
		||||
        "lodash.clonedeep": "^4.5.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "node-red",
 | 
			
		||||
    "version": "2.1.4",
 | 
			
		||||
    "version": "2.1.6",
 | 
			
		||||
    "description": "Low-code programming for event-driven applications",
 | 
			
		||||
    "homepage": "http://nodered.org",
 | 
			
		||||
    "license": "Apache-2.0",
 | 
			
		||||
@@ -31,13 +31,13 @@
 | 
			
		||||
        "flow"
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@node-red/editor-api": "2.1.4",
 | 
			
		||||
        "@node-red/runtime": "2.1.4",
 | 
			
		||||
        "@node-red/util": "2.1.4",
 | 
			
		||||
        "@node-red/nodes": "2.1.4",
 | 
			
		||||
        "@node-red/editor-api": "2.1.6",
 | 
			
		||||
        "@node-red/runtime": "2.1.6",
 | 
			
		||||
        "@node-red/util": "2.1.6",
 | 
			
		||||
        "@node-red/nodes": "2.1.6",
 | 
			
		||||
        "basic-auth": "2.0.1",
 | 
			
		||||
        "bcryptjs": "2.4.3",
 | 
			
		||||
        "express": "4.17.1",
 | 
			
		||||
        "express": "4.17.2",
 | 
			
		||||
        "fs-extra": "10.0.0",
 | 
			
		||||
        "node-red-admin": "^2.2.1",
 | 
			
		||||
        "nopt": "5.0.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										539
									
								
								test/nodes/core/function/rbe_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										539
									
								
								test/nodes/core/function/rbe_spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,539 @@
 | 
			
		||||
 | 
			
		||||
var should = require("should");
 | 
			
		||||
var helper = require("node-red-node-test-helper");
 | 
			
		||||
 | 
			
		||||
var testNode = require("nr-test-utils").require("@node-red/nodes/core/function/rbe.js");
 | 
			
		||||
 | 
			
		||||
describe('rbe node', function() {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
    beforeEach(function(done) {
 | 
			
		||||
        helper.startServer(done);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(function(done) {
 | 
			
		||||
        helper.unload().then(function() {
 | 
			
		||||
            helper.stopServer(done);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("should be loaded with correct defaults", function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", "name":"rbe1", "wires":[[]]}];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            n1.should.have.property("name", "rbe1");
 | 
			
		||||
            n1.should.have.property("func", "rbe");
 | 
			
		||||
            n1.should.have.property("gap", "0");
 | 
			
		||||
            done();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if payload changes - with multiple topics (rbe)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 2);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload");
 | 
			
		||||
                    msg.payload.should.have.a.property("b",1);
 | 
			
		||||
                    msg.payload.should.have.a.property("c",2);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload",true);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 4) {
 | 
			
		||||
                    msg.should.have.a.property("payload",false);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 5) {
 | 
			
		||||
                    msg.should.have.a.property("payload",true);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 6) {
 | 
			
		||||
                    msg.should.have.a.property("topic","a");
 | 
			
		||||
                    msg.should.have.a.property("payload",1);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 7) {
 | 
			
		||||
                    msg.should.have.a.property("topic","b");
 | 
			
		||||
                    msg.should.have.a.property("payload",1);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else  {
 | 
			
		||||
                    c += 1;
 | 
			
		||||
                    msg.should.have.a.property("topic","c");
 | 
			
		||||
                    msg.should.have.a.property("payload",1);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"a"});
 | 
			
		||||
            n1.emit("input", {payload:2});
 | 
			
		||||
            n1.emit("input", {payload:2});
 | 
			
		||||
            n1.emit("input", {payload:{b:1,c:2}});
 | 
			
		||||
            n1.emit("input", {payload:{c:2,b:1}});
 | 
			
		||||
            n1.emit("input", {payload:{c:2,b:1}});
 | 
			
		||||
            n1.emit("input", {payload:true});
 | 
			
		||||
            n1.emit("input", {payload:false});
 | 
			
		||||
            n1.emit("input", {payload:false});
 | 
			
		||||
            n1.emit("input", {payload:true});
 | 
			
		||||
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"c",payload:1});
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should ignore multiple topics if told to (rbe)', function(done) {
 | 
			
		||||
        var flow = [{id:"n1", type:"rbe", func:"rbe", gap:"0", septopics:false, wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 2);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload");
 | 
			
		||||
                    msg.payload.should.have.a.property("b",1);
 | 
			
		||||
                    msg.payload.should.have.a.property("c",2);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload",true);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 4) {
 | 
			
		||||
                    msg.should.have.a.property("payload",false);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 5) {
 | 
			
		||||
                    msg.should.have.a.property("payload",true);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 6) {
 | 
			
		||||
                    msg.should.have.a.property("topic","a");
 | 
			
		||||
                    msg.should.have.a.property("payload",1);
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else  {
 | 
			
		||||
                    msg.should.have.a.property("topic","a");
 | 
			
		||||
                    msg.should.have.a.property("payload",2);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"c",payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:2});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:2});
 | 
			
		||||
            n1.emit("input", {payload:{b:1,c:2}});
 | 
			
		||||
            n1.emit("input", {payload:{c:2,b:1}});
 | 
			
		||||
            n1.emit("input", {payload:{c:2,b:1}});
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:true});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:false});
 | 
			
		||||
            n1.emit("input", {topic:"c",payload:false});
 | 
			
		||||
            n1.emit("input", {topic:"d",payload:true});
 | 
			
		||||
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"b",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"c",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"d",payload:1});
 | 
			
		||||
            n1.emit("input", {topic:"a",payload:2});
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if another chosen property changes - foo (rbe)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", property:"foo", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("foo", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("foo", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    msg.should.have.a.property("foo");
 | 
			
		||||
                    msg.foo.should.have.a.property("b",1);
 | 
			
		||||
                    msg.foo.should.have.a.property("c",2);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {foo:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"a"});
 | 
			
		||||
            n1.emit("input", {foo:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"a"});
 | 
			
		||||
            n1.emit("input", {foo:"a"});
 | 
			
		||||
            n1.emit("input", {foo:"b"});
 | 
			
		||||
            n1.emit("input", {foo:{b:1,c:2}});
 | 
			
		||||
            n1.emit("input", {foo:{c:2,b:1}});
 | 
			
		||||
            n1.emit("input", {payload:{c:2,b:1}});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if payload changes - ignoring first value (rbei)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"rbei", gap:"0", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    msg.should.have.a.property("topic", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    msg.should.have.a.property("topic", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "c");
 | 
			
		||||
                    msg.should.have.a.property("topic", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    msg.should.have.a.property("payload", "c");
 | 
			
		||||
                    msg.should.have.a.property("topic", "b");
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:"a", topic:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"a", topic:"b"});
 | 
			
		||||
            n1.emit("input", {payload:"a", topic:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"b", topic:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"b", topic:"b"});
 | 
			
		||||
            n1.emit("input", {payload:"c", topic:"a"});
 | 
			
		||||
            n1.emit("input", {payload:"c", topic:"b"});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should send output if queue is reset (rbe)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 4) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 5) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "b");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 6) {
 | 
			
		||||
                    msg.should.have.a.property("payload", "a");
 | 
			
		||||
                    c+=1;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    msg.should.have.a.property("payload", "c");
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {reset:true});             // reset all
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {topic:"b", reset:""});    // reset b
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {reset:""}); // reset all
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"c"});              // don't reset a non topic
 | 
			
		||||
            n1.emit("input", {topic:"b", payload:"b"});
 | 
			
		||||
            n1.emit("input", {topic:"a", payload:"a"});
 | 
			
		||||
            n1.emit("input", {topic:"c", payload:"c"});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if x away from original value (deadbandEq)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"deadbandEq", gap:"10", inout:"out", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                c = c + 1;
 | 
			
		||||
                if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 0);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 10);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c == 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 20);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:0});
 | 
			
		||||
            n1.emit("input", {payload:2});
 | 
			
		||||
            n1.emit("input", {payload:4});
 | 
			
		||||
            n1.emit("input", {payload:6});
 | 
			
		||||
            n1.emit("input", {payload:8});
 | 
			
		||||
            n1.emit("input", {payload:10});
 | 
			
		||||
            n1.emit("input", {payload:15});
 | 
			
		||||
            n1.emit("input", {payload:20});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if more than x away from original value (deadband)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                c = c + 1;
 | 
			
		||||
                //console.log(c,msg);
 | 
			
		||||
                if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 0);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 20);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    msg.should.have.a.property("payload", "5 deg");
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:0});
 | 
			
		||||
            n1.emit("input", {payload:2});
 | 
			
		||||
            n1.emit("input", {payload:4});
 | 
			
		||||
            n1.emit("input", {payload:"6 deg"});
 | 
			
		||||
            n1.emit("input", {payload:8});
 | 
			
		||||
            n1.emit("input", {payload:20});
 | 
			
		||||
            n1.emit("input", {payload:15});
 | 
			
		||||
            n1.emit("input", {payload:"5 deg"});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only send output if more than x% away from original value (deadband)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10%", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                c = c + 1;
 | 
			
		||||
                if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 100);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 111);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 135);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:100});
 | 
			
		||||
            n1.emit("input", {payload:95});
 | 
			
		||||
            n1.emit("input", {payload:105});
 | 
			
		||||
            n1.emit("input", {payload:111});
 | 
			
		||||
            n1.emit("input", {payload:120});
 | 
			
		||||
            n1.emit("input", {payload:135});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should warn if no number found in deadband mode', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                c += 1;
 | 
			
		||||
            });
 | 
			
		||||
            setTimeout( function() {
 | 
			
		||||
                c.should.equal(0);
 | 
			
		||||
                helper.log().called.should.be.true;
 | 
			
		||||
                var logEvents = helper.log().args.filter(function (evt) {
 | 
			
		||||
                    return evt[0].type == "rbe";
 | 
			
		||||
                });
 | 
			
		||||
                logEvents.should.have.length(1);
 | 
			
		||||
                var msg = logEvents[0][0];
 | 
			
		||||
                msg.should.have.property('level', helper.log().WARN);
 | 
			
		||||
                msg.should.have.property('id', 'n1');
 | 
			
		||||
                msg.should.have.property('type', 'rbe');
 | 
			
		||||
                msg.should.have.property('msg', 'rbe.warn.nonumber');
 | 
			
		||||
                done();
 | 
			
		||||
            },50);
 | 
			
		||||
            n1.emit("input", {payload:"banana"});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should not send output if x away or greater from original value (narrowbandEq)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"narrowbandEq", gap:"10", inout:"out", start:"1", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                c = c + 1;
 | 
			
		||||
                //console.log(c,msg);
 | 
			
		||||
                if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 0);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 2) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 5);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 3) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 10);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:100});
 | 
			
		||||
            n1.emit("input", {payload:0});
 | 
			
		||||
            n1.emit("input", {payload:10});
 | 
			
		||||
            n1.emit("input", {payload:5});
 | 
			
		||||
            n1.emit("input", {payload:15});
 | 
			
		||||
            n1.emit("input", {payload:10});
 | 
			
		||||
            n1.emit("input", {payload:20});
 | 
			
		||||
            n1.emit("input", {payload:25});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should not send output if more than x away from original value (narrowband)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 0);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload","6 deg");
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    msg.should.have.a.property("payload", "5 deg");
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
                c += 1;
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:0});
 | 
			
		||||
            n1.emit("input", {payload:20});
 | 
			
		||||
            n1.emit("input", {payload:40});
 | 
			
		||||
            n1.emit("input", {payload:"6 deg"});
 | 
			
		||||
            n1.emit("input", {payload:18});
 | 
			
		||||
            n1.emit("input", {payload:20});
 | 
			
		||||
            n1.emit("input", {payload:50});
 | 
			
		||||
            n1.emit("input", {payload:"5 deg"});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should send output if gap is 0 and input doesnt change (narrowband)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"0", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 1);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 4) {
 | 
			
		||||
                    msg.should.have.a.property("payload",1);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
                c += 1;
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:1});
 | 
			
		||||
            n1.emit("input", {payload:1});
 | 
			
		||||
            n1.emit("input", {payload:1});
 | 
			
		||||
            n1.emit("input", {payload:1});
 | 
			
		||||
            n1.emit("input", {payload:0});
 | 
			
		||||
            n1.emit("input", {payload:1});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should not send output if more than x away from original value (narrowband in step mode)', function(done) {
 | 
			
		||||
        var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", inout:"in", start:"500", wires:[["n2"]] },
 | 
			
		||||
            {id:"n2", type:"helper"} ];
 | 
			
		||||
        helper.load(testNode, flow, function() {
 | 
			
		||||
            var n1 = helper.getNode("n1");
 | 
			
		||||
            var n2 = helper.getNode("n2");
 | 
			
		||||
            var c = 0;
 | 
			
		||||
            n2.on("input", function(msg) {
 | 
			
		||||
                if (c === 0) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 55);
 | 
			
		||||
                }
 | 
			
		||||
                else if (c === 1) {
 | 
			
		||||
                    msg.should.have.a.property("payload", 205);
 | 
			
		||||
                    done();
 | 
			
		||||
                }
 | 
			
		||||
                c += 1;
 | 
			
		||||
            });
 | 
			
		||||
            n1.emit("input", {payload:50});
 | 
			
		||||
            n1.emit("input", {payload:55});
 | 
			
		||||
            n1.emit("input", {payload:200});
 | 
			
		||||
            n1.emit("input", {payload:205});
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user