<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "c9", "settings", "ui", "menus", "ace",
        "ace.gotoline", "tabManager"
    ];
    main.provides = ["ace.status"];
    return main;

    function main(options, imports, register) {
        var c9        = imports.c9;
        var Plugin    = imports.Plugin;
        var settings  = imports.settings;
        var ui        = imports.ui;
        var tabs      = imports.tabManager;
        var menus     = imports.menus;
        var gotoline  = imports["ace.gotoline"];
        var aceHandle = imports.ace;

        var skin    = require("text!./skin.xml");
        var markup  = require("text!./statusbar.xml");
        var menuAml = require("text!./menu.xml");

        var aceWhitespace = require("ace/ext/whitespace");
        var lang = require("ace/lib/lang");

        /***** Generic Load *****/

        // Set up the generic handle
        var deps       = main.consumes.slice(0, main.consumes.length - 1);
        var handle     = new Plugin("Ajax.org", deps);
        var handleEmit = handle.getEmitter();

        var statusbars = {};
        var menuItem, menu, menuTabs;

        handle.on("load", function(){
            settings.on("read", function(e){
                settings.setDefaults("user/ace/statusbar", [["show", "true"]]);

                if (settings.getBool("user/ace/statusbar/@show"))
                    handleEmit("show");
            }, handle);

            menuItem = new apf.item({
                test : "1",
                type : "check",
                checked : "[{settings.model}::user/ace/statusbar/@show]"
                // -> if you're looking for disabled, check the init function :-)
                // the moment that someone clicks this thing well call preinit
                // (its already called if the user has it checked on IDE load)
            });

            menus.addItemByPath("View/Status Bar", menuItem, 600, handle);

            aceHandle.on("create", function(e){
                var editor = e.editor;
                var statusbar;

                editor.on("draw", function(){
                    statusbar = new Statusbar(editor);
                }, editor);
                editor.on("unload", function h2(){
                    if (statusbar) statusbar.unload();
                }, editor);
            });

            ui.insertMarkup(null, menuAml, handle);

            menu     = handle.getElement("menu");
            menuTabs = handle.getElement("menuTabs");

            var currentSession;
            function setCurrentSession(menu){
                var node = menu.opener;
                while (node && node.localName != "tab")
                    node = node.parentNode;
                if (!node) return;

                var tab = node.cloud9pane.getTab();
                currentSession = tab.document.getSession();
            }

            function setOption(name, value){
                if (currentSession) {
                    currentSession.session.setOption(name, value);
                    currentSession.statusBar.update();
                }
            }

            function getOption(name){
                return currentSession && currentSession.session.getOption(name);
            }

            // Checkboxes
            menu.on("afterrender", function(e){
                var itmSbWrap   = window.itmSbWrap;
                var itmSbWrapVP = window.itmSbWrapVP;

                itmSbWrap.on("click", function(){
                    setOption("wrap", itmSbWrap.checked
                        ? itmSbWrapVP.checked || "printMargin"
                        : false);
                });
                itmSbWrapVP.on("click", function(){
                    setOption("wrap", itmSbWrap.checked
                        ? itmSbWrapVP.checked || "printMargin"
                        : false);
                });

                function update(e){
                    if (!e || e.value){
                        setCurrentSession(menu);

                        var wrap = getOption("wrap");
                        itmSbWrap.setAttribute("checked", !ui.isFalse(wrap));
                        itmSbWrapVP.setAttribute("checked", wrap != "printMargin");
                    }
                }

                menu.on("prop.visible", update);
                update();
            });

            // Menu Tab functionality
            var handlers = [
                function(){ setOption("useSoftTabs", !this.checked) },
                function(){},
                function(){ setOption("tabSize", 2) },
                function(){ setOption("tabSize", 3) },
                function(){ setOption("tabSize", 4) },
                function(){ setOption("tabSize", 8) },
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.detectIndentation(session.session);
                    var useSoftTabs = session.session.getOption("useSoftTabs");
                    var tabSize     = session.session.getOption("tabSize");
                    menuTabs.childNodes[0].setAttribute("checked", useSoftTabs);
                    if (tabSize < 9)
                        menuTabs.childNodes[tabSize + 1].setAttribute("selected", true);
                    session.statusBar.update();
                },
                // Tabs to Spaces
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.convertIndentation(session.session, " ");
                    session.statusBar.update();
                },
                // Spaces to Tabs
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.convertIndentation(session.session, "\t");
                    session.statusBar.update();
                }
            ];

            menuTabs.on("afterrender", function(e){
                var items = menuTabs.selectNodes("a:item");
                items.forEach(function(node, idx){
                    node.on("click", handlers[idx]);
                });

                var itmTabSize  = window.itmTabSize;
                itmTabSize.on("afterchange", function(){
                    setOption("tabSize", this.value);
                    update();
                });

                var lut = [0,0,2,3,4,0,0,0,5];

                function update(e){
                    if (e && !e.value)
                        return;

                    setCurrentSession(menuTabs);

                    items[0].setAttribute("checked", getOption("useSoftTabs"));

                    var tabSize = getOption("tabSize") || 1;
                    items.forEach(function(node, idx){
                        node.setAttribute("selected", "false");
                    });
                    if (lut[tabSize])
                        items[lut[tabSize]].setAttribute("selected", "true");
                    itmTabSize.setAttribute("value", getOption("tabSize"));
                }

                menuTabs.on("prop.visible", update);
                update();
            });
        });

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

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

            // Import Skin
            ui.insertSkin({
                name         : "c9statusbar",
                data         : skin,
                "media-path" : options.staticPrefix + "/images/",
                "icon-path"  : options.staticPrefix + "/icons/"
            }, handle);
        };

        function getStatusbar(editor){
            return statusbars[editor.name];
        }

        function show(){
            settings.set("user/ace/statusbar/@show", "true");
        }

        function hide(){
            settings.set("user/ace/statusbar/@show", "false");
        }

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

        /**
         * Manages the status bar instances for ace.
         * @singleton
         **/
        handle.freezePublicAPI({
            /**
             * Show all the status bars.
             */
            show : show,

            /**
             * Hide all the status bars.
             */
            hide : hide,

            /**
             * Retrieve the status bar that belongs to an ace editor.
             * @param {Editor} ace  The ace editor the status bar belongs to.
             * @return {ace.status.Statusbar}
             */
            getStatusbar : getStatusbar,

            /**
             * Inserts CSS for the statusbar.
             * @private
             */
            draw : draw
        });

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

        var counter = 0;

        function Statusbar(editor){
            var plugin = new Plugin("Ajax.org", deps);
            var emit   = plugin.getEmitter();

            var showRange;

            var bar, lblSelection, lblStatus, lblRowCol, lblTabs, lblSyntax; // ui elements

            statusbars[editor.name] = plugin;

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

                function updateBarVisible(){
                    if (!settings.getBool("user/ace/statusbar/@show")) {
                        bar && bar.hide();
                        menuItem.enable();
                    }
                    else {
                        draw();
                        bar.show();
                        menuItem.enable();
                    }
                }

                settings.on("user/ace/statusbar", updateBarVisible, plugin);

                handle.on("show", show);

                if (settings.getBool("user/ace/statusbar/@show"))
                    draw();

                editor.on("documentLoad", function(e){
                    var session = e.doc.getSession();
                    session.statusBar = plugin;
                    session.session.on("changeMode", function(e){
                        var acesession = session.session;
                        if (acesession) {
                            var mode = acesession.syntax.uCaseFirst();
                            lblSyntax && lblSyntax.setAttribute("caption", mode);
                        }
                    });
                }, plugin);
                editor.on("documentActivate", function(e){
                    var session = e.doc.getSession();
                    var mode    = session.session.syntax.uCaseFirst();
                    lblSyntax && lblSyntax.setAttribute("caption", mode);
                }, plugin);
                editor.on("documentUnload", function(e){
                    delete e.doc.getSession().statusBar;
                }, plugin);
            }

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

                handle.draw();

                // Create UI elements
                var htmlNode = editor.ace.container.parentNode.host;
                ui.insertMarkup(htmlNode, markup, plugin);

                function setTheme(e){
                    var theme    = e.theme;
                    if (!theme) return;

                    var cssClass = theme.cssClass;
                    var isDark   = theme.isDark;

                    var bg = ui.getStyleRule("." + cssClass, "background-color");

                    bar.setAttribute("class", isDark ? "ace_dark" : "");
                    if (bg) {
                        bg = bg.replace(/rgb\((.*)\)/, "rgba($1, 0.9)");
                        bar.$ext.style.backgroundColor = bg;
                    }
                }
                editor.on("themeChange", setTheme);

                bar          = plugin.getElement("bar");
                lblSelection = plugin.getElement("lblSelectionLength");
                lblStatus    = plugin.getElement("lblEditorStatus");
                lblRowCol    = plugin.getElement("lblRowCol");
                lblTabs      = plugin.getElement("lblTabs");
                lblSyntax    = plugin.getElement("lblSyntax");

                // For editor search of submenus
                bar.editor = editor;

                // Set sub menus
                var button = plugin.getElement("btnSbPrefs");
                button.setAttribute("submenu", menu);

                lblTabs.setAttribute("submenu", menuTabs);

                var mnuSyntax = menus.get("View/Syntax").menu;
                lblSyntax.setAttribute("submenu", mnuSyntax);
                lblSyntax.on("mousedown", function(){
                    if (editor.activeDocument)
                        tabs.focusTab(editor.activeDocument.tab);
                });

                // Click behavior for the labels
                lblSelection.on("click", function(){
                    showRange = !showRange;
                    updateStatus();
                });

                lblRowCol.on("click", function(){
                    gotoline.gotoline();
                });

                // Hook into ace
                var ace = editor.ace;
                if (!ace.$hasStatusBar) {
                    // Throttle UI updates
                    var selStatusUpdate = lang.delayedCall(updateSelStatus, 100);
                    var statusUpdate    = lang.delayedCall(updateStatus, 100);
                    ace.on("changeSelection", function() {selStatusUpdate.schedule()});
                    ace.on("changeStatus", function() {statusUpdate.schedule()});
                    ace.renderer.on("scrollbarVisibilityChanged", function(e, renderer) {
                        bar.$ext.style.right = renderer.scrollBarV.getWidth() + 5 + "px";
                        bar.$ext.style.bottom = renderer.scrollBarH.getHeight() + 3 + "px";
                    });
                    ace.$hasStatusBar = true;

                    var theme = editor.theme;
                    setTheme({theme: theme});
                }

                // Update status information
                updateStatus();

                emit("draw");
            }

            /***** Helper Functions *****/

            function updateSelStatus(){
                var ace = editor.ace;
                if (!ace) return;

                if (!ace.selection.isEmpty()) {
                    var range = ace.getSelectionRange();
                    var selLen;

                    if (showRange) {
                        selLen = "(" +
                            (range.end.row - range.start.row) + ":" +
                            (range.end.column - range.start.column) + ")";
                    }
                    else {
                        selLen = "(" + ace.session.getTextRange(range).length + " Bytes)";
                    }

                    lblSelection.setAttribute("caption", selLen);
                }
                else {
                    lblSelection.setAttribute("caption", "");
                }

                var cursor = ace.selection.lead;
                lblRowCol.setAttribute("caption", (cursor.row + 1) + ":" + (cursor.column + 1));
            }

            function updateStatus() {
                var ace = editor.ace;

                updateSelStatus();

                lblTabs.setAttribute("caption",
                    (ace.getOption("useSoftTabs") ? "Spaces" : "Tabs") + ": "
                      + ace.getOption("tabSize")); // "\\[" + + "\\]");

                var status = "";
                if (ace.$vimModeHandler)
                    status = ace.$vimModeHandler.getStatusText();
                else if (ace.commands.recording)
                    status = "REC";

                lblStatus.setAttribute("caption", status);
            }

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

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

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

            });
            plugin.on("unload", function(){
                loaded = false;
            });

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

            /**
             * The status bar for ace editors.
             * @class ace.status.Statusbar
             **/
            plugin.freezePublicAPI({
                /**
                 * Redraw the display of the statusbar
                 */
                update : updateStatus
            });

            plugin.load("acestatus" + counter++);

            return plugin;
        }

        register(null, {
            "ace.status" : handle
        });
    }
});<commit_msg>Send "create" event for immedate window<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "c9", "settings", "ui", "menus", "ace",
        "ace.gotoline", "tabManager"
    ];
    main.provides = ["ace.status"];
    return main;

    function main(options, imports, register) {
        var c9        = imports.c9;
        var Plugin    = imports.Plugin;
        var settings  = imports.settings;
        var ui        = imports.ui;
        var tabs      = imports.tabManager;
        var menus     = imports.menus;
        var gotoline  = imports["ace.gotoline"];
        var aceHandle = imports.ace;

        var skin    = require("text!./skin.xml");
        var markup  = require("text!./statusbar.xml");
        var menuAml = require("text!./menu.xml");

        var aceWhitespace = require("ace/ext/whitespace");
        var lang = require("ace/lib/lang");

        /***** Generic Load *****/

        // Set up the generic handle
        var deps       = main.consumes.slice(0, main.consumes.length - 1);
        var handle     = new Plugin("Ajax.org", deps);
        var handleEmit = handle.getEmitter();

        var statusbars = {};
        var menuItem, menu, menuTabs;

        handle.on("load", function(){
            settings.on("read", function(e){
                settings.setDefaults("user/ace/statusbar", [["show", "true"]]);

                if (settings.getBool("user/ace/statusbar/@show"))
                    handleEmit("show");
            }, handle);

            menuItem = new apf.item({
                test : "1",
                type : "check",
                checked : "[{settings.model}::user/ace/statusbar/@show]"
                // -> if you're looking for disabled, check the init function :-)
                // the moment that someone clicks this thing well call preinit
                // (its already called if the user has it checked on IDE load)
            });

            menus.addItemByPath("View/Status Bar", menuItem, 600, handle);

            aceHandle.on("create", function(e){
                if (e.editor.type != "ace")
                    return;

                var editor = e.editor;
                var statusbar;

                editor.on("draw", function(){
                    statusbar = new Statusbar(editor);
                }, editor);
                editor.on("unload", function h2(){
                    if (statusbar) statusbar.unload();
                }, editor);
            });

            ui.insertMarkup(null, menuAml, handle);

            menu     = handle.getElement("menu");
            menuTabs = handle.getElement("menuTabs");

            var currentSession;
            function setCurrentSession(menu){
                var node = menu.opener;
                while (node && node.localName != "tab")
                    node = node.parentNode;
                if (!node) return;

                var tab = node.cloud9pane.getTab();
                currentSession = tab.document.getSession();
            }

            function setOption(name, value){
                if (currentSession) {
                    currentSession.session.setOption(name, value);
                    currentSession.statusBar.update();
                }
            }

            function getOption(name){
                return currentSession && currentSession.session.getOption(name);
            }

            // Checkboxes
            menu.on("afterrender", function(e){
                var itmSbWrap   = window.itmSbWrap;
                var itmSbWrapVP = window.itmSbWrapVP;

                itmSbWrap.on("click", function(){
                    setOption("wrap", itmSbWrap.checked
                        ? itmSbWrapVP.checked || "printMargin"
                        : false);
                });
                itmSbWrapVP.on("click", function(){
                    setOption("wrap", itmSbWrap.checked
                        ? itmSbWrapVP.checked || "printMargin"
                        : false);
                });

                function update(e){
                    if (!e || e.value){
                        setCurrentSession(menu);

                        var wrap = getOption("wrap");
                        itmSbWrap.setAttribute("checked", !ui.isFalse(wrap));
                        itmSbWrapVP.setAttribute("checked", wrap != "printMargin");
                    }
                }

                menu.on("prop.visible", update);
                update();
            });

            // Menu Tab functionality
            var handlers = [
                function(){ setOption("useSoftTabs", !this.checked) },
                function(){},
                function(){ setOption("tabSize", 2) },
                function(){ setOption("tabSize", 3) },
                function(){ setOption("tabSize", 4) },
                function(){ setOption("tabSize", 8) },
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.detectIndentation(session.session);
                    var useSoftTabs = session.session.getOption("useSoftTabs");
                    var tabSize     = session.session.getOption("tabSize");
                    menuTabs.childNodes[0].setAttribute("checked", useSoftTabs);
                    if (tabSize < 9)
                        menuTabs.childNodes[tabSize + 1].setAttribute("selected", true);
                    session.statusBar.update();
                },
                // Tabs to Spaces
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.convertIndentation(session.session, " ");
                    session.statusBar.update();
                },
                // Spaces to Tabs
                function(){
                    var tab = tabs.focussedTab;
                    if (!tab) return;
                    var session = tab.document.getSession();
                    aceWhitespace.convertIndentation(session.session, "\t");
                    session.statusBar.update();
                }
            ];

            menuTabs.on("afterrender", function(e){
                var items = menuTabs.selectNodes("a:item");
                items.forEach(function(node, idx){
                    node.on("click", handlers[idx]);
                });

                var itmTabSize  = window.itmTabSize;
                itmTabSize.on("afterchange", function(){
                    setOption("tabSize", this.value);
                    update();
                });

                var lut = [0,0,2,3,4,0,0,0,5];

                function update(e){
                    if (e && !e.value)
                        return;

                    setCurrentSession(menuTabs);

                    items[0].setAttribute("checked", getOption("useSoftTabs"));

                    var tabSize = getOption("tabSize") || 1;
                    items.forEach(function(node, idx){
                        node.setAttribute("selected", "false");
                    });
                    if (lut[tabSize])
                        items[lut[tabSize]].setAttribute("selected", "true");
                    itmTabSize.setAttribute("value", getOption("tabSize"));
                }

                menuTabs.on("prop.visible", update);
                update();
            });
        });

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

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

            // Import Skin
            ui.insertSkin({
                name         : "c9statusbar",
                data         : skin,
                "media-path" : options.staticPrefix + "/images/",
                "icon-path"  : options.staticPrefix + "/icons/"
            }, handle);
        };

        function getStatusbar(editor){
            return statusbars[editor.name];
        }

        function show(){
            settings.set("user/ace/statusbar/@show", "true");
        }

        function hide(){
            settings.set("user/ace/statusbar/@show", "false");
        }

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

        /**
         * Manages the status bar instances for ace.
         * @singleton
         **/
        handle.freezePublicAPI({
            /**
             * Show all the status bars.
             */
            show : show,

            /**
             * Hide all the status bars.
             */
            hide : hide,

            /**
             * Retrieve the status bar that belongs to an ace editor.
             * @param {Editor} ace  The ace editor the status bar belongs to.
             * @return {ace.status.Statusbar}
             */
            getStatusbar : getStatusbar,

            /**
             * Inserts CSS for the statusbar.
             * @private
             */
            draw : draw
        });

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

        var counter = 0;

        function Statusbar(editor){
            var plugin = new Plugin("Ajax.org", deps);
            var emit   = plugin.getEmitter();

            var showRange;

            var bar, lblSelection, lblStatus, lblRowCol, lblTabs, lblSyntax; // ui elements

            statusbars[editor.name] = plugin;

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

                function updateBarVisible(){
                    if (!settings.getBool("user/ace/statusbar/@show")) {
                        bar && bar.hide();
                        menuItem.enable();
                    }
                    else {
                        draw();
                        bar.show();
                        menuItem.enable();
                    }
                }

                settings.on("user/ace/statusbar", updateBarVisible, plugin);

                handle.on("show", show);

                if (settings.getBool("user/ace/statusbar/@show"))
                    draw();

                editor.on("documentLoad", function(e){
                    var session = e.doc.getSession();
                    session.statusBar = plugin;
                    session.session.on("changeMode", function(e){
                        var acesession = session.session;
                        if (acesession) {
                            var mode = acesession.syntax.uCaseFirst();
                            lblSyntax && lblSyntax.setAttribute("caption", mode);
                        }
                    });
                }, plugin);
                editor.on("documentActivate", function(e){
                    var session = e.doc.getSession();
                    var mode    = session.session.syntax.uCaseFirst();
                    lblSyntax && lblSyntax.setAttribute("caption", mode);
                }, plugin);
                editor.on("documentUnload", function(e){
                    delete e.doc.getSession().statusBar;
                }, plugin);
            }

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

                handle.draw();

                // Create UI elements
                var htmlNode = editor.ace.container.parentNode.host;
                ui.insertMarkup(htmlNode, markup, plugin);

                function setTheme(e){
                    var theme    = e.theme;
                    if (!theme) return;

                    var cssClass = theme.cssClass;
                    var isDark   = theme.isDark;

                    var bg = ui.getStyleRule("." + cssClass, "background-color");

                    bar.setAttribute("class", isDark ? "ace_dark" : "");
                    if (bg) {
                        bg = bg.replace(/rgb\((.*)\)/, "rgba($1, 0.9)");
                        bar.$ext.style.backgroundColor = bg;
                    }
                }
                editor.on("themeChange", setTheme);

                bar          = plugin.getElement("bar");
                lblSelection = plugin.getElement("lblSelectionLength");
                lblStatus    = plugin.getElement("lblEditorStatus");
                lblRowCol    = plugin.getElement("lblRowCol");
                lblTabs      = plugin.getElement("lblTabs");
                lblSyntax    = plugin.getElement("lblSyntax");

                // For editor search of submenus
                bar.editor = editor;

                // Set sub menus
                var button = plugin.getElement("btnSbPrefs");
                button.setAttribute("submenu", menu);

                lblTabs.setAttribute("submenu", menuTabs);

                var mnuSyntax = menus.get("View/Syntax").menu;
                lblSyntax.setAttribute("submenu", mnuSyntax);
                lblSyntax.on("mousedown", function(){
                    if (editor.activeDocument)
                        tabs.focusTab(editor.activeDocument.tab);
                });

                // Click behavior for the labels
                lblSelection.on("click", function(){
                    showRange = !showRange;
                    updateStatus();
                });

                lblRowCol.on("click", function(){
                    gotoline.gotoline();
                });

                // Hook into ace
                var ace = editor.ace;
                if (!ace.$hasStatusBar) {
                    // Throttle UI updates
                    var selStatusUpdate = lang.delayedCall(updateSelStatus, 100);
                    var statusUpdate    = lang.delayedCall(updateStatus, 100);
                    ace.on("changeSelection", function() {selStatusUpdate.schedule()});
                    ace.on("changeStatus", function() {statusUpdate.schedule()});
                    ace.renderer.on("scrollbarVisibilityChanged", function(e, renderer) {
                        bar.$ext.style.right = renderer.scrollBarV.getWidth() + 5 + "px";
                        bar.$ext.style.bottom = renderer.scrollBarH.getHeight() + 3 + "px";
                    });
                    ace.$hasStatusBar = true;

                    var theme = editor.theme;
                    setTheme({theme: theme});
                }

                // Update status information
                updateStatus();

                emit("draw");
            }

            /***** Helper Functions *****/

            function updateSelStatus(){
                var ace = editor.ace;
                if (!ace) return;

                if (!ace.selection.isEmpty()) {
                    var range = ace.getSelectionRange();
                    var selLen;

                    if (showRange) {
                        selLen = "(" +
                            (range.end.row - range.start.row) + ":" +
                            (range.end.column - range.start.column) + ")";
                    }
                    else {
                        selLen = "(" + ace.session.getTextRange(range).length + " Bytes)";
                    }

                    lblSelection.setAttribute("caption", selLen);
                }
                else {
                    lblSelection.setAttribute("caption", "");
                }

                var cursor = ace.selection.lead;
                lblRowCol.setAttribute("caption", (cursor.row + 1) + ":" + (cursor.column + 1));
            }

            function updateStatus() {
                var ace = editor.ace;

                updateSelStatus();

                lblTabs.setAttribute("caption",
                    (ace.getOption("useSoftTabs") ? "Spaces" : "Tabs") + ": "
                      + ace.getOption("tabSize")); // "\\[" + + "\\]");

                var status = "";
                if (ace.$vimModeHandler)
                    status = ace.$vimModeHandler.getStatusText();
                else if (ace.commands.recording)
                    status = "REC";

                lblStatus.setAttribute("caption", status);
            }

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

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

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

            });
            plugin.on("unload", function(){
                loaded = false;
            });

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

            /**
             * The status bar for ace editors.
             * @class ace.status.Statusbar
             **/
            plugin.freezePublicAPI({
                /**
                 * Redraw the display of the statusbar
                 */
                update : updateStatus
            });

            plugin.load("acestatus" + counter++);

            return plugin;
        }

        register(null, {
            "ace.status" : handle
        });
    }
});<|endoftext|>


<commit_before>import {Slice, Fragment} from "../model"

import {Transform} from "./transform"
import {ReplaceStep, ReplaceAroundStep} from "./replace_step"

// :: (Node, number, ?number) → bool
// Tells you whether the range in the given positions' shared
// ancestor, or any of _its_ ancestor nodes, can be lifted out of a
// parent.
export function canLift(doc, from, to) {
  return !!findLiftable(doc.resolve(from), doc.resolve(to == null ? from : to))
}

function rangeDepth($from, $to) {
  let shared = $from.sameDepth($to)
  if ($from.node(shared).isTextblock || $from.pos == $to.pos) --shared
  if (shared < 0 || $from.pos > $to.pos) return null
  return shared
}

function canCut(node, start, end) {
  return (start == 0 || node.canReplace(start, node.childCount)) &&
    (end == node.childCount || node.canReplace(0, start))
}

function findLiftable($from, $to) {
  let shared = rangeDepth($from, $to)
  if (!shared) return null
  let parent = $from.node(shared), content = parent.content.cutByIndex($from.index(shared), $to.indexAfter(shared))
  for (let depth = shared;; --depth) {
    let node = $from.node(depth), index = $from.index(depth)
    if (depth < shared && node.canReplace(index, index + 1, content))
      return {depth, shared, unwrap: false}
    if (depth == 0 || !canCut(node, index, index + 1)) break
  }

  if (parent.isBlock) {
    let joined = Fragment.empty
    content.forEach(node => joined = joined.append(node.content))
    for (let depth = shared;; --depth) {
      let node = $from.node(depth), index = $from.index(depth)
      if (depth < shared && node.canReplace(index, index + 1, joined))
        return {depth, shared, unwrap: true}
      if (depth == 0 || !canCut(node, index, index + 1)) break
    }
  }
}

// :: (number, ?number, ?bool) → Transform
// Lift the nearest liftable ancestor of the [sibling
// range](#Node.siblingRange) of the given positions out of its parent
// (or do nothing if no such node exists). When `silent` is true, this
// won't raise an error when the lift is impossible.
Transform.prototype.lift = function(from, to = from, silent = false) {
  let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
  let liftable = findLiftable($from, $to)
  if (!liftable) {
    if (!silent) throw new RangeError("No valid lift target")
    return this
  }

  let {depth, shared, unwrap} = liftable

  let gapStart = $from.before(shared + 1), gapEnd = $to.after(shared + 1)
  let start = gapStart, end = gapEnd

  let before = Fragment.empty, beforeDepth = 0
  for (let d = shared, splitting = false; d > depth; d--)
    if (splitting || $from.index(d) > 0) {
      splitting = true
      before = Fragment.from($from.node(d).copy(before))
      beforeDepth++
    } else {
      start--
    }
  let after = Fragment.empty, afterDepth = 0
  for (let d = shared, splitting = false; d > depth; d--)
    if (splitting || $to.after(d + 1) < $to.end(d)) {
      splitting = true
      after = Fragment.from($to.node(d).copy(after))
      afterDepth++
    } else {
      end++
    }

  if (unwrap) {
    let joinPos = gapStart, parent = $from.node(shared)
    for (let i = $from.index(shared), e = $to.index(shared) + 1, first = true; i < e; i++, first = false) {
      if (!first) {
        this.join(joinPos)
        end -= 2
        gapEnd -= 2
      }
      joinPos += parent.child(i).nodeSize - (first ? 0 : 2)
    }
    ++gapStart
    --gapEnd
  }

  return this.step(new ReplaceAroundStep(start, end, gapStart, gapEnd,
                                         new Slice(before.append(after), beforeDepth, afterDepth),
                                         before.size - beforeDepth, true))
}

// :: (Node, number, ?number, NodeType, ?Object) → bool
// Determines whether the [sibling range](#Node.siblingRange) of the
// given positions can be wrapped in the given node type.
export function canWrap(doc, from, to, type, attrs) {
  return !!checkWrap(doc.resolve(from), doc.resolve(to == null ? from : to), type, attrs)
}

function checkWrap($from, $to, type, attrs) {
  let shared = rangeDepth($from, $to)
  if (shared == null) return null
  let parent = $from.node(shared)
  let around = parent.contentMatchAt($from.index(shared)).findWrapping(type, attrs)
  if (!around) return null
  if (!parent.canReplaceWith($from.index(shared), $to.indexAfter(shared), around.length ? around[0].type : type,
                             around.length ? around[0].attrs : attrs)) return null
  let inner = parent.child($from.index(shared))
  let inside = type.contentExpr.start(attrs || type.defaultAttrs).findWrapping(inner.type, inner.attrs)
  if (around && inside) return {shared, around, inside}
}

// :: (number, ?number, NodeType, ?Object) → Transform
// Wrap the [sibling range](#Node.siblingRange) of the given positions
// in a node of the given type, with the given attributes (if
// possible).
Transform.prototype.wrap = function(from, to = from, type, wrapAttrs) {
  let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
  let check = checkWrap($from, $to, type, wrapAttrs)
  if (!check) throw new RangeError("Wrap not possible")
  let {shared, around, inside} = check

  let content = Fragment.empty, open = inside.length + 1 + around.length
  for (let i = inside.length - 1; i >= 0; i--) content = Fragment.from(inside[i].type.create(inside[i].attrs, content))
  content = Fragment.from(type.create(wrapAttrs, content))
  for (let i = around.length - 1; i >= 0; i--) content = Fragment.from(around[i].type.create(around[i].attrs, content))

  let start = $from.before(shared + 1), end = $to.after(shared + 1)
  this.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), open, true))

  if (inside.length) {
    let splitPos = start + open, parent = $from.node(shared)
    for (let i = $from.index(shared), e = $to.index(shared) + 1, first = true; i < e; i++, first = false) {
      if (!first) this.split(splitPos, inside.length)
      splitPos += parent.child(i).nodeSize + (first ? 0 : 2 * inside.length)
    }
  }
  return this
}

// :: (number, ?number, NodeType, ?Object) → Transform
// Set the type of all textblocks (partly) between `from` and `to` to
// the given node type with the given attributes.
Transform.prototype.setBlockType = function(from, to = from, type, attrs) {
  if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock")
  let mapFrom = this.steps.length
  this.doc.nodesBetween(from, to, (node, pos) => {
    if (node.isTextblock && !node.hasMarkup(type, attrs)) {
      // Ensure all markup that isn't allowed in the new node type is cleared
      this.clearMarkupFor(this.map(pos, 1, mapFrom), type, attrs)
      let startM = this.map(pos, 1, mapFrom), endM = this.map(pos + node.nodeSize, 1, mapFrom)
      this.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
                                      new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
      return false
    }
  })
  return this
}

// :: (number, ?NodeType, ?Object) → Transform
// Change the type and attributes of the node after `pos`.
Transform.prototype.setNodeType = function(pos, type, attrs) {
  let node = this.doc.nodeAt(pos)
  if (!node) throw new RangeError("No node at given position")
  if (!type) type = node.type
  if (node.type.isLeaf)
    return this.replaceWith(pos, pos + node.nodeSize, type.create(attrs, null, node.marks))

  if (!type.validContent(node.content, attrs))
    throw new RangeError("Invalid content for node type " + type.name)

  return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
                                         new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
}

// :: (Node, number, ?NodeType, ?Object) → bool
// Check whether splitting at the given position is allowed.
export function canSplit(doc, pos, depth = 1, typeAfter, attrsAfter) {
  let $pos = doc.resolve(pos), base = $pos.depth - depth
  if (base < 0 ||
      !$pos.parent.canReplace($pos.index(), $pos.parent.childCount) ||
      !$pos.parent.canReplace(0, $pos.indexAfter()))
    return false
  for (let d = $pos.depth - 1; d > base; d--) {
    let node = $pos.node(d), index = $pos.index(d)
    if (!node.canReplace(0, index) ||
        !node.canReplaceWith(index, node.childCount, typeAfter || $pos.node(d + 1).type,
                             typeAfter ? attrsAfter : $pos.node(d + 1).attrs))
      return false
    typeAfter = null
  }
  let index = $pos.indexAfter(base)
  return $pos.node(base).canReplaceWith(index, index, typeAfter || $pos.node(base + 1).type,
                                        typeAfter ? attrsAfter : $pos.node(base + 1).attrs)
}

// :: (number, ?number, ?NodeType, ?Object) → Transform
// Split the node at the given position, and optionally, if `depth` is
// greater than one, any number of nodes above that. By default, the part
// split off will inherit the node type of the original node. This can
// be changed by passing `typeAfter` and `attrsAfter`.
Transform.prototype.split = function(pos, depth = 1, typeAfter, attrsAfter) {
  let $pos = this.doc.resolve(pos), before = Fragment.empty, after = Fragment.empty
  for (let d = $pos.depth, e = $pos.depth - depth; d > e; d--) {
    before = Fragment.from($pos.node(d).copy(before))
    after = Fragment.from(typeAfter ? typeAfter.create(attrsAfter, after) : $pos.node(d).copy(after))
    typeAfter = null
  }
  return this.step(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth, true)))
}

// :: (Node, number) → bool
// Test whether the blocks before and after a given position can be
// joined.
export function joinable(doc, pos) {
  let $pos = doc.resolve(pos), index = $pos.index()
  return canJoin($pos.nodeBefore, $pos.nodeAfter) &&
    $pos.parent.canReplace(index, index + 1)
}

function canJoin(a, b) {
  return a && b && !a.isText && a.canAppend(b)
}

// :: (Node, number, ?number) → ?number
// Find an ancestor of the given position that can be joined to the
// block before (or after if `dir` is positive). Returns the joinable
// point, if any.
export function joinPoint(doc, pos, dir = -1) {
  let $pos = doc.resolve(pos)
  for (let d = $pos.depth;; d--) {
    let before, after
    if (d == $pos.depth) {
      before = $pos.nodeBefore
      after = $pos.nodeAfter
    } else if (dir > 0) {
      before = $pos.node(d + 1)
      after = $pos.node(d).maybeChild($pos.index(d) + 1)
    } else {
      before = $pos.node(d).maybeChild($pos.index(d) - 1)
      after = $pos.node(d + 1)
    }
    if (before && !before.isTextblock && canJoin(before, after)) return pos
    if (d == 0) break
    pos = dir < 0 ? $pos.before(d) : $pos.after(d)
  }
}

// :: (number, ?number, ?bool) → Transform
// Join the blocks around the given position. When `silent` is true,
// the method will return without raising an error if the position
// isn't a valid place to join.
Transform.prototype.join = function(pos, depth = 1, silent = false) {
  if (silent && (pos < depth || pos + depth > this.doc.content.size)) return this
  let step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true)
  if (silent) this.maybeStep(step)
  else this.step(step)
  return this
}
<commit_msg>Fix missing check in canWrap<commit_after>import {Slice, Fragment} from "../model"

import {Transform} from "./transform"
import {ReplaceStep, ReplaceAroundStep} from "./replace_step"

// :: (Node, number, ?number) → bool
// Tells you whether the range in the given positions' shared
// ancestor, or any of _its_ ancestor nodes, can be lifted out of a
// parent.
export function canLift(doc, from, to) {
  return !!findLiftable(doc.resolve(from), doc.resolve(to == null ? from : to))
}

function rangeDepth($from, $to) {
  let shared = $from.sameDepth($to)
  if ($from.node(shared).isTextblock || $from.pos == $to.pos) --shared
  if (shared < 0 || $from.pos > $to.pos) return null
  return shared
}

function canCut(node, start, end) {
  return (start == 0 || node.canReplace(start, node.childCount)) &&
    (end == node.childCount || node.canReplace(0, start))
}

function findLiftable($from, $to) {
  let shared = rangeDepth($from, $to)
  if (!shared) return null
  let parent = $from.node(shared), content = parent.content.cutByIndex($from.index(shared), $to.indexAfter(shared))
  for (let depth = shared;; --depth) {
    let node = $from.node(depth), index = $from.index(depth)
    if (depth < shared && node.canReplace(index, index + 1, content))
      return {depth, shared, unwrap: false}
    if (depth == 0 || !canCut(node, index, index + 1)) break
  }

  if (parent.isBlock) {
    let joined = Fragment.empty
    content.forEach(node => joined = joined.append(node.content))
    for (let depth = shared;; --depth) {
      let node = $from.node(depth), index = $from.index(depth)
      if (depth < shared && node.canReplace(index, index + 1, joined))
        return {depth, shared, unwrap: true}
      if (depth == 0 || !canCut(node, index, index + 1)) break
    }
  }
}

// :: (number, ?number, ?bool) → Transform
// Lift the nearest liftable ancestor of the [sibling
// range](#Node.siblingRange) of the given positions out of its parent
// (or do nothing if no such node exists). When `silent` is true, this
// won't raise an error when the lift is impossible.
Transform.prototype.lift = function(from, to = from, silent = false) {
  let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
  let liftable = findLiftable($from, $to)
  if (!liftable) {
    if (!silent) throw new RangeError("No valid lift target")
    return this
  }

  let {depth, shared, unwrap} = liftable

  let gapStart = $from.before(shared + 1), gapEnd = $to.after(shared + 1)
  let start = gapStart, end = gapEnd

  let before = Fragment.empty, beforeDepth = 0
  for (let d = shared, splitting = false; d > depth; d--)
    if (splitting || $from.index(d) > 0) {
      splitting = true
      before = Fragment.from($from.node(d).copy(before))
      beforeDepth++
    } else {
      start--
    }
  let after = Fragment.empty, afterDepth = 0
  for (let d = shared, splitting = false; d > depth; d--)
    if (splitting || $to.after(d + 1) < $to.end(d)) {
      splitting = true
      after = Fragment.from($to.node(d).copy(after))
      afterDepth++
    } else {
      end++
    }

  if (unwrap) {
    let joinPos = gapStart, parent = $from.node(shared)
    for (let i = $from.index(shared), e = $to.index(shared) + 1, first = true; i < e; i++, first = false) {
      if (!first) {
        this.join(joinPos)
        end -= 2
        gapEnd -= 2
      }
      joinPos += parent.child(i).nodeSize - (first ? 0 : 2)
    }
    ++gapStart
    --gapEnd
  }

  return this.step(new ReplaceAroundStep(start, end, gapStart, gapEnd,
                                         new Slice(before.append(after), beforeDepth, afterDepth),
                                         before.size - beforeDepth, true))
}

// :: (Node, number, ?number, NodeType, ?Object) → bool
// Determines whether the [sibling range](#Node.siblingRange) of the
// given positions can be wrapped in the given node type.
export function canWrap(doc, from, to, type, attrs) {
  return !!checkWrap(doc.resolve(from), doc.resolve(to == null ? from : to), type, attrs)
}

function checkWrap($from, $to, type, attrs) {
  let shared = rangeDepth($from, $to)
  if (shared == null) return null
  let parent = $from.node(shared), parentFrom = $from.index(shared), parentTo = $to.indexAfter(shared)
  let around = parent.contentMatchAt(parentFrom).findWrapping(type, attrs)
  if (!around) return null
  if (!parent.canReplaceWith(parentFrom, parentTo, around.length ? around[0].type : type,
                             around.length ? around[0].attrs : attrs)) return null
  let inner = parent.child(parentFrom)
  let inside = type.contentExpr.start(attrs || type.defaultAttrs).findWrapping(inner.type, inner.attrs)
  if (!inside) return null
  let lastInside = inside[inside.length - 1]
  let innerMatch = (lastInside ? lastInside.type : type).contentExpr.start(lastInside ? lastInside.attrs : attrs)
  for (let i = parentFrom; i < parentTo; i++)
    if (!(innerMatch = innerMatch.matchNode(parent.child(i)))) return null
  return {shared, around, inside}
}

// :: (number, ?number, NodeType, ?Object) → Transform
// Wrap the [sibling range](#Node.siblingRange) of the given positions
// in a node of the given type, with the given attributes (if
// possible).
Transform.prototype.wrap = function(from, to = from, type, wrapAttrs) {
  let $from = this.doc.resolve(from), $to = this.doc.resolve(to)
  let check = checkWrap($from, $to, type, wrapAttrs)
  if (!check) throw new RangeError("Wrap not possible")
  let {shared, around, inside} = check

  let content = Fragment.empty, open = inside.length + 1 + around.length
  for (let i = inside.length - 1; i >= 0; i--) content = Fragment.from(inside[i].type.create(inside[i].attrs, content))
  content = Fragment.from(type.create(wrapAttrs, content))
  for (let i = around.length - 1; i >= 0; i--) content = Fragment.from(around[i].type.create(around[i].attrs, content))

  let start = $from.before(shared + 1), end = $to.after(shared + 1)
  this.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), open, true))

  if (inside.length) {
    let splitPos = start + open, parent = $from.node(shared)
    for (let i = $from.index(shared), e = $to.index(shared) + 1, first = true; i < e; i++, first = false) {
      if (!first) this.split(splitPos, inside.length)
      splitPos += parent.child(i).nodeSize + (first ? 0 : 2 * inside.length)
    }
  }
  return this
}

// :: (number, ?number, NodeType, ?Object) → Transform
// Set the type of all textblocks (partly) between `from` and `to` to
// the given node type with the given attributes.
Transform.prototype.setBlockType = function(from, to = from, type, attrs) {
  if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock")
  let mapFrom = this.steps.length
  this.doc.nodesBetween(from, to, (node, pos) => {
    if (node.isTextblock && !node.hasMarkup(type, attrs)) {
      // Ensure all markup that isn't allowed in the new node type is cleared
      this.clearMarkupFor(this.map(pos, 1, mapFrom), type, attrs)
      let startM = this.map(pos, 1, mapFrom), endM = this.map(pos + node.nodeSize, 1, mapFrom)
      this.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1,
                                      new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
      return false
    }
  })
  return this
}

// :: (number, ?NodeType, ?Object) → Transform
// Change the type and attributes of the node after `pos`.
Transform.prototype.setNodeType = function(pos, type, attrs) {
  let node = this.doc.nodeAt(pos)
  if (!node) throw new RangeError("No node at given position")
  if (!type) type = node.type
  if (node.type.isLeaf)
    return this.replaceWith(pos, pos + node.nodeSize, type.create(attrs, null, node.marks))

  if (!type.validContent(node.content, attrs))
    throw new RangeError("Invalid content for node type " + type.name)

  return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
                                         new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true))
}

// :: (Node, number, ?NodeType, ?Object) → bool
// Check whether splitting at the given position is allowed.
export function canSplit(doc, pos, depth = 1, typeAfter, attrsAfter) {
  let $pos = doc.resolve(pos), base = $pos.depth - depth
  if (base < 0 ||
      !$pos.parent.canReplace($pos.index(), $pos.parent.childCount) ||
      !$pos.parent.canReplace(0, $pos.indexAfter()))
    return false
  for (let d = $pos.depth - 1; d > base; d--) {
    let node = $pos.node(d), index = $pos.index(d)
    if (!node.canReplace(0, index) ||
        !node.canReplaceWith(index, node.childCount, typeAfter || $pos.node(d + 1).type,
                             typeAfter ? attrsAfter : $pos.node(d + 1).attrs))
      return false
    typeAfter = null
  }
  let index = $pos.indexAfter(base)
  return $pos.node(base).canReplaceWith(index, index, typeAfter || $pos.node(base + 1).type,
                                        typeAfter ? attrsAfter : $pos.node(base + 1).attrs)
}

// :: (number, ?number, ?NodeType, ?Object) → Transform
// Split the node at the given position, and optionally, if `depth` is
// greater than one, any number of nodes above that. By default, the part
// split off will inherit the node type of the original node. This can
// be changed by passing `typeAfter` and `attrsAfter`.
Transform.prototype.split = function(pos, depth = 1, typeAfter, attrsAfter) {
  let $pos = this.doc.resolve(pos), before = Fragment.empty, after = Fragment.empty
  for (let d = $pos.depth, e = $pos.depth - depth; d > e; d--) {
    before = Fragment.from($pos.node(d).copy(before))
    after = Fragment.from(typeAfter ? typeAfter.create(attrsAfter, after) : $pos.node(d).copy(after))
    typeAfter = null
  }
  return this.step(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth, true)))
}

// :: (Node, number) → bool
// Test whether the blocks before and after a given position can be
// joined.
export function joinable(doc, pos) {
  let $pos = doc.resolve(pos), index = $pos.index()
  return canJoin($pos.nodeBefore, $pos.nodeAfter) &&
    $pos.parent.canReplace(index, index + 1)
}

function canJoin(a, b) {
  return a && b && !a.isText && a.canAppend(b)
}

// :: (Node, number, ?number) → ?number
// Find an ancestor of the given position that can be joined to the
// block before (or after if `dir` is positive). Returns the joinable
// point, if any.
export function joinPoint(doc, pos, dir = -1) {
  let $pos = doc.resolve(pos)
  for (let d = $pos.depth;; d--) {
    let before, after
    if (d == $pos.depth) {
      before = $pos.nodeBefore
      after = $pos.nodeAfter
    } else if (dir > 0) {
      before = $pos.node(d + 1)
      after = $pos.node(d).maybeChild($pos.index(d) + 1)
    } else {
      before = $pos.node(d).maybeChild($pos.index(d) - 1)
      after = $pos.node(d + 1)
    }
    if (before && !before.isTextblock && canJoin(before, after)) return pos
    if (d == 0) break
    pos = dir < 0 ? $pos.before(d) : $pos.after(d)
  }
}

// :: (number, ?number, ?bool) → Transform
// Join the blocks around the given position. When `silent` is true,
// the method will return without raising an error if the position
// isn't a valid place to join.
Transform.prototype.join = function(pos, depth = 1, silent = false) {
  if (silent && (pos < depth || pos + depth > this.doc.content.size)) return this
  let step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true)
  if (silent) this.maybeStep(step)
  else this.step(step)
  return this
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var inherits = require('util').inherits;

var TChannelPeersBase = require('./peers_base.js');
var PeerHeap = require('./peer_heap.js');

function TChannelSubPeers(channel, options) {
    var self = this;
    TChannelPeersBase.call(self, channel, options);

    self.peerScoreThreshold = self.options.peerScoreThreshold || 0;
    self._heap = new PeerHeap(channel.random);
    self.choosePeerWithHeap = channel.choosePeerWithHeap;
}

inherits(TChannelSubPeers, TChannelPeersBase);

TChannelSubPeers.prototype.close = function close(callback) {
    var self = this;

    var peers = self.values();
    TChannelPeersBase.prototype.close.call(self, peers, callback);
};

TChannelSubPeers.prototype.add = function add(hostPort, options) {
    /* eslint max-statements: [2, 25]*/
    var self = this;

    var peer = self._map[hostPort];
    if (peer) {
        return peer;
    }

    var topChannel = self.channel.topChannel;

    if (hostPort === topChannel.hostPort) {
        return topChannel.peers.getSelfPeer();
    }

    peer = topChannel.peers.add(hostPort, options);
    peer.setPreferConnectionDirection(self.preferConnectionDirection);

    self._map[hostPort] = peer;
    self._keys.push(hostPort);

    var el = self._heap.add(peer);
    peer.heapElements.push(el);

    return peer;
};

TChannelSubPeers.prototype.clear = function clear() {
    var self = this;

    self._map = Object.create(null);
    self._keys = [];
    self._heap.clear();
};

TChannelSubPeers.prototype._delete = function _del(peer) {
    var self = this;

    var index = self._keys.indexOf(peer.hostPort);
    if (index === -1) {
        return;
    }

    delete self._map[peer.hostPort];
    popout(self._keys, index);

    for (var i = 0; i < peer.heapElements.length; i++) {
        var el = peer.heapElements[i];
        if (el.heap === self._heap) {
            el.heap.remove(el.index);
            popout(peer.heapElements, i);
            break;
        }
    }
};

TChannelSubPeers.prototype.setChoosePeerWithHeap = function setChoosePeerWithHeap(enabled) {
    var self = this;

    self.choosePeerWithHeap = enabled;
};

TChannelSubPeers.prototype.choosePeer = function choosePeer(req) {
    var self = this;

    if (self.choosePeerWithHeap) {
        return self.chooseHeapPeer(req);
    }

    return self.chooseLinearPeer(req);
};

TChannelSubPeers.prototype.chooseLinearPeer = function chooseLinearPeer(req) {
    /* eslint complexity: [2, 15]*/
    var self = this;

    var hosts = self._keys;
    if (!hosts || !hosts.length) {
        return null;
    }

    var threshold = self.peerScoreThreshold;

    var selectedPeer = null;
    var selectedScore = 0;
    for (var i = 0; i < hosts.length; i++) {
        var hostPort = hosts[i];
        var peer = self._map[hostPort];
        if (!req || !req.triedRemoteAddrs || !req.triedRemoteAddrs[hostPort]) {
            var score = peer.getScore(req);

            if (self.channel.topChannel.peerScoredEvent) {
                self.channel.topChannel.peerScoredEvent.emit(peer, {
                    peer: peer,
                    reason: 'chooseLinearPeer',
                    score: score
                });
            }

            var want = score > threshold &&
                       (selectedPeer === null || score > selectedScore);
            if (want) {
                selectedPeer = peer;
                selectedScore = score;
            }
        }
    }

    if (self.channel.topChannel.peerChosenEvent) {
        self.channel.topChannel.peerChosenEvent.emit(self, {
            mode: 'linear',
            peer: selectedPeer
        });
    }

    return selectedPeer;
};

TChannelSubPeers.prototype.chooseHeapPeer = function chooseHeapPeer(req) {
    var self = this;

    var peer;
    if (req && req.triedRemoteAddrs) {
        peer = self._choosePeerSkipTried(req);
    } else {
        peer = self._heap.choose(self.peerScoreThreshold);
    }

    if (self.channel.topChannel.peerChosenEvent) {
        self.channel.topChannel.peerChosenEvent.emit(self, {
            mode: 'heap',
            peer: peer
        });
    }

    return peer;
};

TChannelSubPeers.prototype._choosePeerSkipTried =
function _choosePeerSkipTried(req) {
    var self = this;

    return self._heap.choose(self.peerScoreThreshold, filterTriedPeers);

    function filterTriedPeers(peer) {
        return !req.triedRemoteAddrs[peer.hostPort];
    }
};

function popout(array, i) {
    if (!array.length) {
        return;
    }

    var j = array.length - 1;
    if (i !== j) {
        var tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
    array.pop();
}

module.exports = TChannelSubPeers;
<commit_msg>TChannelSubPeers: drop .self access pattern from constructor<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var inherits = require('util').inherits;

var TChannelPeersBase = require('./peers_base.js');
var PeerHeap = require('./peer_heap.js');

function TChannelSubPeers(channel, options) {
    TChannelPeersBase.call(this, channel, options);
    this.peerScoreThreshold = this.options.peerScoreThreshold || 0;
    this._heap = new PeerHeap(channel.random);
    this.choosePeerWithHeap = channel.choosePeerWithHeap;
}

inherits(TChannelSubPeers, TChannelPeersBase);

TChannelSubPeers.prototype.close = function close(callback) {
    var self = this;

    var peers = self.values();
    TChannelPeersBase.prototype.close.call(self, peers, callback);
};

TChannelSubPeers.prototype.add = function add(hostPort, options) {
    /* eslint max-statements: [2, 25]*/
    var self = this;

    var peer = self._map[hostPort];
    if (peer) {
        return peer;
    }

    var topChannel = self.channel.topChannel;

    if (hostPort === topChannel.hostPort) {
        return topChannel.peers.getSelfPeer();
    }

    peer = topChannel.peers.add(hostPort, options);
    peer.setPreferConnectionDirection(self.preferConnectionDirection);

    self._map[hostPort] = peer;
    self._keys.push(hostPort);

    var el = self._heap.add(peer);
    peer.heapElements.push(el);

    return peer;
};

TChannelSubPeers.prototype.clear = function clear() {
    var self = this;

    self._map = Object.create(null);
    self._keys = [];
    self._heap.clear();
};

TChannelSubPeers.prototype._delete = function _del(peer) {
    var self = this;

    var index = self._keys.indexOf(peer.hostPort);
    if (index === -1) {
        return;
    }

    delete self._map[peer.hostPort];
    popout(self._keys, index);

    for (var i = 0; i < peer.heapElements.length; i++) {
        var el = peer.heapElements[i];
        if (el.heap === self._heap) {
            el.heap.remove(el.index);
            popout(peer.heapElements, i);
            break;
        }
    }
};

TChannelSubPeers.prototype.setChoosePeerWithHeap = function setChoosePeerWithHeap(enabled) {
    var self = this;

    self.choosePeerWithHeap = enabled;
};

TChannelSubPeers.prototype.choosePeer = function choosePeer(req) {
    var self = this;

    if (self.choosePeerWithHeap) {
        return self.chooseHeapPeer(req);
    }

    return self.chooseLinearPeer(req);
};

TChannelSubPeers.prototype.chooseLinearPeer = function chooseLinearPeer(req) {
    /* eslint complexity: [2, 15]*/
    var self = this;

    var hosts = self._keys;
    if (!hosts || !hosts.length) {
        return null;
    }

    var threshold = self.peerScoreThreshold;

    var selectedPeer = null;
    var selectedScore = 0;
    for (var i = 0; i < hosts.length; i++) {
        var hostPort = hosts[i];
        var peer = self._map[hostPort];
        if (!req || !req.triedRemoteAddrs || !req.triedRemoteAddrs[hostPort]) {
            var score = peer.getScore(req);

            if (self.channel.topChannel.peerScoredEvent) {
                self.channel.topChannel.peerScoredEvent.emit(peer, {
                    peer: peer,
                    reason: 'chooseLinearPeer',
                    score: score
                });
            }

            var want = score > threshold &&
                       (selectedPeer === null || score > selectedScore);
            if (want) {
                selectedPeer = peer;
                selectedScore = score;
            }
        }
    }

    if (self.channel.topChannel.peerChosenEvent) {
        self.channel.topChannel.peerChosenEvent.emit(self, {
            mode: 'linear',
            peer: selectedPeer
        });
    }

    return selectedPeer;
};

TChannelSubPeers.prototype.chooseHeapPeer = function chooseHeapPeer(req) {
    var self = this;

    var peer;
    if (req && req.triedRemoteAddrs) {
        peer = self._choosePeerSkipTried(req);
    } else {
        peer = self._heap.choose(self.peerScoreThreshold);
    }

    if (self.channel.topChannel.peerChosenEvent) {
        self.channel.topChannel.peerChosenEvent.emit(self, {
            mode: 'heap',
            peer: peer
        });
    }

    return peer;
};

TChannelSubPeers.prototype._choosePeerSkipTried =
function _choosePeerSkipTried(req) {
    var self = this;

    return self._heap.choose(self.peerScoreThreshold, filterTriedPeers);

    function filterTriedPeers(peer) {
        return !req.triedRemoteAddrs[peer.hostPort];
    }
};

function popout(array, i) {
    if (!array.length) {
        return;
    }

    var j = array.length - 1;
    if (i !== j) {
        var tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
    array.pop();
}

module.exports = TChannelSubPeers;
<|endoftext|>


<commit_before>// FLUSHALL Redis before starting

var   assert        = require('../support/assert')
    , tests         = module.exports = {}
    , _             = require('underscore')
    , querystring   = require('querystring')
    , fs            = require('fs')
    , redis         = require('redis')
    , th            = require('../support/test_helper')
    , Step          = require('step')
    , mapnik        = require('mapnik')
    , Windshaft     = require('../../lib/windshaft')
    , ServerOptions = require('../support/server_options')
    , http          = require('http');

suite('attributes', function() {

    ////////////////////////////////////////////////////////////////////
    //
    // SETUP
    //
    ////////////////////////////////////////////////////////////////////

    var server = new Windshaft.Server(ServerOptions);
    server.setMaxListeners(0);
    var redis_client = redis.createClient(ServerOptions.redis.port);

    checkCORSHeaders = function(res) {
      var h = res.headers['access-control-allow-headers'];
      assert.ok(h);
      assert.equal(h, 'X-Requested-With, X-Prototype-Version, X-CSRF-Token');
      var h = res.headers['access-control-allow-origin'];
      assert.ok(h);
      assert.equal(h, '*');
    };

    suiteSetup(function(done) {

      // Check that we start with an empty redis db
      redis_client.keys("*", function(err, matches) {
          if ( err ) { done(err); return; }
          assert.equal(matches.length, 0, "redis keys present at setup time:\n" + matches.join("\n"));
          done();
      });

    });

    test("can only be fetched from layer having an attributes spec",
    function(done) {

      var mapconfig =  {
        version: '1.1.0',
        layers: [
           { type: 'mapnik', options: {
               sql: "select 1 as id, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
               cartocss: '#style { }',
               cartocss_version: '2.0.1'
             } },
           { type: 'mapnik', options: {
               sql: "select 1 as i, 6 as n, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
               attributes: { id:'i', columns: ['n'] },
               cartocss: '#style { }',
               cartocss_version: '2.0.1'
             } }
        ]
      };
      var expected_token;
      Step(
        function do_post()
        {
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup',
              method: 'POST',
              headers: {'Content-Type': 'application/json' },
              data: JSON.stringify(mapconfig)
          }, {}, function(res, err) { next(err, res); });
        },
        function checkPost(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          // CORS headers should be sent with response
          // from layergroup creation via POST
          checkCORSHeaders(res);
          var parsedBody = JSON.parse(res.body);
          if ( expected_token ) assert.deepEqual(parsedBody, {layergroupid: expected_token, layercount: 2});
          else expected_token = parsedBody.layergroupid;
          return null;
        },
        function do_get_attr_0(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token + '/0/attributes/1',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_error_0(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 400, res.statusCode + ( res.statusCode != 200 ? (': ' + res.body) : '' ));
          var parsed = JSON.parse(res.body);
          assert.equal(parsed.error, "Layer 0 has no exposed attributes");
          return null;
        },
        function do_get_attr_1(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token + '/1/attributes/1',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_attr_1(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          var parsed = JSON.parse(res.body);
          assert.deepEqual(parsed, {"n":6});
          return null;
        },
        function finish(err) {
          var errors = [];
          if ( err ) errors.push(''+err);
          redis_client.exists("map_cfg|" +  expected_token, function(err, exists) {
              if ( err ) errors.push(err.message);
              //assert.ok(exists, "Missing expected token " + expected_token + " from redis");
              redis_client.del("map_cfg|" +  expected_token, function(err) {
                if ( err ) errors.push(err.message);
                if ( errors.length ) done(new Error(errors));
                else done(null);
              });
          });
        }
      );
    });

    ////////////////////////////////////////////////////////////////////
    //
    // TEARDOWN
    //
    ////////////////////////////////////////////////////////////////////

    suiteTeardown(function(done) {

      // Check that we left the redis db empty
      redis_client.keys("*", function(err, matches) {
          try {
            assert.equal(matches.length, 0, "Left over redis keys:\n" + matches.join("\n"));
          } catch (err2) {
            if ( err ) err.message += '\n' + err2.message;
            else err = err2;
          }
          redis_client.flushall(function() {
            done(err);
          });
      });

    });

});

<commit_msg>Add test for fetching attributes with jsonp<commit_after>// FLUSHALL Redis before starting

var   assert        = require('../support/assert')
    , tests         = module.exports = {}
    , _             = require('underscore')
    , querystring   = require('querystring')
    , fs            = require('fs')
    , redis         = require('redis')
    , th            = require('../support/test_helper')
    , Step          = require('step')
    , mapnik        = require('mapnik')
    , Windshaft     = require('../../lib/windshaft')
    , ServerOptions = require('../support/server_options')
    , http          = require('http');

suite('attributes', function() {

    ////////////////////////////////////////////////////////////////////
    //
    // SETUP
    //
    ////////////////////////////////////////////////////////////////////

    var server = new Windshaft.Server(ServerOptions);
    server.setMaxListeners(0);
    var redis_client = redis.createClient(ServerOptions.redis.port);

    var test_mapconfig_1 =  {
      version: '1.1.0',
      layers: [
         { type: 'mapnik', options: {
             sql: "select 1 as id, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
             cartocss: '#style { }',
             cartocss_version: '2.0.1'
           } },
         { type: 'mapnik', options: {
             sql: "select 1 as i, 6 as n, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
             attributes: { id:'i', columns: ['n'] },
             cartocss: '#style { }',
             cartocss_version: '2.0.1'
           } }
      ]
    };

    checkCORSHeaders = function(res) {
      var h = res.headers['access-control-allow-headers'];
      assert.ok(h);
      assert.equal(h, 'X-Requested-With, X-Prototype-Version, X-CSRF-Token');
      var h = res.headers['access-control-allow-origin'];
      assert.ok(h);
      assert.equal(h, '*');
    };

    suiteSetup(function(done) {

      // Check that we start with an empty redis db
      redis_client.keys("*", function(err, matches) {
          if ( err ) { done(err); return; }
          assert.equal(matches.length, 0, "redis keys present at setup time:\n" + matches.join("\n"));
          done();
      });

    });

    test("can only be fetched from layer having an attributes spec",
    function(done) {

      var expected_token;
      Step(
        function do_post()
        {
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup',
              method: 'POST',
              headers: {'Content-Type': 'application/json' },
              data: JSON.stringify(test_mapconfig_1)
          }, {}, function(res, err) { next(err, res); });
        },
        function checkPost(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          // CORS headers should be sent with response
          // from layergroup creation via POST
          checkCORSHeaders(res);
          var parsedBody = JSON.parse(res.body);
          if ( expected_token ) assert.deepEqual(parsedBody, {layergroupid: expected_token, layercount: 2});
          else expected_token = parsedBody.layergroupid;
          return null;
        },
        function do_get_attr_0(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token + '/0/attributes/1',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_error_0(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 400, res.statusCode + ( res.statusCode != 200 ? (': ' + res.body) : '' ));
          var parsed = JSON.parse(res.body);
          assert.equal(parsed.error, "Layer 0 has no exposed attributes");
          return null;
        },
        function do_get_attr_1(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token + '/1/attributes/1',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_attr_1(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          var parsed = JSON.parse(res.body);
          assert.deepEqual(parsed, {"n":6});
          return null;
        },
        function finish(err) {
          var errors = [];
          if ( err ) errors.push(''+err);
          redis_client.exists("map_cfg|" +  expected_token, function(err, exists) {
              if ( err ) errors.push(err.message);
              //assert.ok(exists, "Missing expected token " + expected_token + " from redis");
              redis_client.del("map_cfg|" +  expected_token, function(err) {
                if ( err ) errors.push(err.message);
                if ( errors.length ) done(new Error(errors));
                else done(null);
              });
          });
        }
      );
    });

    test("can be used with jsonp", function(done) {

      var expected_token;
      Step(
        function do_post()
        {
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup',
              method: 'POST',
              headers: {'Content-Type': 'application/json' },
              data: JSON.stringify(test_mapconfig_1)
          }, {}, function(res, err) { next(err, res); });
        },
        function checkPost(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          // CORS headers should be sent with response
          // from layergroup creation via POST
          checkCORSHeaders(res);
          var parsedBody = JSON.parse(res.body);
          if ( expected_token ) assert.deepEqual(parsedBody, {layergroupid: expected_token, layercount: 2});
          else expected_token = parsedBody.layergroupid;
          return null;
        },
        function do_get_attr_0(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token +
                   '/0/attributes/1?callback=test',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_error_0(err, res) {
          if ( err ) throw err;
          // jsonp errors should be returned with HTTP status 200
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          assert.equal(res.body, 'test({"error":"Layer 0 has no exposed attributes"});');
          return null;
        },
        function do_get_attr_1(err)
        {
          if ( err ) throw err;
          var next = this;
          assert.response(server, {
              url: '/database/windshaft_test/layergroup/' + expected_token + '/1/attributes/1',
              method: 'GET'
          }, {}, function(res, err) { next(err, res); });
        },
        function check_attr_1(err, res) {
          if ( err ) throw err;
          assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
          var parsed = JSON.parse(res.body);
          assert.deepEqual(parsed, {"n":6});
          return null;
        },
        function finish(err) {
          var errors = [];
          if ( err ) errors.push(''+err);
          redis_client.exists("map_cfg|" +  expected_token, function(err, exists) {
              if ( err ) errors.push(err.message);
              //assert.ok(exists, "Missing expected token " + expected_token + " from redis");
              redis_client.del("map_cfg|" +  expected_token, function(err) {
                if ( err ) errors.push(err.message);
                if ( errors.length ) done(new Error(errors));
                else done(null);
              });
          });
        }
      );
    });

    ////////////////////////////////////////////////////////////////////
    //
    // TEARDOWN
    //
    ////////////////////////////////////////////////////////////////////

    suiteTeardown(function(done) {

      // Check that we left the redis db empty
      redis_client.keys("*", function(err, matches) {
          try {
            assert.equal(matches.length, 0, "Left over redis keys:\n" + matches.join("\n"));
          } catch (err2) {
            if ( err ) err.message += '\n' + err2.message;
            else err = err2;
          }
          redis_client.flushall(function() {
            done(err);
          });
      });

    });

});

<|endoftext|>


<commit_before>/*
 * Copyright 2019 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

const options = {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
};
const path = require('path');
const fs = require('fs');
const assert = require('assert');
const _ = require('lodash');
const anyGrpc = require('../any_grpc');
const clientGrpc = anyGrpc.client;
const serverGrpc = anyGrpc.server;
const protoLoader = require('../../packages/proto-loader', options);
const testServiceDef = protoLoader.loadSync(__dirname + '/../proto/test_service.proto');
const TestService = serverGrpc.loadPackageDefinition(testServiceDef).TestService.service;
const TestServiceClient = clientGrpc.loadPackageDefinition(testServiceDef).TestService;

const clientCreds = clientGrpc.credentials.createInsecure();
const serverCreds = serverGrpc.ServerCredentials.createInsecure();

const serviceImpl = {
  unary: function(call, cb) {
    cb(null, {});
  },
  clientStream: function(stream, cb){
    stream.on('data', function(data) {});
    stream.on('end', function() {
      cb(null, {});
    });
  },
  serverStream: function(stream) {
    stream.end();
  },
  bidiStream: function(stream) {
    stream.on('data', function(data) {});
    stream.on('end', function() {
      stream.end();
    });
  }
};

describe('Reconnection', function() {
  let server1;
  let server2;
  let port;
  before(function(done) {
    server1 = new serverGrpc.Server();
    server1.addService(TestService, serviceImpl);
    server2 = new serverGrpc.Server();
    server2.addService(TestService, serviceImpl);
    server1.bindAsync('localhost:0', serverCreds, (err, _port) => {
      assert.ifError(err);
      server1.start();
      port = _port;
      client = new TestServiceClient(`localhost:${port}`, clientCreds);
      done();
    });
  });
  after(function() {
    server1.forceShutdown();
    server2.forceShutdown();
  });
  it('Should end with either OK or UNAVAILABLE when querying a server that is shutting down', function(done) {
    let pendingCalls = 0;
    let testDone = false;
    let callInterval;
    function maybeDone() {
      if (testDone && pendingCalls === 0) {
        done();
      }
    };
    client.unary({}, (err, data) => {
      assert.ifError(err);
      server1.tryShutdown(() => {
        server2.bindAsync(`localhost:${port}`, serverCreds, (err) => {
          assert.ifError(err);
          server2.start();
          client.unary({}, (err, data) => {
            assert.ifError(err);
            clearInterval(callInterval);
            testDone = true;
            maybeDone();
          });
        });
      });
      callInterval = setInterval(() => {
        pendingCalls += 1;
        client.unary({}, (err, data) => {
          pendingCalls -= 1;
          if (err) {
            assert.strictEqual(err.code, clientGrpc.status.UNAVAILABLE);
          }
          maybeDone();
        });
      }, 0);
    });
  });
});
<commit_msg>test: make connctivity test more robust<commit_after>/*
 * Copyright 2019 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

const options = {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
};
const path = require('path');
const fs = require('fs');
const assert = require('assert');
const _ = require('lodash');
const anyGrpc = require('../any_grpc');
const clientGrpc = anyGrpc.client;
const serverGrpc = anyGrpc.server;
const protoLoader = require('../../packages/proto-loader', options);
const testServiceDef = protoLoader.loadSync(__dirname + '/../proto/test_service.proto');
const TestService = serverGrpc.loadPackageDefinition(testServiceDef).TestService.service;
const TestServiceClient = clientGrpc.loadPackageDefinition(testServiceDef).TestService;

const clientCreds = clientGrpc.credentials.createInsecure();
const serverCreds = serverGrpc.ServerCredentials.createInsecure();

const serviceImpl = {
  unary: function(call, cb) {
    cb(null, {});
  },
  clientStream: function(stream, cb){
    stream.on('data', function(data) {});
    stream.on('end', function() {
      cb(null, {});
    });
  },
  serverStream: function(stream) {
    stream.end();
  },
  bidiStream: function(stream) {
    stream.on('data', function(data) {});
    stream.on('end', function() {
      stream.end();
    });
  }
};

describe('Reconnection', function() {
  let server1;
  let server2;
  let port;
  before(function(done) {
    server1 = new serverGrpc.Server();
    server1.addService(TestService, serviceImpl);
    server2 = new serverGrpc.Server();
    server2.addService(TestService, serviceImpl);
    server1.bindAsync('localhost:0', serverCreds, (err, _port) => {
      assert.ifError(err);
      server1.start();
      port = _port;
      client = new TestServiceClient(`localhost:${port}`, clientCreds);
      done();
    });
  });
  after(function() {
    client.close();
    server1.forceShutdown();
    server2.forceShutdown();
  });
  it('Should end with either OK or UNAVAILABLE when querying a server that is shutting down', function(done) {
    this.timeout(10000);
    let pendingCalls = 0;
    let testDone = false;
    let callInterval;
    function maybeDone() {
      if (testDone && pendingCalls === 0) {
        done();
      }
    };
    client.unary({}, (err, data) => {
      assert.ifError(err);
      server1.tryShutdown(() => {
        server2.bindAsync(`localhost:${port}`, serverCreds, (err) => {
          assert.ifError(err);
          server2.start();
          const metadata = new clientGrpc.Metadata({ waitForReady: true });
          client.unary({}, metadata, (err, data) => {
            assert.ifError(err);
            clearInterval(callInterval);
            testDone = true;
            maybeDone();
          });
        });
      });
      callInterval = setInterval(() => {
        assert.strictEqual(testDone, false);
        pendingCalls += 1;
        client.unary({}, (err, data) => {
          pendingCalls -= 1;
          if (err) {
            assert.strictEqual(err.code, clientGrpc.status.UNAVAILABLE);
          }
          maybeDone();
        });
      }, 0);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
let userUtils = require('./../../dev-tools/users-utils.js');
let User = userUtils.User;

describe('Simulator abstraction layer features', function () {
	describe('Battle', function () {
		describe('player identifiers', function () {
			let p1, p2, room;
			afterEach(function () {
				if (p1) {
					p1.disconnectAll();
					p1.destroy();
				}
				if (p2) {
					p2.disconnectAll();
					p2.destroy();
				}
				if (room) room.expire();
			});

			it('should not get players out of sync in rated battles on rename', function () {
				// Regression test for 47263c8749
				// Since updated to new API used by room battles.
				let packedTeam = 'Weavile||lifeorb||swordsdance,knockoff,iceshard,iciclecrash|Jolly|,252,,,4,252|||||';
				p1 = new User();
				p2 = new User();
				p1.forceRename("Missingno."); // Don't do this at home
				room = Rooms.global.startBattle(p1, p2, 'customgame', packedTeam, packedTeam, {rated: true});
				if (!room.active) {
					room.joinBattle(p1, packedTeam);
					room.joinBattle(p2, packedTeam);
				}
				p1.resetName();
				for (let i = 0; i < room.battle.playerNames.length; i++) {
					let playerName = room.battle.playerNames[i];
					let playerData = room.battle['p' + (i + 1)];
					assert.strictEqual(playerData.name, playerName);
					assert.strictEqual(playerData, room.battle.players[toId(playerName)]);
				}
			});
		});
	});
});
<commit_msg>Test: make player name sync regression test more robust<commit_after>'use strict';

const assert = require('assert');
let userUtils = require('./../../dev-tools/users-utils.js');
let User = userUtils.User;

describe('Simulator abstraction layer features', function () {
	describe('Battle', function () {
		describe('player identifiers', function () {
			let p1, p2, room;
			afterEach(function () {
				if (p1) {
					p1.disconnectAll();
					p1.destroy();
				}
				if (p2) {
					p2.disconnectAll();
					p2.destroy();
				}
				if (room) room.expire();
			});

			it('should not get players out of sync in rated battles on rename', function () {
				// Regression test for 47263c8749
				// Since updated to new API used by room battles.
				let packedTeam = 'Weavile||lifeorb||swordsdance,knockoff,iceshard,iciclecrash|Jolly|,252,,,4,252|||||';
				p1 = new User();
				p2 = new User();
				p1.forceRename("Missingno."); // Don't do this at home
				room = Rooms.global.startBattle(p1, p2, '', packedTeam, packedTeam, {rated: true});
				if (!room.active) {
					room.joinBattle(p1, packedTeam);
					room.joinBattle(p2, packedTeam);
				}
				p1.resetName();
				for (let i = 0; i < room.battle.playerNames.length; i++) {
					let playerName = room.battle.playerNames[i];
					let playerData = room.battle['p' + (i + 1)];
					assert.strictEqual(playerData.name, playerName);
					assert.strictEqual(playerData, room.battle.players[toId(playerName)]);
				}
			});
		});
	});
});
<|endoftext|>


<commit_before>import React from "react";
import ReactDOM from "react-dom";
import TestUtils from "react-addons-test-utils";
let shallowRenderer = TestUtils.createRenderer();
import InfinityMenu from "../src/infinity-menu";
import sinon from "sinon";
import should from "should";
import "should-sinon";

describe("Custom Component prop", function() {
	let component;
	let dom;

	before(function() {
		const CustomComponent = () => {
			return

;
		};
		const tree = [
			{
				name: "menu1",
				id: 1,
				isOpen: true,
				customComponent: CustomComponent,
				children: [
					{
						name: "submenu1",
						id: 1,
						isOpen: true,
						children: [
							{
								name: "item1-1",
								id: 1
							},
							{
								name: "item1-2",
								id: 2
							}
						]
					}
				]
			}
		];
		component = <InfinityMenu tree={tree} />;
		dom = TestUtils.renderIntoDocument(component);
	});

	after(function() {
		ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(dom).parentNode);
	});

	it("should render the custom component", function () {
		should.doesNotThrow(() => {
			TestUtils.findRenderedDOMComponentWithClass(
				dom,
				"test-custom-component"
			);
		});
	});
});<commit_msg>Adding a test case for passing customComponent as string witih mappings<commit_after>import React from "react";
import ReactDOM from "react-dom";
import TestUtils from "react-addons-test-utils";
let shallowRenderer = TestUtils.createRenderer();
import InfinityMenu from "../src/infinity-menu";
import sinon from "sinon";
import should from "should";
import "should-sinon";

describe("Custom Component prop", function() {
	const CustomComponent = () => {
		return

;
	};
	let component;
	let dom;

	afterEach(function() {
		ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(dom).parentNode);
	});

	it("should render the custom component when given as a constant", function () {
		const tree = [
			{
				name: "menu1",
				id: 1,
				isOpen: true,
				customComponent: CustomComponent,
				children: [
					{
						name: "submenu1",
						id: 1,
						isOpen: true,
						children: [
							{
								name: "item1-1",
								id: 1
							},
							{
								name: "item1-2",
								id: 2
							}
						]
					}
				]
			}
		];
		component = <InfinityMenu tree={tree} />;
		dom = TestUtils.renderIntoDocument(component);
		should.doesNotThrow(() => {
			TestUtils.findRenderedDOMComponentWithClass(
				dom,
				"test-custom-component"
			);
		});
	});

	it("should render the custom component when given as a string with mappings", function () {
			const tree = [
				{
					name: "menu1",
					id: 1,
					isOpen: true,
					customComponent: 'CustomComponent',
					children: [
						{
							name: "submenu1",
							id: 1,
							isOpen: true,
							children: [
								{
									name: "item1-1",
									id: 1
								},
								{
									name: "item1-2",
									id: 2
								}
							]
						}
					]
				}
			];
			component = <InfinityMenu tree={tree} customComponentMappings={{ 'CustomComponent': CustomComponent }}/>;
			dom = TestUtils.renderIntoDocument(component);
			should.doesNotThrow(() => {
				TestUtils.findRenderedDOMComponentWithClass(
					dom,
					"test-custom-component"
				);
			});
		});
});
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var serialize = require('../../../../src/js/lib/serialize');

describe('serialize', function() {

  // @TODO Could really use more extensive tests here
  it('correctly serializes the data', function() {
    var data = {
      Null: null,
      Bool0: false,
      Bool1: true,
      Int: 257,
      Data: [1, 2, 3, 4],
      String: 'test'
    };
    var expected = [
      6, 'N', 'u', 'l', 'l', '\u0000', 0, 'B', 'o', 'o', 'l', '0', '\u0000', 1, 0,
      'B', 'o', 'o', 'l', '1', '\u0000', 1, 1, 'I', 'n', 't', '\u0000', 2, 1, 1, 0, 0,
      'D', 'a', 't', 'a', '\u0000', 3, 4, 0, 1, 2, 3, 4, 'S', 't', 'r', 'i', 'n',
      'g', '\u0000', 4, 't', 'e', 's', 't', '\u0000'
    ];

    assert.deepEqual(serialize(data), expected);
  });

  it('throws for objects with too many keys', function() {
    var data = {};
    for (var i = 0; i < 256; i++) {
      data['test' + i] = true;
    }

    assert.throws(function() { serialize(data); }, /255/);
  });
});
<commit_msg>Fix JS lint error<commit_after>'use strict';

var assert = require('assert');
var serialize = require('../../../../src/js/lib/serialize');

describe('serialize', function() {

  // @TODO Could really use more extensive tests here
  it('correctly serializes the data', function() {
    var data = {
      Null: null,
      Bool0: false,
      Bool1: true,
      Int: 257,
      Data: [1, 2, 3, 4],
      String: 'test'
    };
    var expected = [
      6, 'N', 'u', 'l', 'l', '\u0000', 0, 'B', 'o', 'o', 'l', '0', '\u0000', 1, 0,
      'B', 'o', 'o', 'l', '1', '\u0000', 1, 1, 'I', 'n', 't', '\u0000', 2, 1, 1, 0,
      0, 'D', 'a', 't', 'a', '\u0000', 3, 4, 0, 1, 2, 3, 4, 'S', 't', 'r', 'i',
      'n', 'g', '\u0000', 4, 't', 'e', 's', 't', '\u0000'
    ];

    assert.deepEqual(serialize(data), expected);
  });

  it('throws for objects with too many keys', function() {
    var data = {};
    for (var i = 0; i < 256; i++) {
      data['test' + i] = true;
    }

    assert.throws(function() { serialize(data); }, /255/);
  });
});
<|endoftext|>