mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
444 lines
18 KiB
JavaScript
444 lines
18 KiB
JavaScript
/**
|
|
* Copyright 2013, 2015 IBM Corp.
|
|
*
|
|
* 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() {
|
|
|
|
var deploymentTypes = {
|
|
"full":{img:"images/deploy-full-o.png"},
|
|
"nodes":{img:"images/deploy-nodes-o.png"},
|
|
"flows":{img:"images/deploy-flows-o.png"}
|
|
}
|
|
var deploymentType = "full";
|
|
|
|
function save(force) {
|
|
if (RED.view.dirty()) {
|
|
//$("#debug-tab-clear").click(); // uncomment this to auto clear debug on deploy
|
|
|
|
if (!force) {
|
|
var invalid = false;
|
|
var unknownNodes = [];
|
|
RED.nodes.eachNode(function(node) {
|
|
invalid = invalid || !node.valid;
|
|
if (node.type === "unknown") {
|
|
if (unknownNodes.indexOf(node.name) == -1) {
|
|
unknownNodes.push(node.name);
|
|
}
|
|
invalid = true;
|
|
}
|
|
});
|
|
if (invalid) {
|
|
if (unknownNodes.length > 0) {
|
|
$( "#node-dialog-confirm-deploy-config" ).hide();
|
|
$( "#node-dialog-confirm-deploy-unknown" ).show();
|
|
var list = "<li>"+unknownNodes.join("</li><li>")+"</li>";
|
|
$( "#node-dialog-confirm-deploy-unknown-list" ).html(list);
|
|
} else {
|
|
$( "#node-dialog-confirm-deploy-config" ).show();
|
|
$( "#node-dialog-confirm-deploy-unknown" ).hide();
|
|
}
|
|
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
|
|
return;
|
|
}
|
|
}
|
|
var nns = RED.nodes.createCompleteNodeSet();
|
|
|
|
$("#btn-icn-deploy").removeClass('fa-download');
|
|
$("#btn-icn-deploy").addClass('spinner');
|
|
RED.view.dirty(false);
|
|
|
|
$.ajax({
|
|
url:"flows",
|
|
type: "POST",
|
|
data: JSON.stringify(nns),
|
|
contentType: "application/json; charset=utf-8",
|
|
headers: {
|
|
"Node-RED-Deployment-Type":deploymentType
|
|
}
|
|
}).done(function(data,textStatus,xhr) {
|
|
RED.notify("Successfully deployed","success");
|
|
RED.nodes.eachNode(function(node) {
|
|
if (node.changed) {
|
|
node.dirty = true;
|
|
node.changed = false;
|
|
}
|
|
if(node.credentials) {
|
|
delete node.credentials;
|
|
}
|
|
});
|
|
RED.nodes.eachConfig(function (confNode) {
|
|
if (confNode.credentials) {
|
|
delete confNode.credentials;
|
|
}
|
|
});
|
|
// Once deployed, cannot undo back to a clean state
|
|
RED.history.markAllDirty();
|
|
RED.view.redraw();
|
|
}).fail(function(xhr,textStatus,err) {
|
|
RED.view.dirty(true);
|
|
if (xhr.responseText) {
|
|
RED.notify("<strong>Error</strong>: "+xhr.responseText,"error");
|
|
} else {
|
|
RED.notify("<strong>Error</strong>: no response from server","error");
|
|
}
|
|
}).always(function() {
|
|
$("#btn-icn-deploy").removeClass('spinner');
|
|
$("#btn-icn-deploy").addClass('fa-download');
|
|
});
|
|
}
|
|
}
|
|
|
|
$('#btn-deploy').click(function() { save(); });
|
|
|
|
$( "#node-dialog-confirm-deploy" ).dialog({
|
|
title: "Confirm deploy",
|
|
modal: true,
|
|
autoOpen: false,
|
|
width: 530,
|
|
height: 230,
|
|
buttons: [
|
|
{
|
|
text: "Confirm deploy",
|
|
click: function() {
|
|
save(true);
|
|
$( this ).dialog( "close" );
|
|
}
|
|
},
|
|
{
|
|
text: "Cancel",
|
|
click: function() {
|
|
$( this ).dialog( "close" );
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
function loadSettings() {
|
|
RED.settings.init(loadNodeList);
|
|
}
|
|
|
|
function loadNodeList() {
|
|
$.ajax({
|
|
headers: {
|
|
"Accept":"application/json"
|
|
},
|
|
cache: false,
|
|
url: 'nodes',
|
|
success: function(data) {
|
|
RED.nodes.setNodeList(data);
|
|
loadNodes();
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadNodes() {
|
|
$.ajax({
|
|
headers: {
|
|
"Accept":"text/html"
|
|
},
|
|
cache: false,
|
|
url: 'nodes',
|
|
success: function(data) {
|
|
$("body").append(data);
|
|
$(".palette-spinner").hide();
|
|
$(".palette-scroll").show();
|
|
$("#palette-search").show();
|
|
loadFlows();
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadFlows() {
|
|
$.ajax({
|
|
headers: {
|
|
"Accept":"application/json"
|
|
},
|
|
cache: false,
|
|
url: 'flows',
|
|
success: function(nodes) {
|
|
RED.nodes.import(nodes);
|
|
RED.view.dirty(false);
|
|
RED.view.redraw();
|
|
RED.comms.subscribe("status/#",function(topic,msg) {
|
|
var parts = topic.split("/");
|
|
var node = RED.nodes.node(parts[1]);
|
|
if (node) {
|
|
node.status = msg;
|
|
if (statusEnabled) {
|
|
node.dirty = true;
|
|
RED.view.redraw();
|
|
}
|
|
}
|
|
});
|
|
RED.comms.subscribe("node/#",function(topic,msg) {
|
|
var i,m;
|
|
var typeList;
|
|
var info;
|
|
|
|
if (topic == "node/added") {
|
|
var addedTypes = [];
|
|
for (i=0;i<msg.length;i++) {
|
|
m = msg[i];
|
|
var id = m.id;
|
|
RED.nodes.addNodeSet(m);
|
|
if (m.loaded) {
|
|
addedTypes = addedTypes.concat(m.types);
|
|
$.get('nodes/'+id, function(data) {
|
|
$("body").append(data);
|
|
});
|
|
}
|
|
}
|
|
if (addedTypes.length) {
|
|
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
|
RED.notify("Node"+(addedTypes.length!=1 ? "s":"")+" added to palette:"+typeList,"success");
|
|
}
|
|
} else if (topic == "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("Node"+(m.types.length!=1 ? "s":"")+" removed from palette:"+typeList,"success");
|
|
}
|
|
}
|
|
} else if (topic == "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("Node"+(msg.types.length!=1 ? "s":"")+" enabled:"+typeList,"success");
|
|
} else {
|
|
$.get('nodes/'+msg.id, function(data) {
|
|
$("body").append(data);
|
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
|
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" added to palette:"+typeList,"success");
|
|
});
|
|
}
|
|
}
|
|
} else if (topic == "node/disabled") {
|
|
if (msg.types) {
|
|
RED.nodes.disableNodeSet(msg.id);
|
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
|
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" disabled:"+typeList,"success");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
var statusEnabled = false;
|
|
function toggleStatus(state) {
|
|
statusEnabled = state;
|
|
RED.view.status(statusEnabled);
|
|
}
|
|
|
|
function showHelp() {
|
|
|
|
var dialog = $('#node-help');
|
|
|
|
//$("#node-help").draggable({
|
|
// handle: ".modal-header"
|
|
//});
|
|
|
|
dialog.on('show',function() {
|
|
RED.keyboard.disable();
|
|
});
|
|
dialog.on('hidden',function() {
|
|
RED.keyboard.enable();
|
|
});
|
|
|
|
dialog.modal();
|
|
}
|
|
|
|
function changeDeploymentType(type) {
|
|
deploymentType = type;
|
|
$("#btn-deploy img").attr("src",deploymentTypes[type].img);
|
|
}
|
|
|
|
function loadEditor() {
|
|
RED.menu.init({id:"btn-sidemenu",
|
|
options: [
|
|
{id:"btn-sidebar",label:"Sidebar",toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true},
|
|
{id:"btn-node-status",label:"Display node status",toggle:true,onselect:toggleStatus, selected: true},
|
|
null,
|
|
{id:"btn-import-menu",label:"Import",options:[
|
|
{id:"btn-import-clipboard",label:"Clipboard",onselect:RED.view.showImportNodesDialog},
|
|
{id:"btn-import-library",label:"Library",options:[]}
|
|
]},
|
|
{id:"btn-export-menu",label:"Export",disabled:true,options:[
|
|
{id:"btn-export-clipboard",label:"Clipboard",disabled:true,onselect:RED.view.showExportNodesDialog},
|
|
{id:"btn-export-library",label:"Library",disabled:true,onselect:RED.view.showExportNodesLibraryDialog}
|
|
]},
|
|
null,
|
|
{id:"btn-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show},
|
|
null,
|
|
{id:"btn-subflow-menu",label:"Subflows", options: [
|
|
{id:"btn-create-subflow",label:"Create subflow",onselect:RED.view.createSubflow},
|
|
{id:"btn-convert-subflow",label:"Selection to subflow",disabled:true,onselect:RED.view.convertToSubflow},
|
|
]},
|
|
null,
|
|
{id:"btn-workspace-menu",label:"Workspaces",options:[
|
|
{id:"btn-workspace-add",label:"Add"},
|
|
{id:"btn-workspace-edit",label:"Rename"},
|
|
{id:"btn-workspace-delete",label:"Delete"},
|
|
null
|
|
]},
|
|
null,
|
|
{id:"btn-keyboard-shortcuts",label:"Keyboard Shortcuts",onselect:showHelp},
|
|
{id:"btn-help",label:"Node-RED Website", href:"http://nodered.org/docs"}
|
|
]
|
|
});
|
|
|
|
RED.menu.init({id:"btn-deploy-options",
|
|
options: [
|
|
{id:"btn-deploy-full",toggle:"deploy-type",icon:"images/deploy-full.png",label:"Full",sublabel:"Deploys everything in the workspace",onselect:function(s) { if(s){changeDeploymentType("full")}}},
|
|
{id:"btn-deploy-flow",toggle:"deploy-type",icon:"images/deploy-flows.png",label:"Modified Flows",sublabel:"Only deploys flows that contain changed nodes", onselect:function(s) {if(s){changeDeploymentType("flows")}}},
|
|
{id:"btn-deploy-node",toggle:"deploy-type",icon:"images/deploy-nodes.png",label:"Modified Nodes",sublabel:"Only deploys nodes that have changed",onselect:function(s) { if(s){changeDeploymentType("nodes")}}}
|
|
]
|
|
});
|
|
|
|
if (RED.settings.user) {
|
|
$("#header .username").html(RED.settings.user.username);
|
|
$("#header .user").show();
|
|
RED.menu.addItem("btn-sidemenu", null);
|
|
RED.menu.addItem("btn-sidemenu",{
|
|
id:"btn-logout",
|
|
icon:"fa fa-user",
|
|
label:"Logout",
|
|
onselect:function() {
|
|
// TODO: invalidate token
|
|
|
|
$.ajax({
|
|
url: "auth/revoke",
|
|
type: "POST",
|
|
data: {token:RED.settings.get("auth-tokens").access_token},
|
|
success: function() {
|
|
RED.settings.remove("auth-tokens");
|
|
document.location.reload(true);
|
|
}
|
|
})
|
|
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
$("#main-container").show();
|
|
$("#btn-deploy").show();
|
|
$("#btn-sidemenu").show();
|
|
|
|
RED.library.init();
|
|
RED.palette.init();
|
|
RED.sidebar.init();
|
|
RED.view.init();
|
|
|
|
RED.keyboard.add(/* ? */ 191,{shift:true},function(){showHelp();d3.event.preventDefault();});
|
|
RED.comms.connect();
|
|
loadNodeList();
|
|
}
|
|
|
|
function showLogin() {
|
|
var dialog = $("#node-dialog-login");
|
|
dialog.dialog({
|
|
autoOpen: false,
|
|
dialogClass: "ui-dialog-no-close",
|
|
modal: true,
|
|
closeOnEscape: false,
|
|
width: 600,
|
|
resizable: false,
|
|
draggable: false
|
|
});
|
|
$("#node-dialog-login-fields").empty();
|
|
$.ajax({
|
|
dataType: "json",
|
|
url: "auth/login",
|
|
success: function(data) {
|
|
if (data.type == "credentials") {
|
|
for (var i=0;i<data.prompts.length;i++) {
|
|
var field = data.prompts[i];
|
|
var row = $("<div/>",{class:"form-row"});
|
|
$('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row);
|
|
$('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'"/>').appendTo(row);
|
|
row.appendTo("#node-dialog-login-fields");
|
|
}
|
|
$('<div class="form-row" style="text-align: right"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">Login failed</span><img src="spin.svg" style="height: 30px" class="login-spinner hide"/> <a href="#" id="node-dialog-login-submit">Login</a></div>').appendTo("#node-dialog-login-fields");
|
|
$("#node-dialog-login-submit").button().click(function( event ) {
|
|
$("#node-dialog-login-submit").button("option","disabled",true);
|
|
$("#node-dialog-login-failed").hide();
|
|
$(".login-spinner").show();
|
|
|
|
var body = {
|
|
client_id: "node-red-admin",
|
|
grant_type: "password",
|
|
scope:"*"
|
|
}
|
|
for (var i=0;i<data.prompts.length;i++) {
|
|
var field = data.prompts[i];
|
|
body[field.id] = $("#node-dialog-login-"+field.id).val();
|
|
}
|
|
$.ajax({
|
|
url:"auth/token",
|
|
type: "POST",
|
|
data: body
|
|
}).done(function(data,textStatus,xhr) {
|
|
RED.settings.set("auth-tokens",data);
|
|
$("#node-dialog-login").dialog("close");
|
|
load();
|
|
}).fail(function(jqXHR,textStatus,errorThrown) {
|
|
RED.settings.remove("auth-tokens");
|
|
$("#node-dialog-login-failed").show();
|
|
}).always(function() {
|
|
$("#node-dialog-login-submit").button("option","disabled",false);
|
|
$(".login-spinner").hide();
|
|
});
|
|
event.preventDefault();
|
|
});
|
|
}
|
|
dialog.dialog("open");
|
|
}
|
|
});
|
|
}
|
|
|
|
function load() {
|
|
RED.settings.init(function(err,msg) {
|
|
if (err) {
|
|
if (err === 401) {
|
|
showLogin();
|
|
} else {
|
|
console.log("Unexpected error:",err,msg);
|
|
}
|
|
} else {
|
|
loadEditor();
|
|
}
|
|
});
|
|
}
|
|
|
|
$(function() {
|
|
|
|
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
|
document.title = "Node-RED : "+window.location.hostname;
|
|
}
|
|
$("#btn-deploy").hide();
|
|
$("#btn-sidemenu").hide();
|
|
|
|
load();
|
|
});
|
|
|
|
|
|
return {
|
|
};
|
|
})();
|