mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
495 lines
21 KiB
JavaScript
495 lines
21 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.
|
|
**/
|
|
RED.library = (function() {
|
|
|
|
var exportToLibraryDialog;
|
|
var element = "node-input-name";
|
|
|
|
function loadFlowLibrary() {
|
|
$.getJSON("library/flows",function(data) {
|
|
//console.log(data);
|
|
|
|
var buildMenu = function(data,root) {
|
|
var i;
|
|
var li;
|
|
var a;
|
|
var ul = document.createElement("ul");
|
|
if (root === "") {
|
|
ul.id = "menu-item-import-library-submenu";
|
|
}
|
|
ul.className = "dropdown-menu";
|
|
if (data.d) {
|
|
for (i in data.d) {
|
|
if (data.d.hasOwnProperty(i)) {
|
|
li = document.createElement("li");
|
|
li.className = "dropdown-submenu pull-left";
|
|
a = document.createElement("a");
|
|
a.href="#";
|
|
var label = i.replace(/^@.*\//,"").replace(/^node-red-contrib-/,"").replace(/^node-red-node-/,"").replace(/-/," ").replace(/_/," ");
|
|
a.innerHTML = label;
|
|
li.appendChild(a);
|
|
li.appendChild(buildMenu(data.d[i],root+(root!==""?"/":"")+i));
|
|
ul.appendChild(li);
|
|
}
|
|
}
|
|
}
|
|
if (data.f) {
|
|
for (i in data.f) {
|
|
if (data.f.hasOwnProperty(i)) {
|
|
li = document.createElement("li");
|
|
a = document.createElement("a");
|
|
a.href="#";
|
|
a.innerHTML = data.f[i];
|
|
a.flowName = root+(root!==""?"/":"")+data.f[i];
|
|
a.onclick = function() {
|
|
$.get('library/flows/'+this.flowName, function(data) {
|
|
RED.view.importNodes(data);
|
|
});
|
|
};
|
|
li.appendChild(a);
|
|
ul.appendChild(li);
|
|
}
|
|
}
|
|
}
|
|
return ul;
|
|
};
|
|
var examples;
|
|
if (data.d && data.d._examples_) {
|
|
examples = data.d._examples_;
|
|
delete data.d._examples_;
|
|
}
|
|
var menu = buildMenu(data,"");
|
|
$("#menu-item-import-examples").remove();
|
|
if (examples) {
|
|
RED.menu.addItem("menu-item-import",{id:"menu-item-import-examples",label:RED._("menu.label.examples"),options:[]})
|
|
$("#menu-item-import-examples-submenu").replaceWith(buildMenu(examples,"_examples_"));
|
|
}
|
|
//TODO: need an api in RED.menu for this
|
|
$("#menu-item-import-library-submenu").replaceWith(menu);
|
|
});
|
|
}
|
|
|
|
function createUI(options) {
|
|
var libraryData = {};
|
|
var selectedLibraryItem = null;
|
|
var libraryEditor = null;
|
|
element = options.element || "node-input-name";
|
|
|
|
// Orion editor has set/getText
|
|
// ACE editor has set/getValue
|
|
// normalise to set/getValue
|
|
if (options.editor.setText) {
|
|
// Orion doesn't like having pos passed in, so proxy the call to drop it
|
|
options.editor.setValue = function(text,pos) {
|
|
options.editor.setText.call(options.editor,text);
|
|
}
|
|
}
|
|
if (options.editor.getText) {
|
|
options.editor.getValue = options.editor.getText;
|
|
}
|
|
|
|
function buildFileListItem(item) {
|
|
var li = document.createElement("li");
|
|
li.onmouseover = function(e) { $(this).addClass("list-hover"); };
|
|
li.onmouseout = function(e) { $(this).removeClass("list-hover"); };
|
|
return li;
|
|
}
|
|
|
|
function buildFileList(root,data) {
|
|
var ul = document.createElement("ul");
|
|
var li;
|
|
for (var i=0; i<data.length; i++) {
|
|
var v = data[i];
|
|
if (typeof v === "string") {
|
|
// directory
|
|
li = buildFileListItem(v);
|
|
li.onclick = (function () {
|
|
var dirName = v;
|
|
return function(e) {
|
|
var bcli = $('<li class="active"><span class="divider">/</span> <a href="#">'+dirName+'</a></li>');
|
|
$("a",bcli).click(function(e) {
|
|
$(this).parent().nextAll().remove();
|
|
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
|
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
|
});
|
|
e.stopPropagation();
|
|
});
|
|
var bc = $("#node-dialog-library-breadcrumbs");
|
|
$(".active",bc).removeClass("active");
|
|
bc.append(bcli);
|
|
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
|
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
|
});
|
|
}
|
|
})();
|
|
li.innerHTML = '<i class="fa fa-folder"></i> '+v+"</i>";
|
|
ul.appendChild(li);
|
|
} else {
|
|
// file
|
|
li = buildFileListItem(v);
|
|
li.innerHTML = v.name;
|
|
li.onclick = (function() {
|
|
var item = v;
|
|
return function(e) {
|
|
$(".list-selected",ul).removeClass("list-selected");
|
|
$(this).addClass("list-selected");
|
|
$.get("library/"+options.url+root+item.fn, function(data) {
|
|
selectedLibraryItem = item;
|
|
libraryEditor.setValue(data,-1);
|
|
});
|
|
}
|
|
})();
|
|
ul.appendChild(li);
|
|
}
|
|
}
|
|
return ul;
|
|
}
|
|
|
|
$('#'+element).css("width","calc(100% - 52px)").after(
|
|
'<div class="btn-group" style="margin-left:5px;">'+
|
|
'<a id="node-input-'+options.type+'-lookup" class="editor-button" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></a>'+
|
|
'<ul class="dropdown-menu pull-right" role="menu">'+
|
|
'<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">'+RED._("library.openLibrary")+'</a></li>'+
|
|
'<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">'+RED._("library.saveToLibrary")+'</a></li>'+
|
|
'</ul></div>'
|
|
);
|
|
|
|
$('#node-input-'+options.type+'-menu-open-library').click(function(e) {
|
|
$("#node-select-library").children().remove();
|
|
var bc = $("#node-dialog-library-breadcrumbs");
|
|
bc.children().first().nextAll().remove();
|
|
libraryEditor.setValue('',-1);
|
|
|
|
$.getJSON("library/"+options.url,function(data) {
|
|
$("#node-select-library").append(buildFileList("/",data));
|
|
$("#node-dialog-library-breadcrumbs a").click(function(e) {
|
|
$(this).parent().nextAll().remove();
|
|
$("#node-select-library").children().first().replaceWith(buildFileList("/",data));
|
|
e.stopPropagation();
|
|
});
|
|
$( "#node-dialog-library-lookup" ).dialog( "open" );
|
|
});
|
|
|
|
e.preventDefault();
|
|
});
|
|
|
|
$('#node-input-'+options.type+'-menu-save-library').click(function(e) {
|
|
//var found = false;
|
|
var name = $("#"+element).val().replace(/(^\s*)|(\s*$)/g,"");
|
|
|
|
//var buildPathList = function(data,root) {
|
|
// var paths = [];
|
|
// if (data.d) {
|
|
// for (var i in data.d) {
|
|
// var dn = root+(root==""?"":"/")+i;
|
|
// var d = {
|
|
// label:dn,
|
|
// files:[]
|
|
// };
|
|
// for (var f in data.d[i].f) {
|
|
// d.files.push(data.d[i].f[f].fn.split("/").slice(-1)[0]);
|
|
// }
|
|
// paths.push(d);
|
|
// paths = paths.concat(buildPathList(data.d[i],root+(root==""?"":"/")+i));
|
|
// }
|
|
// }
|
|
// return paths;
|
|
//};
|
|
$("#node-dialog-library-save-folder").attr("value","");
|
|
|
|
var filename = name.replace(/[^\w-]/g,"-");
|
|
if (filename === "") {
|
|
filename = "unnamed-"+options.type;
|
|
}
|
|
$("#node-dialog-library-save-filename").attr("value",filename+".js");
|
|
|
|
//var paths = buildPathList(libraryData,"");
|
|
//$("#node-dialog-library-save-folder").autocomplete({
|
|
// minLength: 0,
|
|
// source: paths,
|
|
// select: function( event, ui ) {
|
|
// $("#node-dialog-library-save-filename").autocomplete({
|
|
// minLength: 0,
|
|
// source: ui.item.files
|
|
// });
|
|
// }
|
|
//});
|
|
|
|
$( "#node-dialog-library-save" ).dialog( "open" );
|
|
e.preventDefault();
|
|
});
|
|
|
|
libraryEditor = ace.edit('node-select-library-text');
|
|
libraryEditor.setTheme("ace/theme/tomorrow");
|
|
if (options.mode) {
|
|
libraryEditor.getSession().setMode(options.mode);
|
|
}
|
|
libraryEditor.setOptions({
|
|
readOnly: true,
|
|
highlightActiveLine: false,
|
|
highlightGutterLine: false
|
|
});
|
|
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
|
|
libraryEditor.$blockScrolling = Infinity;
|
|
|
|
$( "#node-dialog-library-lookup" ).dialog({
|
|
title: RED._("library.typeLibrary", {type:options.type}),
|
|
modal: true,
|
|
autoOpen: false,
|
|
width: 800,
|
|
height: 450,
|
|
buttons: [
|
|
{
|
|
text: RED._("common.label.cancel"),
|
|
click: function() {
|
|
$( this ).dialog( "close" );
|
|
}
|
|
},
|
|
{
|
|
text: RED._("common.label.load"),
|
|
class: "primary",
|
|
click: function() {
|
|
if (selectedLibraryItem) {
|
|
for (var i=0; i<options.fields.length; i++) {
|
|
var field = options.fields[i];
|
|
$("#node-input-"+field).val(selectedLibraryItem[field]);
|
|
}
|
|
options.editor.setValue(libraryEditor.getValue(),-1);
|
|
}
|
|
$( this ).dialog( "close" );
|
|
}
|
|
}
|
|
],
|
|
open: function(e) {
|
|
var form = $("form",this);
|
|
form.height(form.parent().height()-30);
|
|
$("#node-select-library-text").height("100%");
|
|
$(".form-row:last-child",form).children().height(form.height()-60);
|
|
},
|
|
resize: function(e) {
|
|
var form = $("form",this);
|
|
form.height(form.parent().height()-30);
|
|
$(".form-row:last-child",form).children().height(form.height()-60);
|
|
}
|
|
});
|
|
|
|
function saveToLibrary(overwrite) {
|
|
var name = $("#"+element).val().replace(/(^\s*)|(\s*$)/g,"");
|
|
if (name === "") {
|
|
name = RED._("library.unnamedType",{type:options.type});
|
|
}
|
|
var filename = $("#node-dialog-library-save-filename").val().replace(/(^\s*)|(\s*$)/g,"");
|
|
var pathname = $("#node-dialog-library-save-folder").val().replace(/(^\s*)|(\s*$)/g,"");
|
|
if (filename === "" || !/.+\.js$/.test(filename)) {
|
|
RED.notify(RED._("library.invalidFilename"),"warning");
|
|
return;
|
|
}
|
|
var fullpath = pathname+(pathname===""?"":"/")+filename;
|
|
if (!overwrite) {
|
|
//var pathnameParts = pathname.split("/");
|
|
//var exists = false;
|
|
//var ds = libraryData;
|
|
//for (var pnp in pathnameParts) {
|
|
// if (ds.d && pathnameParts[pnp] in ds.d) {
|
|
// ds = ds.d[pathnameParts[pnp]];
|
|
// } else {
|
|
// ds = null;
|
|
// break;
|
|
// }
|
|
//}
|
|
//if (ds && ds.f) {
|
|
// for (var f in ds.f) {
|
|
// if (ds.f[f].fn == fullpath) {
|
|
// exists = true;
|
|
// break;
|
|
// }
|
|
// }
|
|
//}
|
|
//if (exists) {
|
|
// $("#node-dialog-library-save-content").html(RED._("library.dialogSaveOverwrite",{libraryType:options.type,libraryName:fullpath}));
|
|
// $("#node-dialog-library-save-confirm").dialog( "open" );
|
|
// return;
|
|
//}
|
|
}
|
|
var queryArgs = [];
|
|
var data = {};
|
|
for (var i=0; i<options.fields.length; i++) {
|
|
var field = options.fields[i];
|
|
if (field == "name") {
|
|
data.name = name;
|
|
} else {
|
|
data[field] = $("#node-input-"+field).val();
|
|
}
|
|
}
|
|
|
|
data.text = options.editor.getValue();
|
|
$.ajax({
|
|
url:"library/"+options.url+'/'+fullpath,
|
|
type: "POST",
|
|
data: JSON.stringify(data),
|
|
contentType: "application/json; charset=utf-8"
|
|
}).done(function(data,textStatus,xhr) {
|
|
RED.notify(RED._("library.savedType", {type:options.type}),"success");
|
|
}).fail(function(xhr,textStatus,err) {
|
|
if (xhr.status === 401) {
|
|
RED.notify(RED._("library.saveFailed",{message:RED._("user.notAuthorized")}),"error");
|
|
} else {
|
|
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
|
}
|
|
});
|
|
}
|
|
$( "#node-dialog-library-save-confirm" ).dialog({
|
|
title: RED._("library.saveToLibrary"),
|
|
modal: true,
|
|
autoOpen: false,
|
|
width: 530,
|
|
height: 230,
|
|
buttons: [
|
|
{
|
|
text: RED._("common.label.cancel"),
|
|
click: function() {
|
|
$( this ).dialog( "close" );
|
|
}
|
|
},
|
|
{
|
|
text: RED._("common.label.save"),
|
|
class: "primary",
|
|
click: function() {
|
|
saveToLibrary(true);
|
|
$( this ).dialog( "close" );
|
|
}
|
|
}
|
|
]
|
|
});
|
|
$( "#node-dialog-library-save" ).dialog({
|
|
title: RED._("library.saveToLibrary"),
|
|
modal: true,
|
|
autoOpen: false,
|
|
width: 530,
|
|
height: 230,
|
|
buttons: [
|
|
{
|
|
text: RED._("common.label.cancel"),
|
|
click: function() {
|
|
$( this ).dialog( "close" );
|
|
}
|
|
},
|
|
{
|
|
text: RED._("common.label.save"),
|
|
class: "primary",
|
|
click: function() {
|
|
saveToLibrary(false);
|
|
$( this ).dialog( "close" );
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
}
|
|
|
|
function exportFlow() {
|
|
//TODO: don't rely on the main dialog
|
|
var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes);
|
|
$("#node-input-library-filename").attr('nodes',JSON.stringify(nns));
|
|
exportToLibraryDialog.dialog( "open" );
|
|
}
|
|
|
|
return {
|
|
init: function() {
|
|
|
|
RED.actions.add("core:library-export",exportFlow);
|
|
|
|
RED.events.on("view:selection-changed",function(selection) {
|
|
if (!selection.nodes) {
|
|
RED.menu.setDisabled("menu-item-export",true);
|
|
RED.menu.setDisabled("menu-item-export-clipboard",true);
|
|
RED.menu.setDisabled("menu-item-export-library",true);
|
|
} else {
|
|
RED.menu.setDisabled("menu-item-export",false);
|
|
RED.menu.setDisabled("menu-item-export-clipboard",false);
|
|
RED.menu.setDisabled("menu-item-export-library",false);
|
|
}
|
|
});
|
|
|
|
if (RED.settings.theme("menu.menu-item-import-library") !== false) {
|
|
loadFlowLibrary();
|
|
}
|
|
|
|
exportToLibraryDialog = $('<div id="library-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
|
.appendTo("body")
|
|
.dialog({
|
|
modal: true,
|
|
autoOpen: false,
|
|
width: 500,
|
|
resizable: false,
|
|
title: RED._("library.exportToLibrary"),
|
|
buttons: [
|
|
{
|
|
id: "library-dialog-cancel",
|
|
text: RED._("common.label.cancel"),
|
|
click: function() {
|
|
$( this ).dialog( "close" );
|
|
}
|
|
},
|
|
{
|
|
id: "library-dialog-ok",
|
|
class: "primary",
|
|
text: RED._("common.label.export"),
|
|
click: function() {
|
|
//TODO: move this to RED.library
|
|
var flowName = $("#node-input-library-filename").val();
|
|
if (!/^\s*$/.test(flowName)) {
|
|
$.ajax({
|
|
url:'library/flows/'+flowName,
|
|
type: "POST",
|
|
data: $("#node-input-library-filename").attr('nodes'),
|
|
contentType: "application/json; charset=utf-8"
|
|
}).done(function() {
|
|
RED.library.loadFlowLibrary();
|
|
RED.notify(RED._("library.savedNodes"),"success");
|
|
}).fail(function(xhr,textStatus,err) {
|
|
if (xhr.status === 401) {
|
|
RED.notify(RED._("library.saveFailed",{message:RED._("user.notAuthorized")}),"error");
|
|
} else {
|
|
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
|
}
|
|
});
|
|
}
|
|
$( this ).dialog( "close" );
|
|
}
|
|
}
|
|
],
|
|
open: function(e) {
|
|
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
|
},
|
|
close: function(e) {
|
|
}
|
|
});
|
|
exportToLibraryDialog.children(".dialog-form").append($(
|
|
'<div class="form-row">'+
|
|
'<label for="node-input-library-filename" data-i18n="[append]editor:library.filename"><i class="fa fa-file"></i> </label>'+
|
|
'<input type="text" id="node-input-library-filename" data-i18n="[placeholder]editor:library.fullFilenamePlaceholder">'+
|
|
'<input type="text" style="display: none;" />'+ // Second hidden input to prevent submit on Enter
|
|
'</div>'
|
|
));
|
|
},
|
|
create: createUI,
|
|
loadFlowLibrary: loadFlowLibrary,
|
|
|
|
export: exportFlow
|
|
}
|
|
})();
|