node-red/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js

1290 lines
57 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.clipboard = (function() {
var dialog;
var dialogContainer;
var exportNodesDialog;
var importNodesDialog;
var disabled = false;
var popover;
var currentPopoverError;
var activeTab;
var libraryBrowser;
var activeLibraries = {};
var pendingImportConfig;
function downloadData(file, data) {
if (window.navigator.msSaveBlob) {
// IE11 workaround
// IE does not support data uri scheme for downloading data
var blob = new Blob([data], {
type: "data:text/plain;charset=utf-8"
});
navigator.msSaveBlob(blob, file);
}
else {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
element.setAttribute('download', file);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
}
function setupDialogs() {
dialog = $('<div id="red-ui-clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("#red-ui-editor")
.dialog({
modal: true,
autoOpen: false,
width: 700,
resizable: false,
classes: {
"ui-dialog": "red-ui-editor-dialog",
"ui-dialog-titlebar-close": "hide",
"ui-widget-overlay": "red-ui-editor-dialog"
},
buttons: [
{ // red-ui-clipboard-dialog-cancel
id: "red-ui-clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{ // red-ui-clipboard-dialog-download
id: "red-ui-clipboard-dialog-download",
class: "primary",
text: RED._("clipboard.download"),
click: function() {
var data = $("#red-ui-clipboard-dialog-export-text").val();
downloadData("flows.json", data);
$( this ).dialog( "close" );
}
},
{ // red-ui-clipboard-dialog-export
id: "red-ui-clipboard-dialog-export",
class: "primary",
text: RED._("clipboard.export.copy"),
click: function() {
if (activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
var flowData = $("#red-ui-clipboard-dialog-export-text").val();
// Close the dialog first otherwise FireFox won't focus the hidden
// clipboard element in copyText
$( this ).dialog( "close" );
copyText(flowData);
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
} else {
var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
var selectedPath = activeLibraries[activeTab].getSelected();
if (!selectedPath.children) {
selectedPath = selectedPath.parent;
}
var filename = $("#red-ui-clipboard-dialog-tab-library-name").val().trim();
var saveFlow = function() {
$.ajax({
url:'library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path + filename,
type: "POST",
data: flowToExport,
contentType: "application/json; charset=utf-8"
}).done(function() {
$(dialog).dialog( "close" );
RED.notify(RED._("library.exportedToLibrary"),"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");
}
});
}
if (selectedPath.children) {
var exists = false;
selectedPath.children.forEach(function(f) {
if (f.label === filename) {
exists = true;
}
});
if (exists) {
dialog.dialog("close");
var notification = RED.notify(RED._("clipboard.export.exists",{file:RED.utils.sanitize(filename)}),{
type: "warning",
fixed: true,
buttons: [{
text: RED._("common.label.cancel"),
click: function() {
notification.hideNotification()
dialog.dialog( "open" );
}
},{
text: RED._("clipboard.export.overwrite"),
click: function() {
notification.hideNotification()
saveFlow();
}
}]
});
} else {
saveFlow();
}
} else {
saveFlow();
}
}
}
},
{ // red-ui-clipboard-dialog-ok
id: "red-ui-clipboard-dialog-ok",
class: "primary",
text: RED._("common.label.import"),
click: function() {
var addNewFlow = ($("#red-ui-clipboard-dialog-import-opt > a.selected").attr('id') === 'red-ui-clipboard-dialog-import-opt-new');
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
} else {
var selectedPath = activeLibraries[activeTab].getSelected();
if (selectedPath.path) {
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
importNodes(data,addNewFlow);
});
}
}
$( this ).dialog( "close" );
}
},
{ // red-ui-clipboard-dialog-import-conflict
id: "red-ui-clipboard-dialog-import-conflict",
class: "primary",
text: RED._("clipboard.import.importSelected"),
click: function() {
var importMap = {};
$('#red-ui-clipboard-dialog-import-conflicts-list input[type="checkbox"]').each(function() {
importMap[$(this).attr("data-node-id")] = this.checked?"import":"skip";
})
$('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').each(function() {
if (!$(this).attr("disabled")) {
importMap[$(this).attr("data-node-id")] = this.checked?"replace":"copy"
}
})
// skip - don't import
// import - import as-is
// copy - import with new id
// replace - import over the top of existing
pendingImportConfig.importOptions.importMap = importMap;
var newNodes = pendingImportConfig.importNodes.filter(function(n) {
if (!importMap[n.id] || importMap[n.z]) {
importMap[n.id] = importMap[n.z];
}
return importMap[n.id] !== "skip"
})
// 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" );
}
}
],
open: function( event, ui ) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
if (popover) {
popover.close(true);
currentPopoverError = null;
}
}
});
dialogContainer = dialog.children(".dialog-form");
exportNodesDialog =
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
'<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-box">'+
'<div class="red-ui-clipboard-dialog-tabs">'+
'<ul id="red-ui-clipboard-dialog-export-tabs"></ul>'+
'</div>'+
'<div id="red-ui-clipboard-dialog-export-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard-tab-bar">'+
'<ul id="red-ui-clipboard-dialog-export-tab-clipboard-tabs"></ul>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-preview">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard-preview-list"></div>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-json">'+
'<div class="form-row" style="height:calc(100% - 40px)">'+
'<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
'</div>'+
'<div class="form-row" style="text-align: right;">'+
'<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
'<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
'</span>'+
'</div>'+
'</div>'+
'</div>'+
'<div class="form-row" id="red-ui-clipboard-dialog-export-tab-library-filename">'+
'<label data-i18n="clipboard.export.exportAs"></label><input id="red-ui-clipboard-dialog-tab-library-name" type="text">'+
'</div>'+
'</div>'+
'</div>'
;
importNodesDialog =
'<div class="red-ui-clipboard-dialog-box" style="margin-bottom: 12px">'+
'<div class="red-ui-clipboard-dialog-tabs">'+
'<ul id="red-ui-clipboard-dialog-import-tabs"></ul>'+
'</div>'+
'<div id="red-ui-clipboard-dialog-import-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+
'<div id="red-ui-clipboard-dialog-import-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+
'<div class="form-row"><span data-i18n="clipboard.pasteNodes"></span>'+
' <a class="red-ui-button" id="red-ui-clipboard-dialog-import-file-upload-btn"><i class="fa fa-upload"></i> <span data-i18n="clipboard.selectFile"></span></a>'+
'<input type="file" id="red-ui-clipboard-dialog-import-file-upload" accept=".json" style="display:none">'+
'</div>'+
'<div class="form-row" style="height:calc(100% - 47px)">'+
'<textarea id="red-ui-clipboard-dialog-import-text"></textarea>'+
'</div>'+
'</div>'+
'</div>'+
'</div>'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.import.import"></label>'+
'<span id="red-ui-clipboard-dialog-import-opt" class="button-group">'+
'<a id="red-ui-clipboard-dialog-import-opt-current" class="red-ui-button toggle selected" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="red-ui-clipboard-dialog-import-opt-new" class="red-ui-button toggle" href="#" data-i18n="clipboard.import.newFlow"></a>'+
'</span>'+
'</div>';
importConflictsDialog =
'<div class="form-row">'+
'<div class="form-row"><p data-i18n="clipboard.import.conflictNotification1"></p><p data-i18n="clipboard.import.conflictNotification2"></p></div>'+
'<div class="red-ui-clipboard-dialog-import-conflicts-list-container">'+
'<div id="red-ui-clipboard-dialog-import-conflicts-list"></div>'+
'</div>'+
'</div>';
}
var validateExportFilenameTimeout
function validateExportFilename() {
if (validateExportFilenameTimeout) {
clearTimeout(validateExportFilenameTimeout);
}
validateExportFilenameTimeout = setTimeout(function() {
var filenameInput = $("#red-ui-clipboard-dialog-tab-library-name");
var filename = filenameInput.val().trim();
var valid = filename.length > 0 && !/[\/\\]/.test(filename);
if (valid) {
filenameInput.removeClass("input-error");
$("#red-ui-clipboard-dialog-export").button("enable");
} else {
filenameInput.addClass("input-error");
$("#red-ui-clipboard-dialog-export").button("disable");
}
},100);
}
var validateImportTimeout;
function validateImport() {
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
if (validateImportTimeout) {
clearTimeout(validateImportTimeout);
}
validateImportTimeout = setTimeout(function() {
var importInput = $("#red-ui-clipboard-dialog-import-text");
var v = importInput.val().trim();
if (v === "") {
popover.close(true);
currentPopoverError = null;
importInput.removeClass("input-error");
$("#red-ui-clipboard-dialog-ok").button("disable");
return;
}
try {
if (!/^\[[\s\S]*\]$/m.test(v)) {
throw new Error(RED._("clipboard.import.errors.notArray"));
}
var res = JSON.parse(v);
for (var i=0;i<res.length;i++) {
if (typeof res[i] !== "object") {
throw new Error(RED._("clipboard.import.errors.itemNotObject",{index:i}));
}
if (!res[i].hasOwnProperty('id')) {
throw new Error(RED._("clipboard.import.errors.missingId",{index:i}));
}
if (!res[i].hasOwnProperty('type')) {
throw new Error(RED._("clipboard.import.errors.missingType",{index:i}));
}
}
currentPopoverError = null;
popover.close(true);
importInput.removeClass("input-error");
importInput.val(v);
$("#red-ui-clipboard-dialog-ok").button("enable");
} catch(err) {
if (v !== "") {
importInput.addClass("input-error");
var errString = err.toString();
if (errString !== currentPopoverError) {
// Display the error as-is.
// Error messages are only in English. Each browser has its
// own set of messages with very little consistency.
// To provide translated messages this code will either need to:
// - reduce everything down to 'unexpected token at position x'
// which is the least useful, but most consistent message
// - use a custom/library parser that gives consistent messages
// which can be translated.
var message = $('<div class="red-ui-clipboard-import-error"></div>').text(errString);
var errorPos;
// Chrome error messages
var m = /at position (\d+)/i.exec(errString);
if (m) {
errorPos = parseInt(m[1]);
} else {
// Firefox error messages
m = /at line (\d+) column (\d+)/i.exec(errString);
if (m) {
var line = parseInt(m[1])-1;
var col = parseInt(m[2])-1;
var lines = v.split("\n");
errorPos = 0;
for (var i=0;i<line;i++) {
errorPos += lines[i].length+1;
}
errorPos += col;
} else {
// Safari doesn't provide any position information
// IE: tbd
}
}
if (errorPos !== undefined) {
v = v.replace(/\n/g,"↵");
var index = parseInt(m[1]);
var parseError = $('<div>').appendTo(message);
var code = $('<pre>').appendTo(parseError);
$('<span>').text(v.substring(errorPos-12,errorPos)).appendTo(code)
$('<span class="error">').text(v.charAt(errorPos)).appendTo(code);
$('<span>').text(v.substring(errorPos+1,errorPos+12)).appendTo(code);
}
popover.close(true).setContent(message).open();
currentPopoverError = errString;
}
} else {
currentPopoverError = null;
}
$("#red-ui-clipboard-dialog-ok").button("disable");
}
},100);
} else {
var file = activeLibraries[activeTab].getSelected();
if (file && file.label && !file.children) {
$("#red-ui-clipboard-dialog-ok").button("enable");
} else {
$("#red-ui-clipboard-dialog-ok").button("disable");
}
}
}
function showImportNodes(mode) {
if (disabled) {
return;
}
mode = mode || "clipboard";
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
var tabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-import-tabs",
vertical: true,
onchange: function(tab) {
$("#red-ui-clipboard-dialog-import-tabs-content").children().hide();
$("#" + tab.id).show();
activeTab = tab.id;
if (popover) {
popover.close(true);
currentPopoverError = null;
}
if (tab.id === "red-ui-clipboard-dialog-import-tab-clipboard") {
$("#red-ui-clipboard-dialog-import-text").trigger("focus");
} else {
activeLibraries[tab.id].focus();
}
validateImport();
}
});
tabs.addTab({
id: "red-ui-clipboard-dialog-import-tab-clipboard",
label: RED._("clipboard.clipboard")
});
var libraries = RED.settings.libraries || [];
libraries.forEach(function(lib) {
var tabId = "red-ui-clipboard-dialog-import-tab-library-"+lib.id
tabs.addTab({
id: tabId,
label: RED._(lib.label||lib.id)
})
var content = $('<div id="red-ui-clipboard-dialog-import-tab-library" class="red-ui-clipboard-dialog-tab-library"></div>')
.attr("id",tabId)
.hide()
.appendTo("#red-ui-clipboard-dialog-import-tabs-content");
var browser = RED.library.createBrowser({
container: content,
onselect: function(file) {
if (file && file.label && !file.children) {
$("#red-ui-clipboard-dialog-ok").button("enable");
} else {
$("#red-ui-clipboard-dialog-ok").button("disable");
}
},
onconfirm: function(item) {
if (item && item.label && !item.children) {
$("#red-ui-clipboard-dialog-ok").trigger("click");
}
}
})
loadFlowLibrary(browser,lib);
activeLibraries[tabId] = browser;
})
$("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename);
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
dialogContainer.i18n();
$("#red-ui-clipboard-dialog-ok").show();
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-download").hide();
$("#red-ui-clipboard-dialog-import-conflict").hide();
$("#red-ui-clipboard-dialog-ok").button("disable");
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
$("#red-ui-clipboard-dialog-import-opt > a").on("click", function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
});
$("#red-ui-clipboard-dialog-import-file-upload").on("change", function() {
var fileReader = new FileReader();
fileReader.onload = function () {
$("#red-ui-clipboard-dialog-import-text").val(fileReader.result);
validateImport();
};
fileReader.readAsText($(this).prop('files')[0]);
})
$("#red-ui-clipboard-dialog-import-file-upload-btn").on("click", function(evt) {
evt.preventDefault();
$("#red-ui-clipboard-dialog-import-file-upload").trigger("click");
})
tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+mode);
if (mode === 'clipboard') {
setTimeout(function() {
$("#red-ui-clipboard-dialog-import-text").trigger("focus");
},100)
}
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
dialog.dialog("option","title",RED._("clipboard.importNodes"))
.dialog("option","width",700)
.dialog("open");
popover = RED.popover.create({
target: $("#red-ui-clipboard-dialog-import-text"),
trigger: "manual",
direction: "bottom",
content: ""
});
}
function showExportNodes(mode) {
if (disabled) {
return;
}
mode = mode || "clipboard";
dialogContainer.empty();
dialogContainer.append($(exportNodesDialog));
var tabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tabs",
vertical: true,
onchange: function(tab) {
$("#red-ui-clipboard-dialog-export-tabs-content").children().hide();
$("#" + tab.id).show();
activeTab = tab.id;
if (tab.id === "red-ui-clipboard-dialog-export-tab-clipboard") {
$("#red-ui-clipboard-dialog-export").button("option","label", RED._("clipboard.export.copy"))
$("#red-ui-clipboard-dialog-download").show();
$("#red-ui-clipboard-dialog-export-tab-library-filename").hide();
} else {
$("#red-ui-clipboard-dialog-export").button("option","label", RED._("clipboard.export.export"))
$("#red-ui-clipboard-dialog-download").hide();
$("#red-ui-clipboard-dialog-export-tab-library-filename").show();
activeLibraries[activeTab].focus();
}
}
});
tabs.addTab({
id: "red-ui-clipboard-dialog-export-tab-clipboard",
label: RED._("clipboard.clipboard")
});
var libraries = RED.settings.libraries || [];
libraries.forEach(function(lib) {
if (lib.readOnly) {
return
}
var tabId = "red-ui-clipboard-dialog-export-tab-library-"+lib.id
tabs.addTab({
id: tabId,
label: RED._(lib.label||lib.id)
})
var content = $('<div class="red-ui-clipboard-dialog-export-tab-library-browser red-ui-clipboard-dialog-tab-library"></div>')
.attr("id",tabId)
.hide()
.insertBefore("#red-ui-clipboard-dialog-export-tab-library-filename");
var browser = RED.library.createBrowser({
container: content,
folderTools: true,
onselect: function(file) {
if (file && file.label && !file.children) {
$("#red-ui-clipboard-dialog-tab-library-name").val(file.label);
}
},
})
loadFlowLibrary(browser,lib);
activeLibraries[tabId] = browser;
})
$("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename);
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
var clipboardTabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
onchange: function(tab) {
$(".red-ui-clipboard-dialog-export-tab-clipboard-tab").hide();
$("#" + tab.id).show();
}
});
clipboardTabs.addTab({
id: "red-ui-clipboard-dialog-export-tab-clipboard-preview",
label: RED._("clipboard.exportNodes")
});
clipboardTabs.addTab({
id: "red-ui-clipboard-dialog-export-tab-clipboard-json",
label: RED._("editor.types.json")
});
var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: []
})
refreshExportPreview();
$("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select();
dialogContainer.i18n();
var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
$("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var flow = $("#red-ui-clipboard-dialog-export-text").val();
if (flow.length > 0) {
var nodes = JSON.parse(flow);
format = $(this).attr('id');
if (format === 'red-ui-clipboard-dialog-export-fmt-full') {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
$("#red-ui-clipboard-dialog-export-text").val(flow);
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50);
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
}
});
$("#red-ui-clipboard-dialog-export-rng-group > a").on("click", function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var type = $(this).attr('id').substring("red-ui-clipboard-dialog-export-rng-".length);
var flow = "";
var nodes = null;
if (type === 'selected') {
var selection = RED.workspaces.selection();
if (selection.length > 0) {
nodes = [];
selection.forEach(function(n) {
nodes.push(n);
nodes = nodes.concat(RED.nodes.groups(n.id));
nodes = nodes.concat(RED.nodes.filterNodes({z:n.id}));
});
} else {
nodes = RED.view.selection().nodes||[];
}
// Don't include the subflow meta-port nodes in the exported selection
nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'}));
} else if (type === 'flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.groups(activeWorkspace);
nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
} else if (type === 'full') {
nodes = RED.nodes.createCompleteNodeSet(false);
}
if (nodes !== null) {
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
}
if (flow.length > 0) {
$("#red-ui-clipboard-dialog-export").removeClass('disabled');
} else {
$("#red-ui-clipboard-dialog-export").addClass('disabled');
}
$("#red-ui-clipboard-dialog-export-text").val(flow);
setTimeout(function() {
$("#red-ui-clipboard-dialog-export-text").scrollTop(0);
refreshExportPreview(type);
},50);
})
$("#red-ui-clipboard-dialog-ok").hide();
$("#red-ui-clipboard-dialog-cancel").hide();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-import-conflict").hide();
var selection = RED.workspaces.selection();
if (selection.length > 0) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
} else {
selection = RED.view.selection();
if (selection.nodes) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
} else {
$("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected');
$("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
}
}
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
$("#red-ui-clipboard-dialog-export-fmt-full").trigger("click");
} else {
$("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click");
}
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode);
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
dialog.dialog("option","title",RED._("clipboard.exportNodes"))
.dialog("option","width",700)
.dialog("open");
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").show();
$("#red-ui-clipboard-dialog-download").show();
$("#red-ui-clipboard-dialog-import-conflict").hide();
}
function refreshExportPreview(type) {
var flowData = $("#red-ui-clipboard-dialog-export-text").val() || "[]";
var flow = JSON.parse(flowData);
var flows = {};
var subflows = {};
var nodes = [];
var nodesByZ = {};
var treeFlows = [];
var treeSubflows = [];
flow.forEach(function(node) {
if (node.type === "tab") {
flows[node.id] = {
element: getFlowLabel(node,false),
deferBuild: type !== "flow",
expanded: type === "flow",
children: []
};
treeFlows.push(flows[node.id])
} else if (node.type === "subflow") {
subflows[node.id] = {
element: getNodeLabel(node,false),
deferBuild: true,
children: []
};
treeSubflows.push(subflows[node.id])
} else {
nodes.push(node);
}
});
var globalNodes = [];
var parentlessNodes = [];
nodes.forEach(function(node) {
var treeNode = {
element: getNodeLabel(node, false, false)
};
if (node.z) {
if (!flows[node.z] && !subflows[node.z]) {
parentlessNodes.push(treeNode)
} else if (flows[node.z]) {
flows[node.z].children.push(treeNode)
} else if (subflows[node.z]) {
subflows[node.z].children.push(treeNode)
}
} else {
globalNodes.push(treeNode);
}
});
var treeData = [];
if (parentlessNodes.length > 0) {
treeData = treeData.concat(parentlessNodes);
}
if (type === "flow") {
treeData = treeData.concat(treeFlows);
} else if (treeFlows.length > 0) {
treeData.push({
label: RED._("menu.label.flows"),
deferBuild: treeFlows.length > 20,
expanded: treeFlows.length <= 20,
children: treeFlows
})
}
if (treeSubflows.length > 0) {
treeData.push({
label: RED._("menu.label.subflows"),
deferBuild: treeSubflows.length > 10,
expanded: treeSubflows.length <= 10,
children: treeSubflows
})
}
if (globalNodes.length > 0) {
treeData.push({
label: RED._("sidebar.info.globalConfig"),
deferBuild: globalNodes.length > 10,
expanded: globalNodes.length <= 10,
children: globalNodes
})
}
$("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").treeList('data',treeData);
}
function loadFlowLibrary(browser,library) {
var icon = 'fa fa-hdd-o';
if (library.icon) {
var fullIcon = RED.utils.separateIconPath(library.icon);
icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
}
browser.data([{
library: library.id,
type: "flows",
icon: icon,
label: RED._(library.label||library.id),
path: "",
expanded: true,
children: [{
library: library.id,
type: "flows",
icon: 'fa fa-cube',
label: "flows",
path: "",
expanded: true,
children: function(done, item) {
RED.library.loadLibraryFolder(library.id,"flows","",function(children) {
item.children = children;
done(children);
})
}
}]
}], true);
}
function hideDropTarget() {
$("#red-ui-drop-target").hide();
}
function copyText(value,element,msg) {
var truncated = false;
var currentFocus = document.activeElement;
if (typeof value !== "string" ) {
value = JSON.stringify(value, function(key,value) {
if (value !== null && typeof value === 'object') {
if (value.__enc__) {
if (value.hasOwnProperty('data') && value.hasOwnProperty('length')) {
truncated = value.data.length !== value.length;
return value.data;
}
if (value.type === 'function' || value.type === 'internal') {
return undefined
}
if (value.type === 'number') {
// Handle NaN and Infinity - they are not permitted
// in JSON. We can either substitute with a String
// representation or null
return null;
}
if (value.type === 'bigint') {
return value.data.toString();
}
if (value.type === 'undefined') {
return undefined;
}
}
}
return value;
});
}
if (truncated) {
msg += "_truncated";
}
$("#red-ui-clipboard-hidden").val(value).focus().select();
var result = document.execCommand("copy");
if (result && element) {
var popover = RED.popover.create({
target: element,
direction: 'left',
size: 'small',
content: RED._(msg)
});
setTimeout(function() {
popover.close();
},1000);
popover.open();
}
$("#red-ui-clipboard-hidden").val("");
if (currentFocus) {
$(currentFocus).focus();
}
return result;
}
function importNodes(nodesStr,addFlow) {
var newNodes = nodesStr;
if (typeof nodesStr === 'string') {
try {
nodesStr = nodesStr.trim();
if (nodesStr.length === 0) {
return;
}
newNodes = JSON.parse(nodesStr);
} catch(err) {
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
e.code = "NODE_RED";
throw e;
}
}
var importOptions = {generateIds: false, addFlow: addFlow};
try {
RED.view.importNodes(newNodes, importOptions);
} catch(error) {
// Thrown for import_conflict
confirmImport(error.importConfig, newNodes, importOptions);
}
}
function confirmImport(importConfig,importNodes,importOptions) {
var notification = RED.notify("<p>"+RED._("clipboard.import.conflictNotification1")+"</p>",{
type: "info",
fixed: true,
buttons: [
{text: RED._("common.label.cancel"), click: function() { notification.close(); }},
{text: RED._("clipboard.import.viewNodes"), click: function() {
notification.close();
showImportConflicts(importConfig,importNodes,importOptions);
}},
{text: RED._("clipboard.import.importCopy"), click: function() {
notification.close();
// generateIds=true to avoid conflicts
// and default to the 'old' behaviour around matching
// config nodes and subflows
importOptions.generateIds = true;
RED.view.importNodes(importNodes, importOptions);
}}
]
})
}
function showImportConflicts(importConfig,importNodes,importOptions) {
pendingImportConfig = {
importConfig: importConfig,
importNodes: importNodes,
importOptions: importOptions
}
var id,node;
var treeData = [];
var container;
var addedHeader = false;
for (id in importConfig.subflows) {
if (importConfig.subflows.hasOwnProperty(id)) {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.subflows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
node = importConfig.subflows[id];
var isConflicted = importConfig.conflicted[node.id];
var isSelected = !isConflicted;
var elements = getNodeElement(node, isConflicted, isSelected );
container = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
class: isSelected?"":"disabled",
deferBuild: true,
children: []
}
treeData.push(container);
if (importConfig.zMap[id]) {
importConfig.zMap[id].forEach(function(node) {
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
container.children.push({
id: node.id,
gutter: childElements.gutter.element,
element: childElements.element,
class: isSelected?"":"disabled"
})
});
}
}
}
addedHeader = false;
for (id in importConfig.tabs) {
if (importConfig.tabs.hasOwnProperty(id)) {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.flows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
node = importConfig.tabs[id];
var isConflicted = importConfig.conflicted[node.id];
var isSelected = true;
var elements = getNodeElement(node, isConflicted, isSelected);
container = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
icon: "red-ui-icons red-ui-icons-flow",
deferBuild: true,
class: isSelected?"":"disabled",
children: []
}
treeData.push(container);
if (importConfig.zMap[id]) {
importConfig.zMap[id].forEach(function(node) {
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
container.children.push({
id: node.id,
gutter: childElements.gutter.element,
element: childElements.element,
class: isSelected?"":"disabled"
})
// console.log(" ["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
});
}
}
}
addedHeader = false;
var extraNodes = [];
importConfig.all.forEach(function(node) {
if (node.type !== "tab" && node.type !== "subflow" && !importConfig.tabs[node.z] && !importConfig.subflows[node.z]) {
var isConflicted = importConfig.conflicted[node.id];
var isSelected = !isConflicted || !importConfig.configs[node.id];
var elements = getNodeElement(node, isConflicted, isSelected);
var item = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
class: isSelected?"":"disabled"
}
if (importConfig.configs[node.id]) {
extraNodes.push(item);
} else {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.nodes"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
treeData.push(item);
}
// console.log("["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
}
})
if (extraNodes.length > 0) {
treeData.push({gutter:$('<span data-i18n="menu.label.displayConfig"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
treeData = treeData.concat(extraNodes);
}
dialogContainer.empty();
dialogContainer.append($(importConflictsDialog));
var nodeList = $("#red-ui-clipboard-dialog-import-conflicts-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: treeData
})
dialogContainer.i18n();
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
$("#red-ui-clipboard-dialog-ok").hide();
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-download").hide();
$("#red-ui-clipboard-dialog-import-conflict").show();
dialog.dialog("option","title",RED._("clipboard.importNodes"))
.dialog("option","width",500)
.dialog( "open" );
}
function getNodeElement(n, isConflicted, isSelected, parent) {
var element;
if (n.type === "tab") {
element = getFlowLabel(n, isSelected);
} else {
element = getNodeLabel(n, isConflicted, isSelected);
}
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
controls.on("click", function(evt) { evt.stopPropagation(); });
if (isConflicted && !parent) {
var cb = $('<label><input '+(isSelected?'':'disabled ')+'type="checkbox" data-node-id="'+n.id+'"> <span data-i18n="clipboard.import.replace"></span></label>').appendTo(controls);
if (n.type === "tab" || (n.type !== "subflow" && n.hasOwnProperty("x") && n.hasOwnProperty("y"))) {
cb.hide();
}
}
return {
element: element,
gutter: getGutter(n, isSelected, parent)
}
}
function getGutter(n, isSelected, parent) {
var span = $("<label>",{class:"red-ui-clipboard-dialog-import-conflicts-gutter"});
var cb = $('<input data-node-id="'+n.id+'" type="checkbox" '+(isSelected?"checked":"")+'>').appendTo(span);
if (parent) {
cb.attr("disabled",true);
parent.addChild(cb);
}
span.on("click", function(evt) {
evt.stopPropagation();
})
cb.on("change", function(evt) {
var state = this.checked;
span.parent().toggleClass("disabled",!!!state);
span.parent().find('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').attr("disabled",!!!state);
childItems.forEach(function(c) {
c.attr("checked",state);
c.trigger("change");
});
})
var childItems = [];
var checkbox = {
addChild: function(c) {
childItems.push(c);
}
}
return {
cb: checkbox,
element: span
}
}
function getFlowLabel(n) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
var label = (typeof n === "string")? n : n.label;
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
contentDiv.text(label);
// A conflicted flow should not be imported by default.
return div;
}
function getNodeLabel(n, isConflicted) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-node-list-item"});
RED.utils.createNodeIcon(n,true).appendTo(div);
return div;
}
return {
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);
RED.actions.add("core:show-library-export-dialog",function() { showExportNodes('library') });
RED.actions.add("core:show-library-import-dialog",function() { showImportNodes('library') });
RED.actions.add("core:show-examples-import-dialog",function() { showImportNodes('examples') });
RED.events.on("editor:open",function() { disabled = true; });
RED.events.on("editor:close",function() { disabled = false; });
RED.events.on("search:open",function() { disabled = true; });
RED.events.on("search:close",function() { disabled = false; });
RED.events.on("actionList:open",function() { disabled = true; });
RED.events.on("actionList:close",function() { disabled = false; });
RED.events.on("type-search:open",function() { disabled = true; });
RED.events.on("type-search:close",function() { disabled = false; });
$('<div id="red-ui-drop-target"><div data-i18n="[append]workspace.dropFlowHere"><i class="fa fa-download"></i><br></div></div>').appendTo('#red-ui-editor');
RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget);
$('#red-ui-workspace-chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
$("#red-ui-drop-target").css({display:'table'}).focus();
}
});
$('#red-ui-drop-target').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
event.preventDefault();
}
})
.on("dragleave",function(event) {
hideDropTarget();
})
.on("drop",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
}
}
hideDropTarget();
event.preventDefault();
});
},
import: showImportNodes,
export: showExportNodes,
copyText: copyText
}
})();