<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "Panel", "settings", "ui", "watcher", "menus", "tabManager", "find",
        "fs", "panels", "fs.cache", "preferences", "c9", "tree", "commands"
    ];
    main.provides = ["navigate"];
    return main;

    function main(options, imports, register) {
        var Panel    = imports.Panel;
        var settings = imports.settings;
        var ui       = imports.ui;
        var c9       = imports.c9;
        var fs       = imports.fs;
        var fsCache  = imports["fs.cache"];
        var tabs     = imports.tabManager;
        var menus    = imports.menus;
        var watcher  = imports.watcher;
        var panels   = imports.panels;
        var find     = imports.find;
        var filetree = imports.tree;
        var prefs    = imports.preferences;
        var commands = imports.commands;

        var markup   = require("text!./navigate.xml");
        var search   = require('./search');
        var Tree     = require("ace_tree/tree");
        var ListData = require("./dataprovider");
        var basename = require("path").basename;

        /***** Initialization *****/

        var plugin = new Panel("Ajax.org", main.consumes, {
            index        : options.index || 200,
            caption      : "Navigate",
            elementName  : "winGoToFile",
            minWidth     : 130,
            autohide     : true,
            where        : options.where || "left"
        });
        var emit   = plugin.getEmitter();

        var winGoToFile, txtGoToFile, tree, ldSearch;
        var lastSearch, lastPreviewed, cleaning, intoOutline;

        var dirty         = true;
        var arrayCache    = [];
        var timer;

        var loaded = false;
        function load(){
            if (loaded) return false;
            loaded = true;

            var command = plugin.setCommand({
                name    : "navigate",
                hint    : "search for a filename, line or symbol and jump to it",
                bindKey : { mac: "Command-E|Command-P", win: "Ctrl-E|Ctrl-P" }
            });

            commands.addCommand({
                name    : "navigate_altkey",
                hint    : "search for a filename, line or symbol and jump to it",
                bindKey : {
                    mac: "Command-O",
                    win: "Ctrl-O"
                },
                group : "Panels",
                exec  : function() {
                    command.exec();
                }
            }, plugin);

            panels.on("afterAnimate", function(){
                if (panels.isActive("navigate"))
                    tree && tree.resize();
            });

            // Menus
            var mnuItem = new apf.item({ command : "navigate" });
            menus.addItemByPath("File/Open...", mnuItem, 500, plugin);
            menus.addItemByPath("Goto/Goto Anything...", mnuItem.cloneNode(false), 100, plugin);

            // Settings
            settings.on("read", function(){
                settings.setDefaults("user/general", [["preview-navigate", "false"]]);
            }, plugin);

            // Prefs
            prefs.add({
                "General" : {
                    "General" : {
                        "Enable Preview on Navigation" : {
                            type     : "checkbox",
                            position : 2000,
                            path     : "user/general/@preview-navigate"
                        }
                    }
                }
            }, plugin);

            // Update when the fs changes
            fs.on("afterWriteFile", function(e){
                // Only mark dirty if file didn't exist yet
                if (arrayCache.indexOf(e.path) == -1)
                    markDirty(e);
            });
            fs.on("afterUnlink",    markDirty);
            fs.on("afterRmfile",    markDirty);
            fs.on("afterRmdir",     markDirty);
            fs.on("afterCopy",      markDirty);
            fs.on("afterRename",    markDirty);
            fs.on("afterSymlink",   markDirty);

            // Or when a watcher fires
            watcher.on("delete",     markDirty);
            watcher.on("directory",  markDirty);

            // Or when the user refreshes the tree
            filetree.on("refresh", markDirty);

            // Or when we change the visibility of hidden files
            fsCache.on("setShowHidden", markDirty);

            // Pre-load file list
            updateFileCache();
        }

        function offlineHandler(e){
            // Online
            if (e.state & c9.STORAGE) {
                txtGoToFile.enable();
                //@Harutyun This doesn't work
                // tree.enable();
            }
            // Offline
            else {
                // do not close panel while typing
                if (!txtGoToFile.ace.isFocused())
                    txtGoToFile.disable();
                //@Harutyun This doesn't work
                // tree.disable();
            }
        }

        var drawn = false;
        function draw(options){
            if (drawn) return;
            drawn = true;

            // Create UI elements
            ui.insertMarkup(options.aml, markup, plugin);

            // Import CSS
            ui.insertCss(require("text!./style.css"), plugin);

            var treeParent   = plugin.getElement("navigateList");
            txtGoToFile      = plugin.getElement("txtGoToFile");
            winGoToFile      = plugin.getElement("winGoToFile");

            // Create the Ace Tree
            tree      = new Tree(treeParent.$int);
            ldSearch  = new ListData(arrayCache);
            ldSearch.search = search;

            // Assign the dataprovider
            tree.setDataProvider(ldSearch);

            tree.renderer.setScrollMargin(0, 10);

            // @TODO this is probably not sufficient
            window.addEventListener("resize", function(){ tree.resize() });

            tree.textInput = txtGoToFile.ace.textInput;

            txtGoToFile.ace.commands.addCommands([
                {
                    bindKey : "ESC",
                    exec    : function(){ plugin.hide(); }
                }, {
                    bindKey : "Enter",
                    exec    : function(){ openFile(true); }
                }, {
                    bindKey : "Shift-Enter",
                    exec    : function(){ openFile(false, true); }
                }, {
                    bindKey : "Shift-Space",
                    exec    : function(){ previewFile(true); }
                },
            ]);
            function forwardToTree() {
                tree.execCommand(this.name);
            }
            txtGoToFile.ace.commands.addCommands([
                "centerselection",
                "goToStart",
                "goToEnd",
                "pageup",
                "gotopageup",
                "scrollup",
                "scrolldown",
                "goUp",
                "goDown",
                "selectUp",
                "selectDown",
                "selectMoreUp",
                "selectMoreDown"
            ].map(function(name) {
                var command = tree.commands.byName[name];
                return {
                    name: command.name,
                    bindKey: command.editorKey || command.bindKey,
                    exec: forwardToTree
                };
            }));

            tree.on("click", function(ev){
                var e = ev.domEvent;
                if (!e.shiftKey && !e.metaKey  && !e.ctrlKey  && !e.altKey)
                if (tree.selection.getSelectedNodes().length === 1)
                    openFile(true);
            });

            tree.selection.$wrapAround = true;

            txtGoToFile.ace.on("input", function(e) {
                var val = txtGoToFile.getValue();
                var parts, tab;

                if (cleaning) {
                    cleaning = false;
                    return;
                }

                if (~val.indexOf("@")) {
                    parts = val.split("@");
                    if (parts[0]) {
                        if (lastSearch != parts[0])
                            filter(parts[0]);
                        previewFile(true);
                        tab = lastPreviewed || tabs.focussedTab;
                        if (tab) {
                            emit("outline", { value: parts[1], tab: tab });
                            intoOutline = true;
                        }
                    }
                    else {
                        tab = tabs.focussedTab;
                        if (tab) {
                            emit("outline", { value: parts[1], tab: tab });
                            intoOutline = true;
                        }
                    }
                }
                else {
                    if (intoOutline)
                        stopOutline();

                    if (~val.indexOf(":")) {
                        parts = val.split(":");
                        if (parts[0]) {
                            if (lastSearch != parts[0])
                                filter(parts[0]);
                            previewFile(true);
                            tab = lastPreviewed || tabs.focussedTab;
                            if (tab && parts[1])
                                tab.editor.ace.gotoLine(parts[1], parts[2]);
                        }
                        else {
                            tab = tabs.focussedTab;
                            if (tab && parts[1])
                                tab.editor.ace.gotoLine(parts[1], parts[2]);
                        }
                    }
                    else {
                        filter(val);
                    }
                }

                if (dirty && val.length > 0 && ldSearch.loaded) {
                    dirty = false;
                    updateFileCache(true);
                }
            });

            tree.selection.on("change", function(){
                cleanInput();
                previewFile();
            });

            function onblur(e){
                if (!winGoToFile.visible)
                    return;

                var to = e.toElement;
                if (!to || apf.isChildOf(winGoToFile, to, true)
                  || (lastPreviewed && tabs.previewTab
                  && tabs.previewTab === lastPreviewed
                  && (apf.isChildOf(lastPreviewed.aml.relPage, to, true)
                  || lastPreviewed.aml == to))) {
                    return;
                }

                // TODO add better support for overlay panels
                setTimeout(function(){ plugin.hide() }, 10);
            }

            apf.addEventListener("movefocus", onblur);

            // Focus the input field
            setTimeout(function(){
                txtGoToFile.focus();
            }, 10);

            // Offline
            c9.on("stateChange", offlineHandler, plugin);
            offlineHandler({ state: c9.status });
        }

        /***** Methods *****/

        function reloadResults(){
            if (!winGoToFile) {
                plugin.once("draw", function(){
                    reloadResults();
                });
                return;
            }

            // Wait until window is visible
            if (!winGoToFile.visible) {
                winGoToFile.on("prop.visible", function visible(e){
                    if (e.value) {
                        reloadResults();
                        winGoToFile.off("prop.visible", visible);
                    }
                });
                return;
            }

            var sel = tree.selection.getSelectedNodes();
            if (lastSearch) {
                filter(lastSearch, sel.length);
            } else {
                ldSearch.updateData(arrayCache);
            }
        }

        function markDirty(options){
            // Ignore hidden files
            var path = options && options.path || "";
            if (path && !fsCache.showHidden && path.charAt(0) == ".")
                return;

            dirty = true;
            if (panels.isActive("navigate")) {
                clearTimeout(timer);
                timer = setTimeout(function(){ updateFileCache(true); }, 2000);
            }
        }

        var updating = false;
        function updateFileCache(isDirty){
            clearTimeout(timer);
            if (updating)
                return;
            updating = true;
            find.getFileList({
                path    : "/",
                nocache : isDirty,
                hidden  : fsCache.showHidden,
                buffer  : true
            }, function(err, data){
                if (err)
                    return console.error(err);

                arrayCache = data.trim().split("\n");

                updating = false;
                reloadResults();
            });

            dirty = false;
        }

        function stopOutline(){
            if (!intoOutline) return;

            emit("outline.stop");
            tree.setDataProvider(ldSearch);
            intoOutline = false;
        }

        function cleanInput(){
            var value = txtGoToFile.getValue();
            if (value.match(/[:\@]/)) {
                cleaning = true;
                txtGoToFile.setValue(value.split(":")[0].split("@")[0]);
            }
        }

        /**
         * Searches through the dataset
         *
         */
        var lastResults;
        function filter(keyword, nosel){
            keyword = keyword.replace(/\*/g, "");

            if (!arrayCache.length) {
                lastSearch = keyword;
                return;
            }

            // Needed for highlighting
            ldSearch.keyword = keyword;

            var searchResults;
            if (!keyword) {
                var result = arrayCache.slice();
                // More prioritization for already open files
                tabs.getTabs().forEach(function (tab) {
                    if (!tab.path
                      || tab.document.meta.preview) return;

                    var idx = result.indexOf(tab.path);
                    if (idx > -1) {
                        result.splice(idx, 1);
                        result.unshift(tab.path);
                    }
                });
                searchResults = result;
            }
            else {
                tree.provider.setScrollTop(0);

                var base;
                if (lastSearch)
                    base = keyword.substr(0, lastSearch.length) == lastSearch
                        ? lastResults : arrayCache;
                else
                    base = arrayCache;

                searchResults = search.fileSearch(base, keyword);
            }

            lastSearch  = keyword;
            lastResults = searchResults.newlist;

            if (searchResults)
                ldSearch.updateData(searchResults);

            if (nosel || !searchResults.length)
                return;

            var first = -1;
            if (keyword) {
                first = 0;
                // See if there are open files that match the search results
                // and the first if in the displayed results
                var openTabs = tabs.getTabs(), hash = {};
                for (var i = openTabs.length - 1; i >= 0; i--) {
                    var tab = openTabs[i];
                    if (!tab.document.meta.preview && tab.path) {
                        if (basename(tab.path).indexOf(keyword) === 0)
                            hash[tab.path] = true;
                    }
                }

                // loop over all visible items. If we find a visible item
                // that is in the `hash`, select it and return.

                var last = tree.renderer.$size.height / tree.provider.rowHeight;
                for (var i = 0; i < last; i++) {
                    if (hash[ldSearch.visibleItems[i]]) {
                        first = i;
                        break;
                    }
                }
            }
            // select the first item in the list
            tree.select(tree.provider.getNodeAtIndex(first));
        }

        function openFile(noanim, nohide){
            if (!ldSearch.loaded)
                return false;

            var nodes = tree.selection.getSelectedNodes();
            var cursor = tree.selection.getCursor();

            // Cancel Preview and Keep the tab if there's only one
            if (tabs.preview({ cancel: true, keep : nodes.length == 1 }) === true
              || intoOutline)
                return nohide || plugin.hide();

            nohide || plugin.hide();

            var fn = function(){};
            for (var i = 0, l = nodes.length; i < l; i++) {
                var id = nodes[i].id;
                if (!id) continue;
                var path  = "/" + id.replace(/^[\/]+/, "");

                var focus = id === cursor.id;
                tabs.open({
                    path   : path,
                    noanim : l > 1,
                    focus : focus && (nohide ? "soft" : true)
                }, fn);
            }

            lastPreviewed = null;
        }

        function previewFile(force){
            if ((!lastPreviewed || !lastPreviewed.loaded)
              && !settings.getBool("user/general/@preview-navigate") && !force)
                return;

            if (!ldSearch.loaded)
                return false;

            var node = tree.selection.getCursor();
            var value = node && node.id;
            if (!value)
                return;

            var path  = "/" + value.replace(/^[\/]+/, "");
            lastPreviewed = tabs.preview({ path: path }, function(){});
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
            load();
        });
        plugin.on("draw", function(e){
            draw(e);
        });
        plugin.on("enable", function(){

        });
        plugin.on("disable", function(){

        });
        plugin.on("show", function(e){
            cleanInput();
            txtGoToFile.focus();
            txtGoToFile.select();
        });
        plugin.on("hide", function(e){
            // Cancel Preview
            tabs.preview({ cancel: true });
            // Stop Outline if there
            stopOutline();
        });
        plugin.on("unload", function(){
            loaded = false;
            drawn  = false;
        });

        /***** Register and define API *****/

        /**
         * Navigation panel. Allows a user to navigate to files by searching
         * for a fuzzy string that matches the path of the file.
         * @singleton
         * @extends Panel
         **/
        /**
         * @command navigate
         */
        /**
         * Fires when the navigate panel shows
         * @event showPanelNavigate
         * @member panels
         */
        /**
         * Fires when the navigate panel hides
         * @event hidePanelNavigate
         * @member panels
         */
        plugin.freezePublicAPI({
            /**
             * @property {Object}  The tree implementation
             * @private
             */
            get tree() { return tree; }
        });

        register(null, {
            navigate: plugin
        });
    }
});<commit_msg>MOre local work<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "Panel", "settings", "ui", "watcher", "menus", "tabManager", "find",
        "fs", "panels", "fs.cache", "preferences", "c9", "tree", "commands"
    ];
    main.provides = ["navigate"];
    return main;

    function main(options, imports, register) {
        var Panel    = imports.Panel;
        var settings = imports.settings;
        var ui       = imports.ui;
        var c9       = imports.c9;
        var fs       = imports.fs;
        var fsCache  = imports["fs.cache"];
        var tabs     = imports.tabManager;
        var menus    = imports.menus;
        var watcher  = imports.watcher;
        var panels   = imports.panels;
        var find     = imports.find;
        var filetree = imports.tree;
        var prefs    = imports.preferences;
        var commands = imports.commands;

        var markup   = require("text!./navigate.xml");
        var search   = require('./search');
        var Tree     = require("ace_tree/tree");
        var ListData = require("./dataprovider");
        var basename = require("path").basename;

        /***** Initialization *****/

        var plugin = new Panel("Ajax.org", main.consumes, {
            index        : options.index || 200,
            caption      : "Navigate",
            elementName  : "winGoToFile",
            minWidth     : 130,
            autohide     : true,
            where        : options.where || "left"
        });
        var emit   = plugin.getEmitter();

        var winGoToFile, txtGoToFile, tree, ldSearch;
        var lastSearch, lastPreviewed, cleaning, intoOutline;

        var dirty         = true;
        var arrayCache    = [];
        var timer;

        var loaded = false;
        function load(){
            if (loaded) return false;
            loaded = true;

            var command = plugin.setCommand({
                name    : "navigate",
                hint    : "search for a filename, line or symbol and jump to it",
                bindKey : { mac: "Command-E|Command-P", win: "Ctrl-E|Ctrl-P" }
            });

            commands.addCommand({
                name    : "navigate_altkey",
                hint    : "search for a filename, line or symbol and jump to it",
                bindKey : {
                    mac: "Command-O",
                    win: "Ctrl-O"
                },
                group : "Panels",
                exec  : function() {
                    command.exec();
                }
            }, plugin);

            panels.on("afterAnimate", function(){
                if (panels.isActive("navigate"))
                    tree && tree.resize();
            });

            // Menus
            var mnuItem = new apf.item({ command : "navigate" });
            menus.addItemByPath("File/Open...", mnuItem, 500, plugin);
            menus.addItemByPath("Goto/Goto Anything...", mnuItem.cloneNode(false), 100, plugin);

            // Settings
            settings.on("read", function(){
                settings.setDefaults("user/general", [["preview-navigate", "false"]]);
            }, plugin);

            // Prefs
            prefs.add({
                "General" : {
                    "General" : {
                        "Enable Preview on Navigation" : {
                            type     : "checkbox",
                            position : 2000,
                            path     : "user/general/@preview-navigate"
                        }
                    }
                }
            }, plugin);

            // Update when the fs changes
            fs.on("afterWriteFile", function(e){
                // Only mark dirty if file didn't exist yet
                if (arrayCache.indexOf(e.path) == -1)
                    markDirty(e);
            });
            fs.on("afterUnlink",    markDirty);
            fs.on("afterRmfile",    markDirty);
            fs.on("afterRmdir",     markDirty);
            fs.on("afterCopy",      markDirty);
            fs.on("afterRename",    markDirty);
            fs.on("afterSymlink",   markDirty);

            // Or when a watcher fires
            watcher.on("delete",     markDirty);
            watcher.on("directory",  markDirty);

            // Or when the user refreshes the tree
            filetree.on("refresh", markDirty);

            // Or when we change the visibility of hidden files
            fsCache.on("setShowHidden", markDirty);

            // Pre-load file list
            updateFileCache();
        }

        function offlineHandler(e){
            // Online
            if (e.state & c9.STORAGE) {
                txtGoToFile.enable();
                //@Harutyun This doesn't work
                // tree.enable();
            }
            // Offline
            else {
                // do not close panel while typing
                if (!txtGoToFile.ace.isFocused())
                    txtGoToFile.disable();
                //@Harutyun This doesn't work
                // tree.disable();
            }
        }

        var drawn = false;
        function draw(options){
            if (drawn) return;
            drawn = true;

            // Create UI elements
            ui.insertMarkup(options.aml, markup, plugin);

            // Import CSS
            ui.insertCss(require("text!./style.css"), plugin);

            var treeParent   = plugin.getElement("navigateList");
            txtGoToFile      = plugin.getElement("txtGoToFile");
            winGoToFile      = plugin.getElement("winGoToFile");

            // Create the Ace Tree
            tree      = new Tree(treeParent.$int);
            ldSearch  = new ListData(arrayCache);
            ldSearch.search = search;

            // Assign the dataprovider
            tree.setDataProvider(ldSearch);

            tree.renderer.setScrollMargin(0, 10);

            // @TODO this is probably not sufficient
            window.addEventListener("resize", function(){ tree.resize() });

            tree.textInput = txtGoToFile.ace.textInput;

            txtGoToFile.ace.commands.addCommands([
                {
                    bindKey : "ESC",
                    exec    : function(){ plugin.hide(); }
                }, {
                    bindKey : "Enter",
                    exec    : function(){ openFile(true); }
                }, {
                    bindKey : "Shift-Enter",
                    exec    : function(){ openFile(false, true); }
                }, {
                    bindKey : "Shift-Space",
                    exec    : function(){ previewFile(true); }
                },
            ]);
            function forwardToTree() {
                tree.execCommand(this.name);
            }
            txtGoToFile.ace.commands.addCommands([
                "centerselection",
                "goToStart",
                "goToEnd",
                "pageup",
                "gotopageup",
                "scrollup",
                "scrolldown",
                "goUp",
                "goDown",
                "selectUp",
                "selectDown",
                "selectMoreUp",
                "selectMoreDown"
            ].map(function(name) {
                var command = tree.commands.byName[name];
                return {
                    name: command.name,
                    bindKey: command.editorKey || command.bindKey,
                    exec: forwardToTree
                };
            }));

            tree.on("click", function(ev){
                var e = ev.domEvent;
                if (!e.shiftKey && !e.metaKey  && !e.ctrlKey  && !e.altKey)
                if (tree.selection.getSelectedNodes().length === 1)
                    openFile(true);
            });

            tree.selection.$wrapAround = true;

            txtGoToFile.ace.on("input", function(e) {
                var val = txtGoToFile.getValue();
                var parts, tab;

                if (cleaning) {
                    cleaning = false;
                    return;
                }

                if (~val.indexOf("@")) {
                    parts = val.split("@");
                    if (parts[0]) {
                        if (lastSearch != parts[0])
                            filter(parts[0]);
                        previewFile(true);
                        tab = lastPreviewed || tabs.focussedTab;
                        if (tab) {
                            emit("outline", { value: parts[1], tab: tab });
                            intoOutline = true;
                        }
                    }
                    else {
                        tab = tabs.focussedTab;
                        if (tab) {
                            emit("outline", { value: parts[1], tab: tab });
                            intoOutline = true;
                        }
                    }
                }
                else {
                    if (intoOutline)
                        stopOutline();

                    if (~val.indexOf(":")) {
                        parts = val.split(":");
                        if (parts[0]) {
                            if (lastSearch != parts[0])
                                filter(parts[0]);
                            previewFile(true);
                            tab = lastPreviewed || tabs.focussedTab;
                            if (tab && parts[1])
                                tab.editor.ace.gotoLine(parts[1], parts[2]);
                        }
                        else {
                            tab = tabs.focussedTab;
                            if (tab && parts[1])
                                tab.editor.ace.gotoLine(parts[1], parts[2]);
                        }
                    }
                    else {
                        filter(val);
                    }
                }

                if (dirty && val.length > 0 && ldSearch.loaded) {
                    dirty = false;
                    updateFileCache(true);
                }
            });

            tree.selection.on("change", function(){
                cleanInput();
                previewFile();
            });

            function onblur(e){
                if (!winGoToFile.visible)
                    return;

                var to = e.toElement;
                if (!to || apf.isChildOf(winGoToFile, to, true)
                  || (lastPreviewed && tabs.previewTab
                  && tabs.previewTab === lastPreviewed
                  && (apf.isChildOf(lastPreviewed.aml.relPage, to, true)
                  || lastPreviewed.aml == to))) {
                    return;
                }

                // TODO add better support for overlay panels
                setTimeout(function(){ plugin.hide() }, 10);
            }

            apf.addEventListener("movefocus", onblur);

            // Focus the input field
            setTimeout(function(){
                txtGoToFile.focus();
            }, 10);

            // Offline
            c9.on("stateChange", offlineHandler, plugin);
            offlineHandler({ state: c9.status });
        }

        /***** Methods *****/

        function reloadResults(){
            if (!winGoToFile) {
                plugin.once("draw", function(){
                    reloadResults();
                });
                return;
            }

            // Wait until window is visible
            if (!winGoToFile.visible) {
                winGoToFile.on("prop.visible", function visible(e){
                    if (e.value) {
                        reloadResults();
                        winGoToFile.off("prop.visible", visible);
                    }
                });
                return;
            }

            var sel = tree.selection.getSelectedNodes();
            if (lastSearch) {
                filter(lastSearch, sel.length);
            } else {
                ldSearch.updateData(arrayCache);
            }
        }

        function markDirty(options){
            // Ignore hidden files
            var path = options && options.path || "";
            if (path && !fsCache.showHidden && path.charAt(0) == ".")
                return;

            dirty = true;
            if (panels.isActive("navigate")) {
                clearTimeout(timer);
                timer = setTimeout(function(){ updateFileCache(true); }, 2000);
            }
        }

        var updating = false;
        function updateFileCache(isDirty){
            clearTimeout(timer);
            if (updating || true)
                return;

            updating = true;
            find.getFileList({
                path    : "/",
                nocache : isDirty,
                hidden  : fsCache.showHidden,
                buffer  : true
            }, function(err, data){
                if (err)
                    return console.error(err);

                arrayCache = data.trim().split("\n");

                updating = false;
                reloadResults();
            });

            dirty = false;
        }

        function stopOutline(){
            if (!intoOutline) return;

            emit("outline.stop");
            tree.setDataProvider(ldSearch);
            intoOutline = false;
        }

        function cleanInput(){
            var value = txtGoToFile.getValue();
            if (value.match(/[:\@]/)) {
                cleaning = true;
                txtGoToFile.setValue(value.split(":")[0].split("@")[0]);
            }
        }

        /**
         * Searches through the dataset
         *
         */
        var lastResults;
        function filter(keyword, nosel){
            keyword = keyword.replace(/\*/g, "");

            if (!arrayCache.length) {
                lastSearch = keyword;
                return;
            }

            // Needed for highlighting
            ldSearch.keyword = keyword;

            var searchResults;
            if (!keyword) {
                var result = arrayCache.slice();
                // More prioritization for already open files
                tabs.getTabs().forEach(function (tab) {
                    if (!tab.path
                      || tab.document.meta.preview) return;

                    var idx = result.indexOf(tab.path);
                    if (idx > -1) {
                        result.splice(idx, 1);
                        result.unshift(tab.path);
                    }
                });
                searchResults = result;
            }
            else {
                tree.provider.setScrollTop(0);

                var base;
                if (lastSearch)
                    base = keyword.substr(0, lastSearch.length) == lastSearch
                        ? lastResults : arrayCache;
                else
                    base = arrayCache;

                searchResults = search.fileSearch(base, keyword);
            }

            lastSearch  = keyword;
            lastResults = searchResults.newlist;

            if (searchResults)
                ldSearch.updateData(searchResults);

            if (nosel || !searchResults.length)
                return;

            var first = -1;
            if (keyword) {
                first = 0;
                // See if there are open files that match the search results
                // and the first if in the displayed results
                var openTabs = tabs.getTabs(), hash = {};
                for (var i = openTabs.length - 1; i >= 0; i--) {
                    var tab = openTabs[i];
                    if (!tab.document.meta.preview && tab.path) {
                        if (basename(tab.path).indexOf(keyword) === 0)
                            hash[tab.path] = true;
                    }
                }

                // loop over all visible items. If we find a visible item
                // that is in the `hash`, select it and return.

                var last = tree.renderer.$size.height / tree.provider.rowHeight;
                for (var i = 0; i < last; i++) {
                    if (hash[ldSearch.visibleItems[i]]) {
                        first = i;
                        break;
                    }
                }
            }
            // select the first item in the list
            tree.select(tree.provider.getNodeAtIndex(first));
        }

        function openFile(noanim, nohide){
            if (!ldSearch.loaded)
                return false;

            var nodes = tree.selection.getSelectedNodes();
            var cursor = tree.selection.getCursor();

            // Cancel Preview and Keep the tab if there's only one
            if (tabs.preview({ cancel: true, keep : nodes.length == 1 }) === true
              || intoOutline)
                return nohide || plugin.hide();

            nohide || plugin.hide();

            var fn = function(){};
            for (var i = 0, l = nodes.length; i < l; i++) {
                var id = nodes[i].id;
                if (!id) continue;
                var path  = "/" + id.replace(/^[\/]+/, "");

                var focus = id === cursor.id;
                tabs.open({
                    path   : path,
                    noanim : l > 1,
                    focus : focus && (nohide ? "soft" : true)
                }, fn);
            }

            lastPreviewed = null;
        }

        function previewFile(force){
            if ((!lastPreviewed || !lastPreviewed.loaded)
              && !settings.getBool("user/general/@preview-navigate") && !force)
                return;

            if (!ldSearch.loaded)
                return false;

            var node = tree.selection.getCursor();
            var value = node && node.id;
            if (!value)
                return;

            var path  = "/" + value.replace(/^[\/]+/, "");
            lastPreviewed = tabs.preview({ path: path }, function(){});
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
            load();
        });
        plugin.on("draw", function(e){
            draw(e);
        });
        plugin.on("enable", function(){

        });
        plugin.on("disable", function(){

        });
        plugin.on("show", function(e){
            cleanInput();
            txtGoToFile.focus();
            txtGoToFile.select();
        });
        plugin.on("hide", function(e){
            // Cancel Preview
            tabs.preview({ cancel: true });
            // Stop Outline if there
            stopOutline();
        });
        plugin.on("unload", function(){
            loaded = false;
            drawn  = false;
        });

        /***** Register and define API *****/

        /**
         * Navigation panel. Allows a user to navigate to files by searching
         * for a fuzzy string that matches the path of the file.
         * @singleton
         * @extends Panel
         **/
        /**
         * @command navigate
         */
        /**
         * Fires when the navigate panel shows
         * @event showPanelNavigate
         * @member panels
         */
        /**
         * Fires when the navigate panel hides
         * @event hidePanelNavigate
         * @member panels
         */
        plugin.freezePublicAPI({
            /**
             * @property {Object}  The tree implementation
             * @private
             */
            get tree() { return tree; }
        });

        register(null, {
            navigate: plugin
        });
    }
});<|endoftext|>


<commit_before>const EventEmitter = require('events');

const moment = require('moment');
const CouchbaseChannelDisney = require('./couchbaseChannelDisney');
const Facilities = require('./disneyFacilityChannel');
const DebugLog = require('../debugPrint.js');
const Cache = require('../cache');

const DisneyUtil = require('./disneyUtil');

const sParkID = Symbol('Park ID');
const sChannel = Symbol('Channel');
const sFacilities = Symbol('Facilities');
const sBusVersion = Symbol('Bus Version');
const sPollInterval = Symbol('Polling Interval');
const sResorts = Symbol('Specific resorts to sync');

class DisneyLiveBusTimes extends EventEmitter {
  constructor(options = {}) {
    super();

    this[sParkID] = options.parkId === undefined ? 'wdw' : options.parkId;
    this[sBusVersion] = options.version === undefined ? '1_0' : options.version;

    // how often to poll for bus time updates (in seconds)
    this[sPollInterval] = options.pollinterval === undefined ? 10 : options.pollinterval;

    // setup our facilities channel, we need this to sync before we get our bus times
    this[sFacilities] = new Facilities({
      resortId: this[sParkID],
    });

    this[sResorts] = options.resorts || undefined;

    // NOTE - whilst there is a live channel for "Outpost Depot at Disney's Fort Wilderness Resort & Campground"
    //  our list of resorts doesn't return the ID 144943 needed to subscribe
    //  However, channel 80010408 has exactly the same data, so we're getting it via the "The Cabins at Disney's Fort Wilderness Resort" resort instead
  }

  /**
   * Callback for "updated" event from our couchbase channel class
   * @param {Object} doc
   */
  OnDocUpdated(doc) {
    // console.log(JSON.stringify(doc, null, 2));
    const id = DisneyUtil.CleanID(doc.id);
    const {
      lastUpdate,
    } = doc;

    this[sFacilities].GetFacilityName(id).then((name) => {
      if (doc.destinations === undefined) {
        return;
      }

      // add human-readable times and names to each destination
      const destinationPromises = [];
      doc.destinations.forEach((dest) => {
        destinationPromises.push(this.ParseDestination(dest));
      });
      Promise.all(destinationPromises).then((destinations) => {
        // emit events for each updated bus stop
        // console.log(JSON.stringify(destinations, null, 2));

        destinations.forEach((dest) => {
          if (dest.arrivals !== undefined) {
            dest.arrivals.forEach((arrival) => {
              this.emit('busupdate', {
                lastUpdate,
                from: name,
                from_id: Number(id),
                to: dest.name,
                to_id: Number(DisneyUtil.CleanID(dest.id)),
                atStop: arrival.atStop,
                atStopHuman: arrival.atStop_human,
                atDestination: arrival.atDestination,
                atDestinationHuman: arrival.atDestination_human,
                frequency: dest.frequency,
                frequencyHuman: dest.frequency_human,
              });
            });
          } else {
            // emit event with boring "every 20 minutes" update
            this.emit('busupdate', {
              lastUpdate,
              from: name,
              from_id: Number(id),
              to: dest.name,
              to_id: Number(DisneyUtil.CleanID(dest.id)),
              frequency: dest.frequency,
              frequencyHuman: dest.frequency_human,
            });
          }
        });
      });
    });
  }

  ParseDestination(destination) {
    return this[sFacilities].GetFacilityName(destination.id).then((name) => {
      destination.name = name;

      // use moment to make some human-readable times
      const now = moment();

      if (destination.frequency !== undefined) {
        const busFrequency = now.clone().add(destination.frequency, 'seconds');
        destination.frequency_human = `every ${busFrequency.from(now, true)}`;
      }

      // add human-readable strings to each arrival
      if (destination.arrivals !== undefined) {
        destination.arrivals.forEach((arrival) => {
          arrival.atStop_human = moment(arrival.atStop).from(now);
          arrival.atDestination_human = moment(arrival.atDestination).from(now);
        });
      }

      return Promise.resolve(destination);
    });
  }

  /**
   * Start fetching live bus time updates
   */
  Start() {
    if (!this[sFacilities].Synced) {
      // start facilities syncing,
      return this[sFacilities].Start().then(() => {
        if (this[sChannel] === undefined) {
          // set this to something in case multiple callbacks arrive before we set it up
          this[sChannel] = 1;

          // find all our resorts and build channel names
          return this.GetChannelNames().then((channels) => {
            // create our channel for fetching bus times
            this[sChannel] = new CouchbaseChannelDisney({
              dbName: `${this[sParkID]}_buses`,
              channel: channels.join(','),
              longpollDelay: this[sPollInterval], // delay each longpoll by 10 seconds (to avoid constant flooding of updates)
            });

            // setup puller hooks for document updates
            this[sChannel].on('updated', this.OnDocUpdated.bind(this));
            this[sChannel].on('error', this.Log.bind(this));

            // start the channel fetching documents
            this[sChannel].Start();
          });
        }

        return Promise.resolve();
      });
    }

    return Promise.resolve();
  }

  GetChannelNames() {
    if (this[sResorts] !== undefined) {
      return Promise.resolve([].concat(this[sResorts]).map(x => `${this[sParkID]}.arrivals.${x};entityType=resort.${this[sBusVersion]}`));
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT id, name FROM disneyFacilities WHERE entityType = ? AND resort_code = ?', ['resort', this[sParkID]], (err, rows) => {
        if (err) {
          return reject(err);
        }

        const channels = [];
        rows.forEach((row) => {
          channels.push(`${this[sParkID]}.arrivals.${row.id};entityType=resort.${this[sBusVersion]}`);
        });

        return resolve(channels);
      });
    }));
  }

  /**
   * Debug print a message (when NODE_DEBUG=themeparks is set in environment)
   * @param {...*} ToPrint Objects/strings to print
   * */
  Log(...args) {
    return DebugLog.apply(null, [`${this.constructor.name}:`, ...args]);
  }
}

module.exports = DisneyLiveBusTimes;
<commit_msg>[+] Add option to disneyBuses script to return stale data when the script starts if wanted<commit_after>const EventEmitter = require('events');

const moment = require('moment');
const CouchbaseChannelDisney = require('./couchbaseChannelDisney');
const Facilities = require('./disneyFacilityChannel');
const DebugLog = require('../debugPrint.js');
const Cache = require('../cache');

const DisneyUtil = require('./disneyUtil');

const sParkID = Symbol('Park ID');
const sChannel = Symbol('Channel');
const sFacilities = Symbol('Facilities');
const sBusVersion = Symbol('Bus Version');
const sPollInterval = Symbol('Polling Interval');
const sResorts = Symbol('Specific resorts to sync');
const sInitialiseWithOldData = Symbol('Broadcast old data when starting channel');

class DisneyLiveBusTimes extends EventEmitter {
  constructor(options = {}) {
    super();

    this[sParkID] = options.parkId === undefined ? 'wdw' : options.parkId;
    this[sBusVersion] = options.version === undefined ? '1_0' : options.version;

    // how often to poll for bus time updates (in seconds)
    this[sPollInterval] = options.pollinterval === undefined ? 10 : options.pollinterval;

    // setup our facilities channel, we need this to sync before we get our bus times
    this[sFacilities] = new Facilities({
      resortId: this[sParkID],
    });

    this[sResorts] = options.resorts || undefined;

    this[sInitialiseWithOldData] = !!options.initWithOldData;

    // NOTE - whilst there is a live channel for "Outpost Depot at Disney's Fort Wilderness Resort & Campground"
    //  our list of resorts doesn't return the ID 144943 needed to subscribe
    //  However, channel 80010408 has exactly the same data, so we're getting it via the "The Cabins at Disney's Fort Wilderness Resort" resort instead
  }

  /**
   * Callback for "updated" event from our couchbase channel class
   * @param {Object} doc
   */
  OnDocUpdated(doc) {
    // console.log(JSON.stringify(doc, null, 2));
    const id = DisneyUtil.CleanID(doc.id);
    const {
      lastUpdate,
    } = doc;

    this[sFacilities].GetFacilityName(id).then((name) => {
      if (doc.destinations === undefined) {
        return;
      }

      // add human-readable times and names to each destination
      const destinationPromises = [];
      doc.destinations.forEach((dest) => {
        destinationPromises.push(this.ParseDestination(dest));
      });
      Promise.all(destinationPromises).then((destinations) => {
        // emit events for each updated bus stop
        // console.log(JSON.stringify(destinations, null, 2));

        destinations.forEach((dest) => {
          if (dest.arrivals !== undefined) {
            dest.arrivals.forEach((arrival) => {
              this.emit('busupdate', {
                lastUpdate,
                from: name,
                from_id: Number(id),
                to: dest.name,
                to_id: Number(DisneyUtil.CleanID(dest.id)),
                atStop: arrival.atStop,
                atStopHuman: arrival.atStop_human,
                atDestination: arrival.atDestination,
                atDestinationHuman: arrival.atDestination_human,
                frequency: dest.frequency,
                frequencyHuman: dest.frequency_human,
              });
            });
          } else {
            // emit event with boring "every 20 minutes" update
            this.emit('busupdate', {
              lastUpdate,
              from: name,
              from_id: Number(id),
              to: dest.name,
              to_id: Number(DisneyUtil.CleanID(dest.id)),
              frequency: dest.frequency,
              frequencyHuman: dest.frequency_human,
            });
          }
        });
      });
    });
  }

  ParseDestination(destination) {
    return this[sFacilities].GetFacilityName(destination.id).then((name) => {
      destination.name = name;

      // use moment to make some human-readable times
      const now = moment();

      if (destination.frequency !== undefined) {
        const busFrequency = now.clone().add(destination.frequency, 'seconds');
        destination.frequency_human = `every ${busFrequency.from(now, true)}`;
      }

      // add human-readable strings to each arrival
      if (destination.arrivals !== undefined) {
        destination.arrivals.forEach((arrival) => {
          arrival.atStop_human = moment(arrival.atStop).from(now);
          arrival.atDestination_human = moment(arrival.atDestination).from(now);
        });
      }

      return Promise.resolve(destination);
    });
  }

  GetCurrentBusState() {
    if (!this[sInitialiseWithOldData]) {
      return Promise.resolve();
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT body FROM couchbasesync WHERE dbName = ?', [`${this[sParkID]}_buses`], (err, rows) => {
        if (err) {
          return reject(new Error(`Fetching current bus state: ${err}`));
        }

        // process existing couchbase docs as if they were new
        rows.map(x => JSON.parse(x.body)).forEach(this.OnDocUpdated.bind(this));

        return resolve();
      });
    }));
  }

  /**
   * Start fetching live bus time updates
   */
  Start() {
    if (!this[sFacilities].Synced) {
      // start facilities syncing,
      return this[sFacilities].Start().then(() => {
        if (this[sChannel] === undefined) {
          // set this to something in case multiple callbacks arrive before we set it up
          this[sChannel] = 1;

          // find all our resorts and build channel names
          return this.GetCurrentBusState().then(() => this.GetChannelNames().then((channels) => {
            // create our channel for fetching bus times
            this[sChannel] = new CouchbaseChannelDisney({
              dbName: `${this[sParkID]}_buses`,
              channel: channels.join(','),
              longpollDelay: this[sPollInterval], // delay each longpoll by 10 seconds (to avoid constant flooding of updates)
            });

            // setup puller hooks for document updates
            this[sChannel].on('updated', this.OnDocUpdated.bind(this));
            this[sChannel].on('error', this.Log.bind(this));

            // start the channel fetching documents
            this[sChannel].Start();
          }));
        }

        return Promise.resolve();
      });
    }

    return Promise.resolve();
  }

  GetChannelNames() {
    if (this[sResorts] !== undefined) {
      return Promise.resolve([].concat(this[sResorts]).map(x => `${this[sParkID]}.arrivals.${x};entityType=resort.${this[sBusVersion]}`));
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT id, name FROM disneyFacilities WHERE entityType = ? AND resort_code = ?', ['resort', this[sParkID]], (err, rows) => {
        if (err) {
          return reject(err);
        }

        const channels = [];
        rows.forEach((row) => {
          channels.push(`${this[sParkID]}.arrivals.${row.id};entityType=resort.${this[sBusVersion]}`);
        });

        return resolve(channels);
      });
    }));
  }

  /**
   * Debug print a message (when NODE_DEBUG=themeparks is set in environment)
   * @param {...*} ToPrint Objects/strings to print
   * */
  Log(...args) {
    return DebugLog.apply(null, [`${this.constructor.name}:`, ...args]);
  }
}

module.exports = DisneyLiveBusTimes;
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

import Cookies from 'js-cookie'
import Vue from 'vue'
import md5 from 'md5'
import message from 'ant-design-vue/es/message'
import router from '@/router'
import store from '@/store'
import { login, logout, api } from '@/api'
import { ACCESS_TOKEN, CURRENT_PROJECT, DEFAULT_THEME, APIS, ASYNC_JOB_IDS } from '@/store/mutation-types'

const user = {
  state: {
    token: '',
    name: '',
    avatar: '',
    info: {},
    apis: {},
    features: {},
    project: {},
    asyncJobIds: [],
    isLdapEnabled: false,
    cloudian: {}
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_PROJECT: (state, project = {}) => {
      Vue.ls.set(CURRENT_PROJECT, project)
      state.project = project
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_INFO: (state, info) => {
      state.info = info
    },
    SET_APIS: (state, apis) => {
      state.apis = apis
      Vue.ls.set(APIS, apis)
    },
    SET_FEATURES: (state, features) => {
      state.features = features
    },
    SET_ASYNC_JOB_IDS: (state, jobsJsonArray) => {
      Vue.ls.set(ASYNC_JOB_IDS, jobsJsonArray)
      state.asyncJobIds = jobsJsonArray
    },
    SET_LDAP: (state, isLdapEnabled) => {
      state.isLdapEnabled = isLdapEnabled
    },
    SET_CLOUDIAN: (state, cloudian) => {
      state.cloudian = cloudian
    },
    RESET_THEME: (state) => {
      Vue.ls.set(DEFAULT_THEME, 'light')
    }
  },

  actions: {
    SetProject ({ commit }, project) {
      commit('SET_PROJECT', project)
    },
    Login ({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(response => {
          const result = response.loginresponse || {}

          Cookies.set('account', result.account, { expires: 1 })
          Cookies.set('domainid', result.domainid, { expires: 1 })
          Cookies.set('role', result.type, { expires: 1 })
          Cookies.set('sessionkey', result.sessionkey, { expires: 1 })
          Cookies.set('timezone', result.timezone, { expires: 1 })
          Cookies.set('timezoneoffset', result.timezoneoffset, { expires: 1 })
          Cookies.set('userfullname', result.firstname + ' ' + result.lastname, { expires: 1 })
          Cookies.set('userid', result.userid, { expires: 1 })
          Cookies.set('username', result.username, { expires: 1 })

          Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 24 * 60 * 60 * 1000)
          commit('SET_TOKEN', result.sessionkey)

          commit('SET_APIS', {})
          commit('SET_NAME', '')
          commit('SET_AVATAR', '')
          commit('SET_INFO', {})
          commit('SET_PROJECT', {})
          commit('SET_ASYNC_JOB_IDS', [])
          commit('SET_FEATURES', {})
          commit('SET_LDAP', {})
          commit('SET_CLOUDIAN', {})

          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    GetInfo ({ commit }) {
      return new Promise((resolve, reject) => {
        const cachedApis = Vue.ls.get(APIS, {})
        const hasAuth = Object.keys(cachedApis).length > 0
        if (hasAuth) {
          console.log('Login detected, using cached APIs')
          commit('SET_APIS', cachedApis)
          resolve(cachedApis)
        } else {
          // This will show the dashboard and some common navigation sections
          // to most users/roles, while we complete API autodiscovery
          const apis = {}
          apis.listUsers = {}
          apis.listAccounts = {}
          commit('SET_APIS', apis)
          resolve(apis)

          const hide = message.loading('Discovering features...', 0)
          api('listApis').then(response => {
            const apis = {}
            const apiList = response.listapisresponse.api
            for (var idx = 0; idx < apiList.length; idx++) {
              const api = apiList[idx]
              const apiName = api.name
              apis[apiName] = {
                params: api.params,
                response: api.response
              }
            }
            commit('SET_APIS', apis)
            store.dispatch('GenerateRoutes', { apis }).then(() => {
              router.addRoutes(store.getters.addRouters)
            })
            hide()
            message.success('Discovered all available features!')
          }).catch(error => {
            reject(error)
          })
        }

        api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
          const result = response.listusersresponse.user[0]
          commit('SET_INFO', result)
          commit('SET_NAME', result.firstname + ' ' + result.lastname)
          if ('email' in result) {
            commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5(result.email))
          } else {
            commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5('dev@cloudstack.apache.org'))
          }
        }).catch(error => {
          reject(error)
        })

        api('listCapabilities').then(response => {
          const result = response.listcapabilitiesresponse.capability
          commit('SET_FEATURES', result)
        }).catch(error => {
          reject(error)
        })

        api('listLdapConfigurations').then(response => {
          const ldapEnable = (response.ldapconfigurationresponse.count > 0)
          commit('SET_LDAP', ldapEnable)
        }).catch(error => {
          reject(error)
        })

        api('cloudianIsEnabled').then(response => {
          const cloudian = response.cloudianisenabledresponse.cloudianisenabled || {}
          commit('SET_CLOUDIAN', cloudian)
        }).catch(ignored => {
        })
      })
    },

    Logout ({ commit, state }) {
      return new Promise((resolve) => {
        var cloudianUrl = null
        if (state.cloudian.url && state.cloudian.enabled) {
          cloudianUrl = state.cloudian.url + 'logout.htm?redirect=' + encodeURIComponent(window.location.href)
        }

        Object.keys(Cookies.get()).forEach(cookieName => {
          Cookies.remove(cookieName)
          Cookies.remove(cookieName, { path: '/client' })
        })

        commit('SET_TOKEN', '')
        commit('SET_APIS', {})
        commit('SET_PROJECT', {})
        commit('SET_ASYNC_JOB_IDS', [])
        commit('SET_FEATURES', {})
        commit('SET_LDAP', {})
        commit('SET_CLOUDIAN', {})
        commit('RESET_THEME')
        Vue.ls.remove(CURRENT_PROJECT)
        Vue.ls.remove(ACCESS_TOKEN)
        Vue.ls.remove(ASYNC_JOB_IDS)

        logout(state.token).then(() => {
          if (cloudianUrl) {
            window.location.href = cloudianUrl
          } else {
            resolve()
          }
        }).catch(() => {
          resolve()
        })
      })
    },
    AddAsyncJob ({ commit }, jobJson) {
      var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
      jobsArray.push(jobJson)
      commit('SET_ASYNC_JOB_IDS', jobsArray)
    }
  }
}

export default user
<commit_msg>auth: Removing setting a cookie for sessionkey (#453)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

import Cookies from 'js-cookie'
import Vue from 'vue'
import md5 from 'md5'
import message from 'ant-design-vue/es/message'
import router from '@/router'
import store from '@/store'
import { login, logout, api } from '@/api'
import { ACCESS_TOKEN, CURRENT_PROJECT, DEFAULT_THEME, APIS, ASYNC_JOB_IDS } from '@/store/mutation-types'

const user = {
  state: {
    token: '',
    name: '',
    avatar: '',
    info: {},
    apis: {},
    features: {},
    project: {},
    asyncJobIds: [],
    isLdapEnabled: false,
    cloudian: {}
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_PROJECT: (state, project = {}) => {
      Vue.ls.set(CURRENT_PROJECT, project)
      state.project = project
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_INFO: (state, info) => {
      state.info = info
    },
    SET_APIS: (state, apis) => {
      state.apis = apis
      Vue.ls.set(APIS, apis)
    },
    SET_FEATURES: (state, features) => {
      state.features = features
    },
    SET_ASYNC_JOB_IDS: (state, jobsJsonArray) => {
      Vue.ls.set(ASYNC_JOB_IDS, jobsJsonArray)
      state.asyncJobIds = jobsJsonArray
    },
    SET_LDAP: (state, isLdapEnabled) => {
      state.isLdapEnabled = isLdapEnabled
    },
    SET_CLOUDIAN: (state, cloudian) => {
      state.cloudian = cloudian
    },
    RESET_THEME: (state) => {
      Vue.ls.set(DEFAULT_THEME, 'light')
    }
  },

  actions: {
    SetProject ({ commit }, project) {
      commit('SET_PROJECT', project)
    },
    Login ({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(response => {
          const result = response.loginresponse || {}

          Cookies.set('account', result.account, { expires: 1 })
          Cookies.set('domainid', result.domainid, { expires: 1 })
          Cookies.set('role', result.type, { expires: 1 })
          Cookies.set('timezone', result.timezone, { expires: 1 })
          Cookies.set('timezoneoffset', result.timezoneoffset, { expires: 1 })
          Cookies.set('userfullname', result.firstname + ' ' + result.lastname, { expires: 1 })
          Cookies.set('userid', result.userid, { expires: 1 })
          Cookies.set('username', result.username, { expires: 1 })

          Vue.ls.set(ACCESS_TOKEN, result.sessionkey, 24 * 60 * 60 * 1000)
          commit('SET_TOKEN', result.sessionkey)

          commit('SET_APIS', {})
          commit('SET_NAME', '')
          commit('SET_AVATAR', '')
          commit('SET_INFO', {})
          commit('SET_PROJECT', {})
          commit('SET_ASYNC_JOB_IDS', [])
          commit('SET_FEATURES', {})
          commit('SET_LDAP', {})
          commit('SET_CLOUDIAN', {})

          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    GetInfo ({ commit }) {
      return new Promise((resolve, reject) => {
        const cachedApis = Vue.ls.get(APIS, {})
        const hasAuth = Object.keys(cachedApis).length > 0
        if (hasAuth) {
          console.log('Login detected, using cached APIs')
          commit('SET_APIS', cachedApis)
          resolve(cachedApis)
        } else {
          // This will show the dashboard and some common navigation sections
          // to most users/roles, while we complete API autodiscovery
          const apis = {}
          apis.listUsers = {}
          apis.listAccounts = {}
          commit('SET_APIS', apis)
          resolve(apis)

          const hide = message.loading('Discovering features...', 0)
          api('listApis').then(response => {
            const apis = {}
            const apiList = response.listapisresponse.api
            for (var idx = 0; idx < apiList.length; idx++) {
              const api = apiList[idx]
              const apiName = api.name
              apis[apiName] = {
                params: api.params,
                response: api.response
              }
            }
            commit('SET_APIS', apis)
            store.dispatch('GenerateRoutes', { apis }).then(() => {
              router.addRoutes(store.getters.addRouters)
            })
            hide()
            message.success('Discovered all available features!')
          }).catch(error => {
            reject(error)
          })
        }

        api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
          const result = response.listusersresponse.user[0]
          commit('SET_INFO', result)
          commit('SET_NAME', result.firstname + ' ' + result.lastname)
          if ('email' in result) {
            commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5(result.email))
          } else {
            commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5('dev@cloudstack.apache.org'))
          }
        }).catch(error => {
          reject(error)
        })

        api('listCapabilities').then(response => {
          const result = response.listcapabilitiesresponse.capability
          commit('SET_FEATURES', result)
        }).catch(error => {
          reject(error)
        })

        api('listLdapConfigurations').then(response => {
          const ldapEnable = (response.ldapconfigurationresponse.count > 0)
          commit('SET_LDAP', ldapEnable)
        }).catch(error => {
          reject(error)
        })

        api('cloudianIsEnabled').then(response => {
          const cloudian = response.cloudianisenabledresponse.cloudianisenabled || {}
          commit('SET_CLOUDIAN', cloudian)
        }).catch(ignored => {
        })
      })
    },

    Logout ({ commit, state }) {
      return new Promise((resolve) => {
        var cloudianUrl = null
        if (state.cloudian.url && state.cloudian.enabled) {
          cloudianUrl = state.cloudian.url + 'logout.htm?redirect=' + encodeURIComponent(window.location.href)
        }

        Object.keys(Cookies.get()).forEach(cookieName => {
          Cookies.remove(cookieName)
          Cookies.remove(cookieName, { path: '/client' })
        })

        commit('SET_TOKEN', '')
        commit('SET_APIS', {})
        commit('SET_PROJECT', {})
        commit('SET_ASYNC_JOB_IDS', [])
        commit('SET_FEATURES', {})
        commit('SET_LDAP', {})
        commit('SET_CLOUDIAN', {})
        commit('RESET_THEME')
        Vue.ls.remove(CURRENT_PROJECT)
        Vue.ls.remove(ACCESS_TOKEN)
        Vue.ls.remove(ASYNC_JOB_IDS)

        logout(state.token).then(() => {
          if (cloudianUrl) {
            window.location.href = cloudianUrl
          } else {
            resolve()
          }
        }).catch(() => {
          resolve()
        })
      })
    },
    AddAsyncJob ({ commit }, jobJson) {
      var jobsArray = Vue.ls.get(ASYNC_JOB_IDS, [])
      jobsArray.push(jobJson)
      commit('SET_ASYNC_JOB_IDS', jobsArray)
    }
  }
}

export default user
<|endoftext|>


<commit_before>var xml     = require('xml');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.title          = options.title;
    this.description    = options.description;
    this.link           = options.link;
    this.image          = options.image;
    this.copyright      = options.copyright;

    this.author         = options.author;

    this.items          = [];
    this.categories     = [];

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            image:          options.image,
            author:         options.author
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
    	console.warn('DEPRECATED: use addItem() instead of item()');
    	return this.addItem(options);
    }

    this.category = function (category) {
    	console.warn('DEPRECATED: use addCategory() instead of category()');
    	return this.addCategory(category);
    }
};

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    feed.push({ title:      options.title });
    feed.push({ id:         options.link });
    feed.push({ updated:    RFC3339(d) });      // Auto-magic :)

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/
    if(author)
    {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (relative link to feed) - will be added in a future version

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/
    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    // contributor

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i=0; i<entries.length; i++) {
        //
        // entry: required elements
        //
        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        // entry author(s)
        if(entries[i].author) {
            for(var y=0; y<entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor

        // published

        // source

        // rights

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var author = options.author;
    var d = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    // Required RSS 2.0 channel elements
    channel.push({ title:           options.title });
    channel.push({ description:     options.description });
    channel.push({ link:            options.link });

    if(author.email && author.name)
        channel.push({ author: author.email + ' (' + author.name + ')' });

    else if(author.email && !author.name)
        channel.push({ author: author.email });

    else if(!author.email && author.name)
         channel.push({ author: author.name });

    channel.push({ lastBuildDate:   d.toUTCString() })

    if(options.image)
        channel.push({ image:   options.image });

    if(options.copyright)
        channel.push({ copyright:   options.copyright });

    channel.push({ generator:   generatorName });

    // Categories
    for(var i=0; i<categories.length; i++)
        channel.push({ category: categories[i] });

    // Items
    for(var i=0; i<items.length; i++) {
        var item = [
            { title:        { _cdata: items[i].title }},
            { link:         items[i].link },
            { pubDate:      items[i].date.toUTCString() },
            { description:  { _cdata: items[i].description }}
        ];

        if(items[i].author) {
            for(var y=0; y<items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

module.exports = Feed;<commit_msg>adding 'docs' node to rss channel<commit_after>var xml = require('xml');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.title       = options.title;
    this.description = options.description;
    this.link        = options.link;
    this.image       = options.image;
    this.copyright   = options.copyright;

    this.author      = options.author;

    this.items       = [];
    this.categories  = [];

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            image:          options.image,
            author:         options.author
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
    	console.warn('DEPRECATED: use addItem() instead of item()');
    	return this.addItem(options);
    }

    this.category = function (category) {
    	console.warn('DEPRECATED: use addCategory() instead of category()');
    	return this.addCategory(category);
    }
};

var urlPattern = new RegExp('(http|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?');

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    feed.push({ title:      options.title });
    feed.push({ id:         options.link });
    feed.push({ updated:    RFC3339(d) });      // Auto-magic :)

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/
    if(author)
    {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (relative link to feed) - will be added in a future version

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/
    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    // contributor

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i=0; i<entries.length; i++) {
        //
        // entry: required elements
        //
        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        // entry author(s)
        if(entries[i].author) {
            for(var y=0; y<entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor

        // published

        // source

        // rights

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var author = options.author;
    var d = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    // Required RSS 2.0 channel elements
    channel.push({ title:           options.title });
    channel.push({ description:     options.description });
    channel.push({ link:            options.link });

    if(author.email && author.name)
        channel.push({ author: author.email + ' (' + author.name + ')' });

    else if(author.email && !author.name)
        channel.push({ author: author.email });

    else if(!author.email && author.name)
         channel.push({ author: author.name });

    channel.push({ lastBuildDate:   d.toUTCString() });
    channel.push({ docs: 'http://blogs.law.harvard.edu/tech/rss'});

    if(options.image)
        channel.push({ image:   options.image });

    if(options.copyright)
        channel.push({ copyright:   options.copyright });

    channel.push({ generator:   generatorName });

    // Categories
    for(var i=0; i<categories.length; i++)
        channel.push({ category: categories[i] });

    // Items
    for(var i=0; i<items.length; i++) {
        var item = [
            { title:        { _cdata: items[i].title }},
            { link:         items[i].link },
            { pubDate:      items[i].date.toUTCString() },
            { description:  { _cdata: items[i].description }}
        ];

        if(items[i].author) {
            for(var y=0; y<items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

module.exports = Feed;<|endoftext|>


<commit_before>/*
 *     Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/* jshint jasmine:true */
/* globals StyledElements */

(function (ns) {

    "use strict";

    describe("KeywordSuggestion", function () {

        describe("new KeywordSuggestion([options])", function () {

            it("can be created without passing any option", function () {
                var element = new ns.KeywordSuggestion();
                expect(element).not.toEqual(null);
            });

        });

        describe("appendEndpoint()", function () {
            var engine;

            beforeEach(function () {
                // Provide a default instance of Select for testing
                engine = new ns.KeywordSuggestion();
            });

            it("should append normal endpoints", function () {
                var endpoint = new Wirecloud.ui.WiringEditor.Endpoint("source",
                    {
                        id: "widget/19/condition-list",
                        friendcode: "list",
                        name: "",
                        description: "",
                        label: ""
                    },
                    {
                        id: "CoNWeT/jenkins-project-build-list/0.1.9",
                        type: "widget"
                    }
                );
                expect(engine.appendEndpoint(endpoint)).toBe(engine);
                expect(engine.endpoints.source.list.length).toBe(1);
            });
        });
    });

})(Wirecloud.wiring);
<commit_msg>Add KeywordSuggestion tests to remove endpoints<commit_after>/*
 *     Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/* jshint jasmine:true */
/* globals StyledElements */

(function (ns) {

    "use strict";

    describe("KeywordSuggestion", function () {

        describe("new KeywordSuggestion([options])", function () {

            it("can be created without passing any option", function () {
                var element = new ns.KeywordSuggestion();
                expect(element).not.toEqual(null);
            });

        });

        describe("appendEndpoint()", function () {
            var engine;

            beforeEach(function () {
                // Provide a default instance of Select for testing
                engine = new ns.KeywordSuggestion();
            });

            it("should append normal endpoints", function () {
                var endpoint = new Wirecloud.ui.WiringEditor.Endpoint("source",
                    {
                        id: "widget/19/condition-list",
                        friendcode: "list",
                        name: "",
                        description: "",
                        label: ""
                    },
                    {
                        id: "CoNWeT/jenkins-project-build-list/0.1.9",
                        type: "widget"
                    }
                );
                expect(engine.appendEndpoint(endpoint)).toBe(engine);
                expect(engine.endpoints.source.list.length).toBe(1);
            });
        });

        describe("removeEndpoint(endpoint)", function () {
            var engine;

            beforeEach(function () {
                // Provide a default instance of Select for testing
                engine = new ns.KeywordSuggestion();
            });

            it("should remove endpoints", function () {
                var endpoint1 = new Wirecloud.ui.WiringEditor.Endpoint("source",
                    {
                        id: "widget/19/condition-list",
                        friendcode: "list",
                        name: "",
                        description: "",
                        label: ""
                    },
                    {
                        id: "CoNWeT/jenkins-project-build-list/0.1.9",
                        type: "widget",
                        equals: function () {return false;}
                    }
                );
                var endpoint2 = new Wirecloud.ui.WiringEditor.Endpoint("target",
                    {
                        id: "operator/1/list",
                        friendcode: "list",
                        name: "",
                        description: "",
                        label: ""
                    },
                    {
                        id: "CoNWeT/example-1/1.0",
                        type: "operator",
                        equals: function () {return false;}
                    }
                );
                var endpoint3 = new Wirecloud.ui.WiringEditor.Endpoint("target",
                    {
                        id: "operator/1/list-sorted",
                        friendcode: "list list-sorted",
                        name: "",
                        description: "",
                        label: ""
                    },
                    {
                        id: "CoNWeT/example-1/1.0",
                        type: "operator",
                        equals: function () {return false;}
                    }
                );
                var callback = jasmine.createSpy('callback');

                engine
                    .appendEndpoint(endpoint1)
                    .appendEndpoint(endpoint2)
                    .appendEndpoint(endpoint3)
                    .removeEndpoint(endpoint3);

                expect(Object.keys(engine.endpoints.target)).toEqual(['list']);

                engine.forEachSuggestion(endpoint1, callback);
                expect(callback.calls.count()).toEqual(1);

                engine
                    .removeEndpoint(endpoint2);
                callback.calls.reset();

                expect(Object.keys(engine.endpoints.target)).toEqual([]);

                engine.forEachSuggestion(endpoint1, callback);
                expect(callback.calls.count()).toEqual(0);
            });

            it("should remove missing endpoints", function () {
                var endpoint1 = new Wirecloud.ui.WiringEditor.Endpoint("source",
                    {
                        id: "widget/19/condition-list",
                        friendcode: "list",
                        name: "",
                        description: "",
                        label: "",
                        missing: true
                    },
                    {
                        id: "CoNWeT/jenkins-project-build-list/0.1.9",
                        type: "widget"
                    }
                );

                engine
                    .appendEndpoint(endpoint1)
                    .removeEndpoint(endpoint1);

                expect(engine.endpoints.source.list).toBeUndefined();
            });
        });
    });

})(Wirecloud.wiring);
<|endoftext|>