/**
 * 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 clipboardTabs;

    var activeLibraries = {};

    var pendingImportConfig;


    function downloadData(file, data) {
        const element = document.createElement('a');
        element.setAttribute('href', 'data:application/json;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.view.focus();
                        }
                    },
                    { // 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.view.focus();
                        }
                    },
                    { // 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"});
                                RED.view.focus();
                            } 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.view.focus();
                                        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.view.focus();
                        }
                    },
                    { // 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" );
                            RED.view.focus();
                        }
                    }
                ],
                open: function( event, ui ) {
                    RED.keyboard.disable();
                },
                beforeClose: function(e) {
                    if (clipboardTabs && activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
                        const jsonTabIndex = clipboardTabs.getTabIndex('red-ui-clipboard-dialog-export-tab-clipboard-json')
                        const activeTabIndex = clipboardTabs.activeIndex()
                        RED.settings.set("editor.dialog.export.json-view", activeTabIndex === jsonTabIndex )
                    }
                },
                close: function(e) {
                    RED.keyboard.enable();
                    if (popover) {
                        popover.close(true);
                        currentPopoverError = null;
                    }
                }
            });

        dialogContainer = dialog.children(".dialog-form");

        exportNodesDialog =
            '<div class="form-row">'+
                '<div style="display: flex; justify-content: space-between;">'+
                    '<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="form-row">'+
                        '<label style="width:auto;margin-right: 10px;" data-i18n="common.label.format"></label>'+
                        '<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 toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
                            '<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
                        '</span>'+
                    '</div>'+
                '</div>'+
            '</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% - 10px)">'+
                                '<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
                            '</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);
    }

    /**
     * Validates if the provided string looks like valid flow json
     * @param {string} flowString the string to validate
     * @returns If valid, returns the node array
     */
    function validateFlowString(flowString) {
        const res = JSON.parse(flowString)
        if (!Array.isArray(res)) {
            throw new Error(RED._("clipboard.import.errors.notArray"));
        }
        for (let i = 0; i < res.length; i++) {
            if (typeof res[i] !== "object") {
                throw new Error(RED._("clipboard.import.errors.itemNotObject",{index:i}));
            }
            if (!Object.hasOwn(res[i], 'id')) {
                throw new Error(RED._("clipboard.import.errors.missingId",{index:i}));
            }
            if (!Object.hasOwn(res[i], 'type')) {
                throw new Error(RED._("clipboard.import.errors.missingType",{index:i}));
            }
        }
        return res
    }

    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 {
                    validateFlowString(v)
                    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(library = 'clipboard') {
        if (disabled) {
            return;
        }

        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-"+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)});

        if (RED.workspaces.active() === 0 || RED.workspaces.isLocked()) {
            $("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
            $("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
        } else {
            $("#red-ui-clipboard-dialog-import-opt-current").removeClass('disabled').addClass("selected");
            $("#red-ui-clipboard-dialog-import-opt-new").removeClass("selected");
        }
        $("#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-"+library);
        if (library === '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: ""
        });
    }

    /**
     * Show the export dialog
     * @params library which export destination to show
     * @params mode whether to default to 'auto' (default) or 'flow'
     **/
    function showExportNodes(library = 'clipboard', mode = 'auto' ) {
        if (disabled) {
            return;
        }

        dialogContainer.empty();
        dialogContainer.append($(exportNodesDialog));
        clipboardTabs = null
        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");

        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")
        });
        if (RED.settings.get("editor.dialog.export.json-view") === true) {
            clipboardTabs.activateTab("red-ui-clipboard-dialog-export-tab-clipboard-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";
        const userFormat = RED.settings.get("editor.dialog.export.pretty")
        if (userFormat === false || userFormat === true) {
            format = userFormat ? "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');
                const pretty = format === "red-ui-clipboard-dialog-export-fmt-full";
                if (pretty) {
                    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.settings.set("editor.dialog.export.pretty", pretty)
            }
        });

        $("#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'}), { includeModuleConfig: true });
            } else if (type === 'flow') {
                var activeWorkspace = RED.workspaces.active();
                nodes = RED.nodes.groups(activeWorkspace);
                nodes = nodes.concat(RED.nodes.junctions(activeWorkspace));
                nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
                RED.nodes.eachConfig(function(n) {
                    if (n.z === RED.workspaces.active() && n._def.hasUsers === false) {
                        // Grab any config nodes scoped to this flow that don't
                        // require any flow-nodes to use them
                        nodes.push(n);
                    }
                });
                var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
                nodes.unshift(parentNode);
                nodes = RED.nodes.createExportableNodeSet(nodes, { includeModuleConfig: true });
            } else if (type === 'full') {
                nodes = RED.nodes.createCompleteNodeSet({ credentials: false, includeModuleConfig: true });
            }
            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();

        if (RED.workspaces.active() === 0) {
            $("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected');
            $("#red-ui-clipboard-dialog-export-rng-flow").addClass('disabled').removeClass('selected');
            $("#red-ui-clipboard-dialog-export-rng-full").trigger("click");
        } else {
            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 (mode === 'flow' && !$("#red-ui-clipboard-dialog-export-rng-flow").hasClass('disabled')) {
            $("#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-"+library);

        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),
                    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 if (node.type !== 'global-config') {
                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";
        }
        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({
                target: element,
                direction: 'left',
                size: 'small',
                content: RED._(msg)
            });
            setTimeout(function() {
                popover.close();
            },1000);
            popover.open();
        }
        clipboardHidden.remove();
        if (currentFocus) {
            $(currentFocus).focus();
        }
        return result;
    }

    function importNodes(nodesStr,addFlow) {
        let newNodes = nodesStr;
        if (typeof nodesStr === 'string') {
            try {
                nodesStr = nodesStr.trim();
                if (nodesStr.length === 0) {
                    return;
                }
                newNodes = validateFlowString(nodesStr)
            } catch(err) {
                const 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, isConflicted);
        } else {
            element = getNodeLabel(n, isConflicted, isSelected, parent);
        }
        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, 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-info-outline-item red-ui-info-outline-item-flow red-ui-node-list-item"});
        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);

        if (!!isConflicted) {
            const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
            RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
        }

        // A conflicted flow should not be imported by default.
        return div;
    }

    function getNodeLabel(n, isConflicted, isSelected, parent) {
        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);

        if (!parent && !!isConflicted) {
            const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
            RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
        }
        return div;
    }

    return {
        init: function() {
            setupDialogs();

            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 (!RED.workspaces.isLocked() && (
                    $.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 ||
                        RED.workspaces.isLocked()) {
                    event.preventDefault();
                }
            })
            .on("dragleave",function(event) {
                hideDropTarget();
            })
            .on("drop",function(event) {
                if (!RED.workspaces.isLocked()) {
                    try {
                        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);
                            }
                        }
                    } catch(err) {
                        console.warn('Import failed: ', err)
                        // Ensure any errors throw above doesn't stop the drop target from
                        // being hidden.
                    }
                }
                hideDropTarget();
                event.preventDefault();
            });

        },
        import: showImportNodes,
        export: showExportNodes,
        copyText: copyText
    }
})();
