1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00
node-red/packages/node_modules/@node-red/editor-client/src/js/red.js
Nick O'Leary afe89c3621
Handle node configs with multiple external scripts properly
If the config had multiple scripts, we were calling the done
callback once for each script. This in turn led to duplicate
flows being loaded.
2019-03-15 18:50:58 +00:00

561 lines
26 KiB
JavaScript

/**
* 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.
**/
var RED = (function() {
function appendNodeConfig(nodeConfig,done) {
done = done || function(){};
var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
var moduleId;
if (m) {
moduleId = m[1];
} else {
moduleId = "unknown";
}
try {
var hasDeferred = false;
var nodeConfigEls = $("<div>"+nodeConfig+"</div>");
var scripts = nodeConfigEls.find("script");
var scriptCount = scripts.length;
scripts.each(function(i,el) {
var srcUrl = $(el).attr('src');
if (srcUrl && !/^\s*(https?:|\/|\.)/.test(srcUrl)) {
$(el).remove();
console.log("Appending script for",moduleId)
var newScript = document.createElement("script");
newScript.onload = function() {
scriptCount--;
if (scriptCount === 0) {
$("body").append(nodeConfigEls);
done()
}
}
$('body').append(newScript);
newScript.src = RED.settings.apiRootUrl+srcUrl;
hasDeferred = true;
} else {
scriptCount--;
}
})
if (!hasDeferred) {
$("body").append(nodeConfigEls);
done();
}
} catch(err) {
RED.notify(RED._("notification.errors.failedToAppendNode",{module:moduleId, error:err.toString()}),{
type: "error",
timeout: 10000
});
console.log("["+moduleId+"] "+err.toString());
done();
}
}
function loadNodeList() {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'nodes',
success: function(data) {
RED.nodes.setNodeList(data);
RED.i18n.loadNodeCatalogs(function() {
loadIconList(loadNodes);
});
}
});
}
function loadIconList(done) {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'icons',
success: function(data) {
RED.nodes.setIconSets(data);
if (done) {
done();
}
}
});
}
function loadNodes() {
$.ajax({
headers: {
"Accept":"text/html"
},
cache: false,
url: 'nodes',
success: function(data) {
var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/);
var stepConfig = function() {
if (configs.length === 0) {
$("body").i18n();
$("#palette > .palette-spinner").hide();
$(".palette-scroll").removeClass("hide");
$("#palette-search").removeClass("hide");
loadFlows(function() {
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.refresh(function(activeProject) {
RED.sidebar.info.refresh()
if (!activeProject) {
// Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true);
RED.menu.setDisabled('menu-item-projects-settings',true);
if (activeProject === false) {
// User previously decline the migration to projects.
} else { // null/undefined
RED.projects.showStartup();
}
}
completeLoad();
});
} else {
// Projects disabled by the user
RED.sidebar.info.refresh()
completeLoad();
}
});
} else {
var config = configs.shift();
appendNodeConfig(config,stepConfig);
}
}
stepConfig();
}
});
}
function loadFlows(done) {
$.ajax({
headers: {
"Accept":"application/json",
},
cache: false,
url: 'flows',
success: function(nodes) {
if (nodes) {
var currentHash = window.location.hash;
RED.nodes.version(nodes.rev);
RED.nodes.import(nodes.flows);
RED.nodes.dirty(false);
RED.view.redraw(true);
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6));
}
}
done();
}
});
}
function completeLoad() {
var persistentNotifications = {};
RED.comms.subscribe("notification/#",function(topic,msg) {
var parts = topic.split("/");
var notificationId = parts[1];
if (notificationId === "runtime-deploy") {
// handled in ui/deploy.js
return;
}
if (notificationId === "node") {
// handled below
return;
}
if (notificationId === "project-update") {
RED.nodes.clear();
RED.history.clear();
RED.view.redraw(true);
RED.projects.refresh(function() {
loadFlows(function() {
var project = RED.projects.getActiveProject();
var message = {
"change-branch": RED._("notification.project.change-branch", {project: project.git.branches.local}),
"merge-abort": RED._("notification.project.merge-abort"),
"loaded": RED._("notification.project.loaded", {project: msg.project}),
"updated": RED._("notification.project.updated", {project: msg.project}),
"pull": RED._("notification.project.pull", {project: msg.project}),
"revert": RED._("notification.project.revert", {project: msg.project}),
"merge-complete": RED._("notification.project.merge-complete")
}[msg.action];
RED.notify("<p>"+message+"</p>");
RED.sidebar.info.refresh()
});
});
return;
}
if (msg.text) {
msg.default = msg.text;
var text = RED._(msg.text,msg);
var options = {
type: msg.type,
fixed: msg.timeout === undefined,
timeout: msg.timeout,
id: notificationId
}
if (notificationId === "runtime-state") {
if (msg.error === "safe-mode") {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
} else if (msg.error === "missing-types") {
text+="<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
if (!!RED.projects.getActiveProject()) {
options.buttons = [
{
text: RED._("notification.label.manage-project-dep"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.settings.show('deps');
}
}
]
// } else if (RED.settings.theme('palette.editable') !== false) {
} else {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
}
} else if (msg.error === "credentials_load_failed") {
if (RED.settings.theme("projects.enabled",false)) {
// projects enabled
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.project.setupCredentials"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showCredentialsPrompt();
}
}
]
}
} else {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
}
} else if (msg.error === "missing_flow_file") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.project.setupProjectFiles"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showFilesPrompt();
}
}
]
}
} else if (msg.error === "missing_package_file") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.project.setupProjectFiles"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showFilesPrompt();
}
}
]
}
} else if (msg.error === "project_empty") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.project.no"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
},
{
text: RED._("notification.project.createDefault"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.createDefaultFileSet();
}
}
]
}
} else if (msg.error === "git_merge_conflict") {
RED.nodes.clear();
RED.sidebar.versionControl.refresh(true);
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.project.mergeConflict"),
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.sidebar.versionControl.showLocalChanges();
}
}
]
}
}
}
if (!persistentNotifications.hasOwnProperty(notificationId)) {
persistentNotifications[notificationId] = RED.notify(text,options);
} else {
persistentNotifications[notificationId].update(text,options);
}
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
persistentNotifications[notificationId].close();
delete persistentNotifications[notificationId];
}
});
RED.comms.subscribe("status/#",function(topic,msg) {
var parts = topic.split("/");
var node = RED.nodes.node(parts[1]);
if (node) {
if (msg.hasOwnProperty("text")) {
if (msg.text[0] !== ".") {
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
}
}
node.status = msg;
node.dirty = true;
RED.view.redraw();
}
});
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
var info;
if (topic == "notification/node/added") {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() {
$.get('nodes/'+id, function(data) {
appendNodeConfig(data);
});
});
});
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
loadIconList();
} else if (topic == "notification/node/removed") {
for (i=0;i<msg.length;i++) {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
}
}
loadIconList();
} else if (topic == "notification/node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
$.get('nodes/'+msg.id, function(data) {
appendNodeConfig(data);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
});
}
}
} else if (topic == "notification/node/disabled") {
if (msg.types) {
RED.nodes.disableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
}
} else if (topic == "notification/node/upgraded") {
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
}
// Refresh flow library to ensure any examples are updated
RED.library.loadFlowLibrary();
});
RED.comms.subscribe("event-log/#", function(topic,payload) {
var id = topic.substring(9);
RED.eventLog.log(id,payload);
});
}
function showAbout() {
$.get('red/about', function(data) {
var aboutHeader = '<div style="text-align:center;">'+
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.info.set(aboutHeader+marked(data));
RED.sidebar.info.show();
});
}
function loadEditor() {
var menuOptions = [];
if (RED.settings.theme("projects.enabled",false)) {
menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[
{id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"},
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
]});
}
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
// {id:"menu-item-view-show-grid",setting:"view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
// {id:"menu-item-view-snap-grid",setting:"view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
// {id:"menu-item-status",setting:"node-show-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true},
//null,
// {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[
// {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}},
// {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("ltr")}}},
// {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("rtl")}}},
// {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}}
// ]},
// null,
{id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true},
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
{id:"menu-item-event-log",label:RED._("eventLog.title"),onselect:"core:show-event-log"},
null
]});
menuOptions.push(null);
menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"},
{id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
]});
menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),options:[
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-export-dialog"},
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"}
]});
menuOptions.push(null);
menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"});
menuOptions.push(null);
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
]});
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"},
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"},
]});
menuOptions.push(null);
if (RED.settings.theme('palette.editable') !== false) {
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
menuOptions.push(null);
}
menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.settings"),onselect:"core:show-user-settings"});
menuOptions.push(null);
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
menuOptions.push({id:"menu-item-help",
label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
});
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
RED.view.init();
RED.userSettings.init();
RED.user.init();
RED.library.init();
RED.keyboard.init();
RED.palette.init();
RED.eventLog.init();
if (RED.settings.theme('palette.editable') !== false) {
RED.palette.editor.init();
} else {
console.log("Palette editor disabled");
}
RED.sidebar.init();
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.init();
} else {
console.log("Projects disabled");
}
RED.subflow.init();
RED.workspaces.init();
RED.clipboard.init();
RED.search.init();
RED.editor.init();
RED.diff.init();
RED.menu.init({id:"btn-sidemenu",options: menuOptions});
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.notifications.init();
RED.actions.add("core:show-about", showAbout);
RED.nodes.init();
RED.comms.connect();
$("#main-container").show();
$(".header-toolbar").show();
loadNodeList();
}
var initialised = false;
function init(options) {
if (initialised) {
throw new Error("RED already initialised");
}
initialised = true;
ace.require("ace/ext/language_tools");
options = options || {};
options.apiRootUrl = options.apiRootUrl || "";
if (options.apiRootUrl && !/\/$/.test(options.apiRootUrl)) {
options.apiRootUrl = options.apiRootUrl+"/";
}
RED.i18n.init(options, function() {
RED.settings.init(options, loadEditor);
})
}
return {
init: init
}
})();