<commit_before>$(function() {

    module("Basic");

    var CST_NB_OF_FRANCE_DPTMT = 96;
    var CST_MAP_MAX_WIDTH = 800;
    var CST_MAP_MAX_HEIGHT = 834.8948306319708; // Calculated

    test("Default instance creation", function(assert) {

        /* Create the basic map! */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* Some basic checks */
        assert.ok($(".mapcontainer .map svg")[0], "SVG map created" );
        assert.ok($(".mapcontainer .map svg path")[0], "Path in SVG map" );
        assert.equal($(".mapcontainer .map svg path").length, CST_NB_OF_FRANCE_DPTMT, "All France department drawn" );
        assert.equal($(".mapcontainer .map svg").attr("width"), CST_MAP_MAX_WIDTH, "Check map width size" );
        assert.equal($(".mapcontainer .map svg").attr("height"), CST_MAP_MAX_HEIGHT, "Check map height size" );
        assert.ok($(".mapcontainer").hasClass("mapael"), "Has mapael class" );
        assert.ok(typeof $(".mapcontainer").data("mapael") === "function", "Has mapael data" );
        assert.ok($(".mapcontainer .map .mapTooltip")[0], "Has tooltip div" );

    });

    test("Creation fail", function(assert) {

        /* Error if wrong map name */
        assert.throws(function(){
            $(".mapcontainer").mapael({
                map: { name: "not_existing_map" }
            });
        }, "Not existing map" );

        /* Create map, then create again!
        $(".mapcontainer").mapael({
            map: { name: "france_departments" }
        });
        assert.throws(function(){
            $(".mapcontainer").mapael({
                map: { name: "france_departments" }
            });
        }, "Already existing map" );
 */
    });

    test("Mouseover", function(assert) {
        var mouseover_async_done = assert.async(CST_NB_OF_FRANCE_DPTMT);

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* mouseover event check (background changement) */
        var default_fill = $(".mapcontainer svg path:first").attr("fill");
        $(".mapcontainer svg path").each(function(id, elem) {
            var $elem = $(elem);

            $elem.trigger("mouseover");
            setTimeout(function() {
                var new_fill = $elem.attr("fill");
                assert.notEqual(default_fill, new_fill, "Check new background" );
                assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden" );

                $elem.trigger("mouseout");
                setTimeout(function() {
                    var new_fill = $elem.attr("fill");
                    assert.equal(new_fill, default_fill, "Check old background" );
                    mouseover_async_done();
                }, 500);
            }, 500);
        });

    });

    test("Responsive", function(assert) {
        var responsive_async_done = assert.async();

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* Responsive checks */
        $(".mapcontainer").width(CST_MAP_MAX_WIDTH/2);
        $(window).trigger('resize');
        setTimeout(function() {
            var $svg = $(".mapcontainer .map svg");
            assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check new map width size" );
            assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check new map height size" );

            $(".mapcontainer").width(CST_MAP_MAX_WIDTH);
            $(window).trigger('resize');
            setTimeout(function() {
                var $svg = $(".mapcontainer .map svg");
                assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH, "Check old map width size" );
                assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT, "Check old map height size" );
                responsive_async_done();
            }, 500);
        }, 500);

    });

    module("Options");

    test("Force width", function(assert) {
        var responsive_async_done = assert.async();

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments",
                width:CST_MAP_MAX_WIDTH/2
            }
        });

        var $svg = $(".mapcontainer .map svg");

        assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check map fixed width size" );
        assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check map fixed height size" );

        /* Responsive checks */
        $(".mapcontainer").width(CST_MAP_MAX_WIDTH/4);
        $(window).trigger('resize');
        setTimeout(function() {
            var $svg = $(".mapcontainer .map svg");
            assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check fixed map width size after resize" );
            assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check fixed map height size after resize" );

            responsive_async_done();
        }, 500);

    });

    test("Default option override", function(assert) {
        var mouseover_async_done = assert.async(CST_NB_OF_FRANCE_DPTMT);

        var CST_DEFAULTAREA = {
            attrs: {
                fill: "#f4f4e8",
                stroke: "#ced8d0"
            }
            , attrsHover: {
                fill: "#a4e100",
                stroke: "#aaaaaa"
            }
            , text: {
                attrs: {
                    fill: "#505444"
                }
                , attrsHover: {
                    fill: "#000"
                }
            }
        };
        var CST_CUSTOMAREA = {
            "department-56": {
                text: {content: "TEXT_department-56", attrs: {"font-size": 10}},
                tooltip: {content: "TOOLTIP_department-56"}
            },
            "department-21": {
                attrs: {
                    fill: "#488402"
                }
                , attrsHover: {
                    fill: "#a4e100"
                }
            }
        };

        $(".mapcontainer").mapael({
            map: {
                name: "france_departments",
                defaultArea: CST_DEFAULTAREA
            },
            areas: CST_CUSTOMAREA
        });

        assert.ok($(".mapcontainer .map svg")[0], "Map created" );

        $(".mapcontainer svg path").each(function(id, elem) {
            var $elem = $(elem);
            var data_id = $elem.attr("data-id");

            if (data_id === "department-21") {
                assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrs.fill, "Check special overriden fill before mouseover for " + data_id);
            } else {
                assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrs.fill, "Check overriden fill before mouseover for " + data_id);
            }
            assert.equal($elem.attr("stroke"), CST_DEFAULTAREA.attrs.stroke, "Check overriden stroke before mouseover for " + data_id);

            $elem.trigger("mouseover");
            setTimeout(function() {

                if (data_id === "department-21") {
                    assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrsHover.fill, "Check special overriden hover fill after mouseover for " + data_id);
                } else {
                    assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrsHover.fill, "Check overriden hover fill after mouseover for " + data_id);
                }
                assert.equal($elem.attr("stroke"), CST_DEFAULTAREA.attrsHover.stroke, "Check overriden hover stroke after mouseover for " + data_id);

                if (data_id === "department-56") {
                    assert.ok($(".mapcontainer .map .mapTooltip").is( ":visible" ), "Check tooltip visible for " + data_id);
                    assert.equal($(".mapcontainer .map .mapTooltip").html(), CST_CUSTOMAREA[data_id].tooltip.content, "Check special tooltip content for " + data_id);
                    /* TODO : check text? */
                } else {
                    /* We can't test the following because it will fail for area around dptmt 56... */
                    //assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden for " + data_id);
                    //assert.equal($(".mapcontainer .map .mapTooltip").html(), "", "Check empty tooltip content for " + data_id);
                }

                $elem.trigger("mouseout");
                setTimeout(function() {
                    if (data_id === "department-21") {
                    assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrs.fill, "Check special overriden fill after mouseout for " + data_id);
                    } else {
                    assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrs.fill, "Check overriden fill after mouseout for " + data_id);
                    }

                    if (data_id === "department-56") {
                        assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden after mouseout for " + data_id);
                        // assert.equal($(".mapcontainer .map .mapTooltip").html(), "", "Check empty tooltip content after mouseout for " + data_id);
                    } else {
                        /* We can't test the following because it will fail for area around dptmt 56... */
                    }

                    mouseover_async_done();
                }, 500);
            }, 500);
        });
    });

});<commit_msg>Fix creation failed tests<commit_after>$(function() {

    module("Basic");

    var CST_NB_OF_FRANCE_DPTMT = 96;
    var CST_MAP_MAX_WIDTH = 800;
    var CST_MAP_MAX_HEIGHT = 834.8948306319708; // Calculated

    test("Default instance creation", function(assert) {

        /* Create the basic map! */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* Some basic checks */
        assert.ok($(".mapcontainer .map svg")[0], "SVG map created" );
        assert.ok($(".mapcontainer .map svg path")[0], "Path in SVG map" );
        assert.equal($(".mapcontainer .map svg path").length, CST_NB_OF_FRANCE_DPTMT, "All France department drawn" );
        assert.equal($(".mapcontainer .map svg").attr("width"), CST_MAP_MAX_WIDTH, "Check map width size" );
        assert.equal($(".mapcontainer .map svg").attr("height"), CST_MAP_MAX_HEIGHT, "Check map height size" );
        assert.ok($(".mapcontainer").hasClass("mapael"), "Has mapael class" );
        assert.ok(typeof $(".mapcontainer").data("mapael") === "function", "Has mapael data" );
        assert.ok($(".mapcontainer .map .mapTooltip")[0], "Has tooltip div" );

    });

    test("Creation fail: wrong map", function(assert) {

        /* Error if wrong map name */
        assert.throws(function(){
            $(".mapcontainer").mapael({
                map: { name: "not_existing_map" }
            });
        }, "Not existing map" );

    });

    test("Creation fail: existing map", function(assert) {
        $(".mapcontainer").mapael({
            map: { name: "france_departments" }
        });
        assert.throws(function(){
            $(".mapcontainer").mapael({
                map: { name: "france_departments" }
            });
        }, "Already existing map" );
    });

    test("Mouseover", function(assert) {
        var mouseover_async_done = assert.async(CST_NB_OF_FRANCE_DPTMT);

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* mouseover event check (background changement) */
        var default_fill = $(".mapcontainer svg path:first").attr("fill");
        $(".mapcontainer svg path").each(function(id, elem) {
            var $elem = $(elem);

            $elem.trigger("mouseover");
            setTimeout(function() {
                var new_fill = $elem.attr("fill");
                assert.notEqual(default_fill, new_fill, "Check new background" );
                assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden" );

                $elem.trigger("mouseout");
                setTimeout(function() {
                    var new_fill = $elem.attr("fill");
                    assert.equal(new_fill, default_fill, "Check old background" );
                    mouseover_async_done();
                }, 500);
            }, 500);
        });

    });

    test("Responsive", function(assert) {
        var responsive_async_done = assert.async();

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments"
            }
        });

        /* Responsive checks */
        $(".mapcontainer").width(CST_MAP_MAX_WIDTH/2);
        $(window).trigger('resize');
        setTimeout(function() {
            var $svg = $(".mapcontainer .map svg");
            assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check new map width size" );
            assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check new map height size" );

            $(".mapcontainer").width(CST_MAP_MAX_WIDTH);
            $(window).trigger('resize');
            setTimeout(function() {
                var $svg = $(".mapcontainer .map svg");
                assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH, "Check old map width size" );
                assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT, "Check old map height size" );
                responsive_async_done();
            }, 500);
        }, 500);

    });

    module("Options");

    test("Force width", function(assert) {
        var responsive_async_done = assert.async();

        /* Create the map */
        $(".mapcontainer").mapael({
            map: {
                name: "france_departments",
                width:CST_MAP_MAX_WIDTH/2
            }
        });

        var $svg = $(".mapcontainer .map svg");

        assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check map fixed width size" );
        assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check map fixed height size" );

        /* Responsive checks */
        $(".mapcontainer").width(CST_MAP_MAX_WIDTH/4);
        $(window).trigger('resize');
        setTimeout(function() {
            var $svg = $(".mapcontainer .map svg");
            assert.equal($svg.attr("width"), CST_MAP_MAX_WIDTH/2, "Check fixed map width size after resize" );
            assert.equal($svg.attr("height"), CST_MAP_MAX_HEIGHT/2, "Check fixed map height size after resize" );

            responsive_async_done();
        }, 500);

    });

    test("Default option override", function(assert) {
        var mouseover_async_done = assert.async(CST_NB_OF_FRANCE_DPTMT);

        var CST_DEFAULTAREA = {
            attrs: {
                fill: "#f4f4e8",
                stroke: "#ced8d0"
            }
            , attrsHover: {
                fill: "#a4e100",
                stroke: "#aaaaaa"
            }
            , text: {
                attrs: {
                    fill: "#505444"
                }
                , attrsHover: {
                    fill: "#000"
                }
            }
        };
        var CST_CUSTOMAREA = {
            "department-56": {
                text: {content: "TEXT_department-56", attrs: {"font-size": 10}},
                tooltip: {content: "TOOLTIP_department-56"}
            },
            "department-21": {
                attrs: {
                    fill: "#488402"
                }
                , attrsHover: {
                    fill: "#a4e100"
                }
            }
        };

        $(".mapcontainer").mapael({
            map: {
                name: "france_departments",
                defaultArea: CST_DEFAULTAREA
            },
            areas: CST_CUSTOMAREA
        });

        assert.ok($(".mapcontainer .map svg")[0], "Map created" );

        $(".mapcontainer svg path").each(function(id, elem) {
            var $elem = $(elem);
            var data_id = $elem.attr("data-id");

            if (data_id === "department-21") {
                assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrs.fill, "Check special overriden fill before mouseover for " + data_id);
            } else {
                assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrs.fill, "Check overriden fill before mouseover for " + data_id);
            }
            assert.equal($elem.attr("stroke"), CST_DEFAULTAREA.attrs.stroke, "Check overriden stroke before mouseover for " + data_id);

            $elem.trigger("mouseover");
            setTimeout(function() {

                if (data_id === "department-21") {
                    assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrsHover.fill, "Check special overriden hover fill after mouseover for " + data_id);
                } else {
                    assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrsHover.fill, "Check overriden hover fill after mouseover for " + data_id);
                }
                assert.equal($elem.attr("stroke"), CST_DEFAULTAREA.attrsHover.stroke, "Check overriden hover stroke after mouseover for " + data_id);

                if (data_id === "department-56") {
                    assert.ok($(".mapcontainer .map .mapTooltip").is( ":visible" ), "Check tooltip visible for " + data_id);
                    assert.equal($(".mapcontainer .map .mapTooltip").html(), CST_CUSTOMAREA[data_id].tooltip.content, "Check special tooltip content for " + data_id);
                    /* TODO : check text? */
                } else {
                    /* We can't test the following because it will fail for area around dptmt 56... */
                    //assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden for " + data_id);
                    //assert.equal($(".mapcontainer .map .mapTooltip").html(), "", "Check empty tooltip content for " + data_id);
                }

                $elem.trigger("mouseout");
                setTimeout(function() {
                    if (data_id === "department-21") {
                    assert.equal($elem.attr("fill"), CST_CUSTOMAREA[data_id].attrs.fill, "Check special overriden fill after mouseout for " + data_id);
                    } else {
                    assert.equal($elem.attr("fill"), CST_DEFAULTAREA.attrs.fill, "Check overriden fill after mouseout for " + data_id);
                    }

                    if (data_id === "department-56") {
                        assert.ok($(".mapcontainer .map .mapTooltip").is( ":hidden" ), "Check tooltip hidden after mouseout for " + data_id);
                        // assert.equal($(".mapcontainer .map .mapTooltip").html(), "", "Check empty tooltip content after mouseout for " + data_id);
                    } else {
                        /* We can't test the following because it will fail for area around dptmt 56... */
                    }

                    mouseover_async_done();
                }, 500);
            }, 500);
        });
    });

});<|endoftext|>


<commit_before>var $, Util;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Socket,
            MouseBinded,
            ShortCuts = {},
            CHANNEL = 'console-data',

            log     = write.bind(null, 'log'),
            error   = write.bind(null, 'error'),

            Buffer  = {
                log     : '',
                error   : ''
            },

            jqconsole;

        function Console(element, prefix, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string';

            if (!callback) {
                callback    = prefix;
                prefix      = '/console';
            }

            if (isString)
                el  = document.querySelector('.console');
            else
                el  = element;

            load(prefix, function() {
                Socket      = new window.Socket(),
                jqconsole   = $(el).jqconsole('', '> ');

                addShortCuts(jqconsole);
                addListeners();

                if (typeof callback === 'function')
                    callback();
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            var console   = jqconsole.$console,
                input     = jqconsole.$input_source,

                focus      = function() {
                    var x   = window.scrollX,
                        y   = window.scrollY;

                    input.focus();
                    window.scrollTo(x,y);
                };

            focus();

            if (!MouseBinded) {
                MouseBinded = true;

                console.unbind('mouseup');
                console.mouseup(function() {
                    var top,
                        isSelection = '' + window.getSelection();

                    if (!isSelection) {
                        top        = console.scrollTop();

                        focus();
                        console.scrollTop(top);
                    }
                });
            }
        };

        function load(prefix, callback) {
            loadScript([prefix + '/lib/client/load.js', prefix + '/join/join.js'], function() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/style.css',
                        '/css/console.css',
                        '/lib/client/console/css/ansi.css',
                    ]),
                    remote  = ([
                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min',
                        '//cdn.socket.io/socket.io-1.0.6'
                    ].map(function(src) {
                        return src + '.js';
                    })),

                    local   = prefix + join([
                        '/node_modules/util-io/lib/util',
                        '/lib/client/console/lib/jqconsole',
                        '/lib/client/socket'
                    ].map(function(src) {
                        return src + '.js';
                    }));

                load.parallel(remote.concat(css), function() {
                    load(local, callback);
                });
            });
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', func);

                document.body.appendChild(element);
            });
        }

        function handler(command) {
            if (command)
                Socket.emit(CHANNEL, command);
            else
                jqconsole.Prompt(true, handler);
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function addListeners() {
            var options = {
                'connect'   : function() {
                    log(Socket.CONNECTED);
                },

                'disconnect': function() {
                    var is = isPrompt();

                    error(Socket.DISCONNECTED);

                    if (is)
                        jqconsole.AbortPrompt();
                }
            };

            options[CHANNEL] = onMessage;

            Socket.on(options);
        }

        function onMessage(json) {
            var is = isPrompt();

            if (json) {
                Util.log(json);

                log(json.stdout);
                error(json.stderr);

                if (json.path)
                    jqconsole.SetPromptLabel(json.path + '> ');
            }

            if (!is)
                jqconsole.Prompt(true, handler);
        }

        function write(status, msg) {
            var isContain;

            if (msg) {
                Buffer[status] += msg;
                isContain       = Util.isContainStr(Buffer[status], '\n');

                if (jqconsole && isContain) {
                    jqconsole.Write(Buffer[status], status + '-msg');
                    Buffer[status] = '';
                }
            }
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, handler);
        }

        return Console;
    }

})(this);
<commit_msg>refactor(console) addOnMouseUp<commit_after>var $, Util;

(function(window) {
    'use strict';

    window.Console = new ConsoleProto();

    function ConsoleProto() {
        var Socket,
            MouseBinded,
            ShortCuts = {},
            CHANNEL = 'console-data',

            log     = write.bind(null, 'log'),
            error   = write.bind(null, 'error'),

            Buffer  = {
                log     : '',
                error   : ''
            },

            jqconsole;

        function Console(element, prefix, callback) {
            var el,
                type        = typeof element,
                isString    = type === 'string',
                isCallback  = typeof callback === 'function';

            if (!callback) {
                callback    = prefix;
                prefix      = '/console';
            }

            if (isString)
                el  = document.querySelector('.console');
            else
                el  = element;

            load(prefix, function() {
                Socket      = new window.Socket(),
                jqconsole   = $(el).jqconsole('', '> ');

                addShortCuts(jqconsole);
                addListeners(jqconsole);
                addOnMouseUp(jqconsole);

                if (isCallback)
                    callback();
            });
        }

        Console.addShortCuts    = function(shortCuts) {
            if (shortCuts)
                ShortCuts = shortCuts;
        };

        Console.getPromptText   = function() {
            var text = jqconsole.GetPromptText();

            return text;
        };

        Console.setPromptText   = function(text) {
            jqconsole.SetPromptText(text);
        };

        Console.focus           = function() {
            var input       = jqconsole.$input_source,

                x           = window.scrollX,
                y           = window.scrollY;

            input.focus();
            window.scrollTo(x,y);
        };

        function addOnMouseUp(jqconsole) {
            var console     = jqconsole.$console;

            console.unbind('mouseup');

            console.mouseup(function() {
                var top,
                    isSelection = '' + window.getSelection();

                if (!isSelection) {
                    top        = console.scrollTop();

                    Console.focus();
                    console.scrollTop(top);
                }
            });
        }

        function load(prefix, callback) {
            loadScript([prefix + '/lib/client/load.js', prefix + '/join/join.js'], function() {
                var load    = window.load,
                    join    = window.join,

                    css     = prefix + join([
                        '/css/style.css',
                        '/css/console.css',
                        '/lib/client/console/css/ansi.css',
                    ]),
                    remote  = ([
                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min',
                        '//cdn.socket.io/socket.io-1.0.6'
                    ].map(function(src) {
                        return src + '.js';
                    })),

                    local   = prefix + join([
                        '/node_modules/util-io/lib/util',
                        '/lib/client/console/lib/jqconsole',
                        '/lib/client/socket'
                    ].map(function(src) {
                        return src + '.js';
                    }));

                load.parallel(remote.concat(css), function() {
                    load(local, callback);
                });
            });
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', func);

                document.body.appendChild(element);
            });
        }

        function handler(command) {
            if (command)
                Socket.emit(CHANNEL, command);
            else
                jqconsole.Prompt(true, handler);
        }

        function isPrompt() {
            var state   = jqconsole.GetState(),
                is      = state === 'prompt';

            return is;
        }

        function addListeners(jqconsole) {
            var options = {
                'connect'   : function() {
                    log(Socket.CONNECTED);
                },

                'disconnect': function() {
                    var is = isPrompt();

                    error(Socket.DISCONNECTED);

                    if (is)
                        jqconsole.AbortPrompt();
                }
            };

            options[CHANNEL] = onMessage;

            Socket.on(options);
        }

        function onMessage(json) {
            var is = isPrompt();

            if (json) {
                Util.log(json);

                log(json.stdout);
                error(json.stderr);

                if (json.path)
                    jqconsole.SetPromptLabel(json.path + '> ');
            }

            if (!is)
                jqconsole.Prompt(true, handler);
        }

        function write(status, msg) {
            var isContain;

            if (msg) {
                Buffer[status] += msg;
                isContain       = Util.isContainStr(Buffer[status], '\n');

                if (jqconsole && isContain) {
                    jqconsole.Write(Buffer[status], status + '-msg');
                    Buffer[status] = '';
                }
            }
        }

        function addShortCuts(jqconsole) {
            jqconsole.RegisterShortcut('Z', function() {
                jqconsole.SetPromptText('');
            });

            jqconsole.RegisterShortcut('L', clear);

            Object.keys(ShortCuts).forEach(function(key) {
                var func = ShortCuts[key];

                jqconsole.RegisterShortcut(key, func);
            });
        }

        function clear() {
            jqconsole.Reset();
            addShortCuts(jqconsole);
            jqconsole.Prompt(true, handler);
        }

        return Console;
    }

})(this);
<|endoftext|>


<commit_before>/*
* "transitions" plugin - Page change tranistions
*/

(function( $, window, undefined ) {

function outInTransitionHandler( name, reverse, $to, $from ) {

	// first, override name if there's no 3D transform support and a fallback is defined
	if( name && !$.support.cssTransform3d ){
		name = $.mobile.transitionFallbacks[ name ] || "none";
	}

	var deferred = new $.Deferred(),
		reverseClass = reverse ? " reverse" : "",
		active	= $.mobile.urlHistory.getActive(),
		touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
		toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
		screenHeight = $.mobile.getScreenHeight(),
		viewportClass = "ui-mobile-viewport-transitioning viewport-" + name,
		maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth,
		none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none",
		doneOut = function() {

			if ( $from ) {
				$from
					.removeClass( $.mobile.activePageClass + " out in reverse " + name )
					.height( "" );
			}

			$to.addClass( $.mobile.activePageClass );

			if( !none ){
				$to.animationComplete( doneIn );
			}

			// Send focus to page as it is now display: block
			$.mobile.focusPage( $to );

			if( touchOverflow && toScroll ){

				//set page's scrollTop to remembered distance
				if( $to.is( ".ui-native-fixed" ) ){
					$to.find( ".ui-content" ).scrollTop( toScroll );
				}
				else{
					$to.scrollTop( toScroll );
				}
			}

			// Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
			if( !touchOverflow ){
				$to.height( screenHeight + toScroll );

				$.mobile.silentScroll( toScroll );
			}

			$to.addClass( name + " in" + reverseClass );

			if( none ){
				doneIn();
			}

		},

		doneIn = function() {
			$to
				.removeClass( "out in reverse " + name )
				.parent().removeClass( viewportClass )
				.height( "" );

			deferred.resolve( name, reverse, $to, $from, true );
		};

	$to
		.parent().addClass( viewportClass );

	//clear page loader
	$.mobile.hidePageLoadingMsg();

	if ( $from && !none ) {
		$from
			.animationComplete( doneOut )
			.height( screenHeight + $(window ).scrollTop() )
			.addClass( name + " out" + reverseClass );
	}
	else {
		doneOut();
	}

	return deferred.promise();
}

// add class for where 3d transforms are supported, or not
$( "html" ).addClass( $.support.cssTransform3d ? "ui-supported-csstransform3d" : "ui-unsupported-csstransform3d" );

// Make our transition handler the public default.
$.mobile.defaultTransitionHandler = outInTransitionHandler;

//transition handler dictionary for 3rd party transitions
$.mobile.transitionHandlers = {
	"default": $.mobile.defaultTransitionHandler
};

$.mobile.transitionFallbacks = {};

})( jQuery, this );
<commit_msg>better comment<commit_after>/*
* "transitions" plugin - Page change tranistions
*/

(function( $, window, undefined ) {

function outInTransitionHandler( name, reverse, $to, $from ) {

	// override name if there's no 3D transform support and a fallback is defined, or if not, to "none"
	if( name && !$.support.cssTransform3d ){
		name = $.mobile.transitionFallbacks[ name ] || "none";
	}

	var deferred = new $.Deferred(),
		reverseClass = reverse ? " reverse" : "",
		active	= $.mobile.urlHistory.getActive(),
		touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
		toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
		screenHeight = $.mobile.getScreenHeight(),
		viewportClass = "ui-mobile-viewport-transitioning viewport-" + name,
		maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth,
		none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none",
		doneOut = function() {

			if ( $from ) {
				$from
					.removeClass( $.mobile.activePageClass + " out in reverse " + name )
					.height( "" );
			}

			$to.addClass( $.mobile.activePageClass );

			if( !none ){
				$to.animationComplete( doneIn );
			}

			// Send focus to page as it is now display: block
			$.mobile.focusPage( $to );

			if( touchOverflow && toScroll ){

				//set page's scrollTop to remembered distance
				if( $to.is( ".ui-native-fixed" ) ){
					$to.find( ".ui-content" ).scrollTop( toScroll );
				}
				else{
					$to.scrollTop( toScroll );
				}
			}

			// Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
			if( !touchOverflow ){
				$to.height( screenHeight + toScroll );

				$.mobile.silentScroll( toScroll );
			}

			$to.addClass( name + " in" + reverseClass );

			if( none ){
				doneIn();
			}

		},

		doneIn = function() {
			$to
				.removeClass( "out in reverse " + name )
				.parent().removeClass( viewportClass )
				.height( "" );

			deferred.resolve( name, reverse, $to, $from, true );
		};

	$to
		.parent().addClass( viewportClass );

	//clear page loader
	$.mobile.hidePageLoadingMsg();

	if ( $from && !none ) {
		$from
			.animationComplete( doneOut )
			.height( screenHeight + $(window ).scrollTop() )
			.addClass( name + " out" + reverseClass );
	}
	else {
		doneOut();
	}

	return deferred.promise();
}

// add class for where 3d transforms are supported, or not
$( "html" ).addClass( $.support.cssTransform3d ? "ui-supported-csstransform3d" : "ui-unsupported-csstransform3d" );

// Make our transition handler the public default.
$.mobile.defaultTransitionHandler = outInTransitionHandler;

//transition handler dictionary for 3rd party transitions
$.mobile.transitionHandlers = {
	"default": $.mobile.defaultTransitionHandler
};

$.mobile.transitionFallbacks = {};

})( jQuery, this );
<|endoftext|>


<commit_before>/**
 * ModulesList component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import classnames from 'classnames';

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { trackEvent } from '../util';
import Data from 'googlesitekit-data';
import Link from './Link';
import ModuleIcon from './ModuleIcon';
import ModuleSettingsWarning from './legacy-notifications/module-settings-warning';
import VisuallyHidden from './VisuallyHidden';
import { CORE_SITE } from '../googlesitekit/datastore/site/constants';
import { CORE_MODULES } from '../googlesitekit/modules/datastore/constants';
import { CORE_LOCATION } from '../googlesitekit/datastore/location/constants';
const { useSelect, useDispatch } = Data;

function ModulesList( { moduleSlugs } ) {
	const { activateModule } = useDispatch( CORE_MODULES );
	const { navigateTo } = useDispatch( CORE_LOCATION );
	const { setInternalServerError } = useDispatch( CORE_SITE );

	const modulesData = useSelect( ( select ) => select( CORE_MODULES ).getModules() );

	const handleSetupModule = useCallback( async ( slug ) => {
		const { response, error } = await activateModule( slug );

		if ( error ) {
			setInternalServerError( {
				id: 'setup-module-error',
				description: error.message,
			} );
			return null;
		}

		await trackEvent(
			`${ slug }_setup`,
			'module_activate',
			slug,
		);

		// Redirect to ReAuthentication URL
		navigateTo( response.moduleReauthURL );
	}, [ activateModule, navigateTo, setInternalServerError ] );

	if ( ! modulesData ) {
		return null;
	}

	// Filter specific modules
	const moduleObjects = Array.isArray( moduleSlugs ) && moduleSlugs.length
		? moduleSlugs
			.filter( ( slug ) => modulesData.hasOwnProperty( slug ) )
			.reduce( ( acc, slug ) => ( { ...acc, [ slug ]: modulesData[ slug ] } ), {} )
		: modulesData;

	// Filter out internal modules and remove modules with dependencies.
	const modules = Object.values( moduleObjects )
		.filter( ( module ) => ! module.internal && 0 === module.dependencies.length )
		.sort( ( a, b ) => a.order - b.order )
	;

	return (

			{ modules.map( ( module ) => {
				const {
					slug,
					name,
					connected,
					active,
				} = module;
				const setupComplete = connected && active;

				return (
					<div
						key={ slug }
						className={ classnames(
							'googlesitekit-modules-list__module',
							`googlesitekit-modules-list__module--${ slug }`,
						) }
					>

								<ModuleIcon slug={ slug } />

								{ name }

						<ModuleSettingsWarning slug={ slug } context="modules-list" />
						{ setupComplete && (

									<VisuallyHidden>
										{ __( 'Connected', 'google-site-kit' ) }
									</VisuallyHidden>

								{ __( 'Connected', 'google-site-kit' ) }

						) }
						{ ! setupComplete && (
							<Link
								onClick={ () => handleSetupModule( slug ) }
								arrow
								small
								inherit
							>
								{ __( 'Connect Service', 'google-site-kit' ) }
							</Link>
						) }

				);
			} ) }

	);
}

export default ModulesList;
<commit_msg>Remove from ModulesList.<commit_after>/**
 * ModulesList component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import classnames from 'classnames';

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { trackEvent } from '../util';
import Data from 'googlesitekit-data';
import Link from './Link';
import ModuleIcon from './ModuleIcon';
import ModuleSettingsWarning from './legacy-notifications/module-settings-warning';
import { CORE_SITE } from '../googlesitekit/datastore/site/constants';
import { CORE_MODULES } from '../googlesitekit/modules/datastore/constants';
import { CORE_LOCATION } from '../googlesitekit/datastore/location/constants';
const { useSelect, useDispatch } = Data;

function ModulesList( { moduleSlugs } ) {
	const { activateModule } = useDispatch( CORE_MODULES );
	const { navigateTo } = useDispatch( CORE_LOCATION );
	const { setInternalServerError } = useDispatch( CORE_SITE );

	const modulesData = useSelect( ( select ) => select( CORE_MODULES ).getModules() );

	const handleSetupModule = useCallback( async ( slug ) => {
		const { response, error } = await activateModule( slug );

		if ( error ) {
			setInternalServerError( {
				id: 'setup-module-error',
				description: error.message,
			} );
			return null;
		}

		await trackEvent(
			`${ slug }_setup`,
			'module_activate',
			slug,
		);

		// Redirect to ReAuthentication URL
		navigateTo( response.moduleReauthURL );
	}, [ activateModule, navigateTo, setInternalServerError ] );

	if ( ! modulesData ) {
		return null;
	}

	// Filter specific modules
	const moduleObjects = Array.isArray( moduleSlugs ) && moduleSlugs.length
		? moduleSlugs
			.filter( ( slug ) => modulesData.hasOwnProperty( slug ) )
			.reduce( ( acc, slug ) => ( { ...acc, [ slug ]: modulesData[ slug ] } ), {} )
		: modulesData;

	// Filter out internal modules and remove modules with dependencies.
	const modules = Object.values( moduleObjects )
		.filter( ( module ) => ! module.internal && 0 === module.dependencies.length )
		.sort( ( a, b ) => a.order - b.order )
	;

	return (

			{ modules.map( ( module ) => {
				const {
					slug,
					name,
					connected,
					active,
				} = module;
				const setupComplete = connected && active;

				return (
					<div
						key={ slug }
						className={ classnames(
							'googlesitekit-modules-list__module',
							`googlesitekit-modules-list__module--${ slug }`,
						) }
					>

								<ModuleIcon slug={ slug } />

								{ name }

						<ModuleSettingsWarning slug={ slug } context="modules-list" />
						{ setupComplete && (

								{ __( 'Connected', 'google-site-kit' ) }

						) }
						{ ! setupComplete && (
							<Link
								onClick={ () => handleSetupModule( slug ) }
								arrow
								small
								inherit
							>
								{ __( 'Connect Service', 'google-site-kit' ) }
							</Link>
						) }

				);
			} ) }

	);
}

export default ModulesList;
<|endoftext|>


<commit_before>describe('pjax table public methods', function() {
  $('body').append('

');
  var $tableContainer = $('#table-container');
  var $table;

  beforeEach(function() {
    $tableContainer.html(Fifty.modules.tableGenerator.generate());
    $table = $tableContainer.find('#pjax-table');
  });

  it('should refresh plugins', function() {
    var table = $table.pjaxTable({
      plugins: {
        target: '.unknown',
        constructorName: 'unknown'
      }
    });
    expect(typeof table.refreshPlugins === 'function').toBe(true);

    spyOn(table, 'refreshPlugins').and.callThrough();
    spyOn(table, '_applyPlugins');

    $table.pjaxTable('refreshPlugins');
    expect(table.refreshPlugins).toHaveBeenCalled();
    expect(table._applyPlugins).toHaveBeenCalled();
  });

  it('should update parameters and load on update', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.update === 'function').toBe(true);

    spyOn(table, 'update').and.callThrough();
    spyOn(table, 'updateParameters').and.callThrough();
    spyOn(table, '_load');

    var sameRef = $table.pjaxTable('update', { testKey: 'testValue' });

    expect(sameRef === table).toBe(true);
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._load).toHaveBeenCalled();
    expect(table._queryState['testKey']).toEqual('testValue');
  });

  it('should refresh', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.refresh === 'function').toBe(true);

    spyOn(table, 'refresh').and.callThrough();
    spyOn(table, '_load');

    var sameRef = $table.pjaxTable('refresh');

    expect(sameRef === table).toBe(true);
    expect(table.refresh).toHaveBeenCalled();
    expect(table._load).toHaveBeenCalled();
  });

  it('should return the current url', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getUrl === 'function').toBe(true);

    spyOn(table, 'getUrl').and.callThrough();

    expect($table.pjaxTable('getUrl')).toEqual('/');
    expect(table.getUrl).toHaveBeenCalled();
  });

  it('should support updating parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.updateParameters === 'function').toBe(true);

    spyOn(table, 'updateParameters').and.callThrough();

    // test the add
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond' });
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toEqual('testSecond');

    // test the delete
    $table.pjaxTable('updateParameters', { first: null, second: null });
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toBeUndefined();
  });

  it('should support removing parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.removeParameters === 'function').toBe(true);

    spyOn(table, 'removeParameters').and.callThrough();

    // test empty return early
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});

    // test return value and spy one time
    expect($table.pjaxTable('removeParameters') === table).toBe(true);
    expect(table.removeParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toEqual('testSecond');

    // test string single key remove
    $table.pjaxTable('removeParameters', 'first');
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toEqual('testSecond');

    // test array remove
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});
    $table.pjaxTable('removeParameters', ['first', 'second']);
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toBeUndefined();

    // test conditional with function remove
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});
    $table.pjaxTable('removeParameters', function(key, val) {
      if (key === 'second') {
        return true;
      }
      return false;
    });
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toBeUndefined();
  });

  it('should support getting parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getParameters === 'function').toBe(true);

    spyOn(table, 'getParameters').and.callThrough();

    // all params copy
    var params = $table.pjaxTable('getParameters');
    expect(params).toBeDefined();
    expect(table.getParameters).toHaveBeenCalled();
    expect(params['page']).toEqual(1);
    expect(params['perpage']).toEqual(50);
    expect(params['order']).toEqual('testProp__desc');
    expect(params['q']).toEqual('testSearchStr');

    // filtered with function
    var filteredParams = $table.pjaxTable('getParameters', function(key, value) {
      if (key === 'page' || key === 'order') {
        return true;
      }
      return false;
    });
    expect(filteredParams['page']).toEqual(1);
    expect(filteredParams['perpage']).toBeUndefined();
    expect(filteredParams['order']).toEqual('testProp__desc');
    expect(filteredParams['q']).toBeUndefined();
  });

  it('should get the number of records', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumRecords === 'function').toBe(true);

    spyOn(table, 'getNumRecords').and.callThrough();

    var numRecords = $table.pjaxTable('getNumRecords');
    expect(table.getNumRecords).toHaveBeenCalled();
    expect(numRecords).toBeDefined();
    expect(typeof numRecords === 'number').toBe(true);
    expect(numRecords).toEqual(50);
  });

  it('should get the number of columns', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumColumns === 'function').toBe(true);

    spyOn(table, 'getNumColumns').and.callThrough();

    var numColumns = $table.pjaxTable('getNumColumns');
    expect(table.getNumColumns).toHaveBeenCalled();
    expect(numColumns).toBeDefined();
    expect(typeof numColumns === 'number').toBe(true);
    expect(numColumns).toEqual(6);
  });

  it('should get whether or not the table has selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.hasSelectedRecords === 'function').toBe(true);

    spyOn(table, 'hasSelectedRecords').and.callThrough();

    var hasSelected = $table.pjaxTable('hasSelectedRecords');
    expect(table.hasSelectedRecords).toHaveBeenCalled();
    expect(hasSelected).toBeDefined();
    expect(typeof hasSelected === 'boolean').toBe(true);
    expect(hasSelected).toBe(false);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var hasSelected = $table.pjaxTable('hasSelectedRecords');
    expect(hasSelected).toBe(true);
  });

  it('should get the number of selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumSelectedRecords === 'function').toBe(true);

    spyOn(table, 'getNumSelectedRecords').and.callThrough();

    var numSelected = $table.pjaxTable('getNumSelectedRecords');
    expect(table.getNumSelectedRecords).toHaveBeenCalled();
    expect(numSelected).toBeDefined();
    expect(typeof numSelected === 'number').toBe(true);
    expect(numSelected).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var numSelected = $table.pjaxTable('getNumSelectedRecords');
    expect(numSelected).toEqual(50);
  });

  it('should get the selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getSelectedRecords === 'function').toBe(true);

    spyOn(table, 'getSelectedRecords').and.callThrough();

    var selectedRows = $table.pjaxTable('getSelectedRecords');
    expect(table.getSelectedRecords).toHaveBeenCalled();
    expect(selectedRows).toBeDefined();
    expect(Array.isArray(selectedRows)).toBe(true);
    expect(selectedRows.length).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var selectedRows = $table.pjaxTable('getSelectedRecords');
    expect(selectedRows.length).toEqual(50);
    expect(selectedRows[0]['Heroes']).toEqual('Griffith Shaffer');
    expect(selectedRows[49]['Heroes']).toEqual('Mannix Walsh');

    // test with formatter
    var selectedNames = $table.pjaxTable('getSelectedRecords', function(record) {
      return record['Heroes'];
    });
    expect(selectedNames).toBeDefined();
    expect(Array.isArray(selectedNames)).toBe(true);
    expect(selectedNames.length).toEqual(50);
    expect(selectedNames[0]).toEqual('Griffith Shaffer');
    expect(selectedNames[49]).toEqual('Mannix Walsh');
  });

  it('should get the select row ids', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getSelectedRecordIds === 'function').toBe(true);

    spyOn(table, 'getSelectedRecordIds').and.callThrough();

    var selectedIds = $table.pjaxTable('getSelectedRecordIds');
    expect(table.getSelectedRecordIds).toHaveBeenCalled();
    expect(selectedIds).toBeDefined();
    expect(Array.isArray(selectedIds)).toBe(true);
    expect(selectedIds.length).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var selectedIds = $table.pjaxTable('getSelectedRecordIds');
    expect(selectedIds.length).toEqual(50);
    expect(selectedIds[0]).toEqual(0);
    expect(selectedIds[49]).toEqual(49);
  });

  it('should get all records', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getRecords === 'function').toBe(true);

    spyOn(table, 'getRecords').and.callThrough();

    var records = $table.pjaxTable('getRecords');
    expect(table.getRecords).toHaveBeenCalled();
    expect(records).toBeDefined();
    expect(Array.isArray(records)).toBe(true);
    expect(records.length).toEqual(50);
    expect(records[0]['id']).toEqual(0);
    expect(records[0]['Heroes']).toEqual('Griffith Shaffer');
    expect(records[49]['id']).toEqual(49);
    expect(records[49]['Heroes']).toEqual('Mannix Walsh');
  });

  it('should update row fields by id', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.updateRow === 'function').toBe(true);

    spyOn(table, 'updateRow').and.callThrough();
    spyOn(table, '_findRowById').and.callThrough();
    spyOn(table, '_updateRowFields').and.callThrough();

    // only test internal calls, as all other processing has already been tested
    $table.pjaxTable('updateRow', 0, { Heroes: 'test' });
    expect(table.updateRow).toHaveBeenCalled();
    expect(table._findRowById).toHaveBeenCalled();
    expect(table._updateRowFields).toHaveBeenCalled();
    expect(table._getRecord(table._findRowById(0))['Heroes']).toEqual('test');
  });

  it('should get the total number of rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumTotalRows === 'function').toBe(true);

    spyOn(table, 'getNumTotalRows').and.callThrough();

    var numTotalRows = $table.pjaxTable('getNumTotalRows');
    expect(table.getNumTotalRows).toHaveBeenCalled();
    expect(numTotalRows).toBeDefined();
    expect(typeof numTotalRows === 'number').toBe(true);
    expect(numTotalRows).toEqual(50);
  });
});
<commit_msg>add load test<commit_after>describe('pjax table public methods', function() {
  $('body').append('

');
  var $tableContainer = $('#table-container');
  var $table;

  beforeEach(function() {
    $tableContainer.html(Fifty.modules.tableGenerator.generate());
    $table = $tableContainer.find('#pjax-table');
  });

  it('should refresh plugins', function() {
    var table = $table.pjaxTable({
      plugins: {
        target: '.unknown',
        constructorName: 'unknown'
      }
    });
    expect(typeof table.refreshPlugins === 'function').toBe(true);

    spyOn(table, 'refreshPlugins').and.callThrough();
    spyOn(table, '_applyPlugins');

    $table.pjaxTable('refreshPlugins');
    expect(table.refreshPlugins).toHaveBeenCalled();
    expect(table._applyPlugins).toHaveBeenCalled();
  });

  it('should update parameters and load on update', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.update === 'function').toBe(true);

    spyOn(table, 'update').and.callThrough();
    spyOn(table, 'updateParameters').and.callThrough();
    spyOn(table, '_load');

    var sameRef = $table.pjaxTable('update', { testKey: 'testValue' });

    expect(sameRef === table).toBe(true);
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._load).toHaveBeenCalled();
    expect(table._queryState['testKey']).toEqual('testValue');
  });

  it('should load', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.load === 'function').toBe(true);

    spyOn(table, 'load').and.callThrough();
    spyOn(table, '_load');

    var testLoadParams = { testKey: 'testValue' };
    var sameRef = $table.pjaxTable('load', testLoadParams);

    expect(sameRef === table).toBe(true);
    expect(table.load).toHaveBeenCalledWith(testLoadParams);
    expect(table._load).toHaveBeenCalledWith(testLoadParams);
  });

  it('should refresh', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.refresh === 'function').toBe(true);

    spyOn(table, 'refresh').and.callThrough();
    spyOn(table, '_load');

    var sameRef = $table.pjaxTable('refresh');

    expect(sameRef === table).toBe(true);
    expect(table.refresh).toHaveBeenCalled();
    expect(table._load).toHaveBeenCalled();
  });

  it('should return the current url', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getUrl === 'function').toBe(true);

    spyOn(table, 'getUrl').and.callThrough();

    expect($table.pjaxTable('getUrl')).toEqual('/');
    expect(table.getUrl).toHaveBeenCalled();
  });

  it('should support updating parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.updateParameters === 'function').toBe(true);

    spyOn(table, 'updateParameters').and.callThrough();

    // test the add
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond' });
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toEqual('testSecond');

    // test the delete
    $table.pjaxTable('updateParameters', { first: null, second: null });
    expect(table.updateParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toBeUndefined();
  });

  it('should support removing parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.removeParameters === 'function').toBe(true);

    spyOn(table, 'removeParameters').and.callThrough();

    // test empty return early
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});

    // test return value and spy one time
    expect($table.pjaxTable('removeParameters') === table).toBe(true);
    expect(table.removeParameters).toHaveBeenCalled();
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toEqual('testSecond');

    // test string single key remove
    $table.pjaxTable('removeParameters', 'first');
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toEqual('testSecond');

    // test array remove
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});
    $table.pjaxTable('removeParameters', ['first', 'second']);
    expect(table._queryState['first']).toBeUndefined();
    expect(table._queryState['second']).toBeUndefined();

    // test conditional with function remove
    $table.pjaxTable('updateParameters', { first: 'testFirst', second: 'testSecond'});
    $table.pjaxTable('removeParameters', function(key, val) {
      if (key === 'second') {
        return true;
      }
      return false;
    });
    expect(table._queryState['first']).toEqual('testFirst');
    expect(table._queryState['second']).toBeUndefined();
  });

  it('should support getting parameters', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getParameters === 'function').toBe(true);

    spyOn(table, 'getParameters').and.callThrough();

    // all params copy
    var params = $table.pjaxTable('getParameters');
    expect(params).toBeDefined();
    expect(table.getParameters).toHaveBeenCalled();
    expect(params['page']).toEqual(1);
    expect(params['perpage']).toEqual(50);
    expect(params['order']).toEqual('testProp__desc');
    expect(params['q']).toEqual('testSearchStr');

    // filtered with function
    var filteredParams = $table.pjaxTable('getParameters', function(key, value) {
      if (key === 'page' || key === 'order') {
        return true;
      }
      return false;
    });
    expect(filteredParams['page']).toEqual(1);
    expect(filteredParams['perpage']).toBeUndefined();
    expect(filteredParams['order']).toEqual('testProp__desc');
    expect(filteredParams['q']).toBeUndefined();
  });

  it('should get the number of records', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumRecords === 'function').toBe(true);

    spyOn(table, 'getNumRecords').and.callThrough();

    var numRecords = $table.pjaxTable('getNumRecords');
    expect(table.getNumRecords).toHaveBeenCalled();
    expect(numRecords).toBeDefined();
    expect(typeof numRecords === 'number').toBe(true);
    expect(numRecords).toEqual(50);
  });

  it('should get the number of columns', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumColumns === 'function').toBe(true);

    spyOn(table, 'getNumColumns').and.callThrough();

    var numColumns = $table.pjaxTable('getNumColumns');
    expect(table.getNumColumns).toHaveBeenCalled();
    expect(numColumns).toBeDefined();
    expect(typeof numColumns === 'number').toBe(true);
    expect(numColumns).toEqual(6);
  });

  it('should get whether or not the table has selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.hasSelectedRecords === 'function').toBe(true);

    spyOn(table, 'hasSelectedRecords').and.callThrough();

    var hasSelected = $table.pjaxTable('hasSelectedRecords');
    expect(table.hasSelectedRecords).toHaveBeenCalled();
    expect(hasSelected).toBeDefined();
    expect(typeof hasSelected === 'boolean').toBe(true);
    expect(hasSelected).toBe(false);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var hasSelected = $table.pjaxTable('hasSelectedRecords');
    expect(hasSelected).toBe(true);
  });

  it('should get the number of selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumSelectedRecords === 'function').toBe(true);

    spyOn(table, 'getNumSelectedRecords').and.callThrough();

    var numSelected = $table.pjaxTable('getNumSelectedRecords');
    expect(table.getNumSelectedRecords).toHaveBeenCalled();
    expect(numSelected).toBeDefined();
    expect(typeof numSelected === 'number').toBe(true);
    expect(numSelected).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var numSelected = $table.pjaxTable('getNumSelectedRecords');
    expect(numSelected).toEqual(50);
  });

  it('should get the selected rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getSelectedRecords === 'function').toBe(true);

    spyOn(table, 'getSelectedRecords').and.callThrough();

    var selectedRows = $table.pjaxTable('getSelectedRecords');
    expect(table.getSelectedRecords).toHaveBeenCalled();
    expect(selectedRows).toBeDefined();
    expect(Array.isArray(selectedRows)).toBe(true);
    expect(selectedRows.length).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var selectedRows = $table.pjaxTable('getSelectedRecords');
    expect(selectedRows.length).toEqual(50);
    expect(selectedRows[0]['Heroes']).toEqual('Griffith Shaffer');
    expect(selectedRows[49]['Heroes']).toEqual('Mannix Walsh');

    // test with formatter
    var selectedNames = $table.pjaxTable('getSelectedRecords', function(record) {
      return record['Heroes'];
    });
    expect(selectedNames).toBeDefined();
    expect(Array.isArray(selectedNames)).toBe(true);
    expect(selectedNames.length).toEqual(50);
    expect(selectedNames[0]).toEqual('Griffith Shaffer');
    expect(selectedNames[49]).toEqual('Mannix Walsh');
  });

  it('should get the select row ids', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getSelectedRecordIds === 'function').toBe(true);

    spyOn(table, 'getSelectedRecordIds').and.callThrough();

    var selectedIds = $table.pjaxTable('getSelectedRecordIds');
    expect(table.getSelectedRecordIds).toHaveBeenCalled();
    expect(selectedIds).toBeDefined();
    expect(Array.isArray(selectedIds)).toBe(true);
    expect(selectedIds.length).toEqual(0);

    // now select and retest
    $table.find('td[data-property="id"] input[type="checkbox"]').prop('checked', true).change();
    var selectedIds = $table.pjaxTable('getSelectedRecordIds');
    expect(selectedIds.length).toEqual(50);
    expect(selectedIds[0]).toEqual(0);
    expect(selectedIds[49]).toEqual(49);
  });

  it('should get all records', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getRecords === 'function').toBe(true);

    spyOn(table, 'getRecords').and.callThrough();

    var records = $table.pjaxTable('getRecords');
    expect(table.getRecords).toHaveBeenCalled();
    expect(records).toBeDefined();
    expect(Array.isArray(records)).toBe(true);
    expect(records.length).toEqual(50);
    expect(records[0]['id']).toEqual(0);
    expect(records[0]['Heroes']).toEqual('Griffith Shaffer');
    expect(records[49]['id']).toEqual(49);
    expect(records[49]['Heroes']).toEqual('Mannix Walsh');
  });

  it('should update row fields by id', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.updateRow === 'function').toBe(true);

    spyOn(table, 'updateRow').and.callThrough();
    spyOn(table, '_findRowById').and.callThrough();
    spyOn(table, '_updateRowFields').and.callThrough();

    // only test internal calls, as all other processing has already been tested
    $table.pjaxTable('updateRow', 0, { Heroes: 'test' });
    expect(table.updateRow).toHaveBeenCalled();
    expect(table._findRowById).toHaveBeenCalled();
    expect(table._updateRowFields).toHaveBeenCalled();
    expect(table._getRecord(table._findRowById(0))['Heroes']).toEqual('test');
  });

  it('should get the total number of rows', function() {
    var table = $table.pjaxTable({});
    expect(typeof table.getNumTotalRows === 'function').toBe(true);

    spyOn(table, 'getNumTotalRows').and.callThrough();

    var numTotalRows = $table.pjaxTable('getNumTotalRows');
    expect(table.getNumTotalRows).toHaveBeenCalled();
    expect(numTotalRows).toBeDefined();
    expect(typeof numTotalRows === 'number').toBe(true);
    expect(numTotalRows).toEqual(50);
  });
});
<|endoftext|>


<commit_before>var BinarySearchTree = require('binary-search-tree').AVLTree
  , model = require('./model')
  , _ = require('underscore')
  , util = require('util')
  ;

/**
 * Two indexed pointers are equal iif they point to the same place
 */
function checkValueEquality (a, b) {
  return a === b;
}

/**
 * Type-aware projection
 */
function projectForUnique (elt) {
  if (elt === null) { return '$null'; }
  if (typeof elt === 'string') { return '$string' + elt; }
  if (typeof elt === 'boolean') { return '$boolean' + elt; }
  if (typeof elt === 'number') { return '$number' + elt; }
  if (util.isArray(elt)) { return '$date' + elt.getTime(); }

  return elt;   // Arrays and objects, will check for pointer equality
}

/**
 * Create a new index
 * All methods on an index guarantee that either the whole operation was successful and the index changed
 * or the operation was unsuccessful and an error is thrown while the index is unchanged
 * @param {String} options.fieldName On which field should the index apply (can use dot notation to index on sub fields)
 * @param {Boolean} options.unique Optional, enforce a unique constraint (default: false)
 * @param {Boolean} options.sparse Optional, allow a sparse index (we can have documents for which fieldName is undefined) (default: false)
 */
function Index (options) {
  this.fieldName = options.fieldName;
  this.unique = options.unique || false;
  this.sparse = options.sparse || false;

  this.treeOptions = { unique: this.unique, compareKeys: model.compareThings, checkValueEquality: checkValueEquality };

  this.reset();   // No data in the beginning
}

/**
 * Reset an index
 * @param {Document or Array of documents} newData Optional, data to initialize the index with
 *                                                 If an error is thrown during insertion, the index is not modified
 */
Index.prototype.reset = function (newData) {
  this.tree = new BinarySearchTree(this.treeOptions);

  if (newData) { this.insert(newData); }
};

/**
 * Insert a new document in the index
 * If an array is passed, we insert all its elements (if one insertion fails the index is not modified)
 * O(log(n))
 */
Index.prototype.insert = function (doc) {
  var key, self = this
    , keys, i, failingI, error
    ;

  if (util.isArray(doc)) { this.insertMultipleDocs(doc); return; }

  key = model.getDotValue(doc, this.fieldName);

  // We don't index documents that don't contain the field if the index is sparse
  if (key === undefined && this.sparse) { return; }

  if (!util.isArray(key)) {
    this.tree.insert(key, doc);
  } else {
    // If an insert fails due to a unique constraint, roll back all inserts before it
    keys = _.uniq(key, projectForUnique);

    for (i = 0; i < keys.length; i += 1) {
      try {
        this.tree.insert(keys[i], doc);
      } catch (e) {
        error = e;
        failingI = i;
        break;
      }
    }

    if (error) {
      for (i = 0; i < failingI; i += 1) {
        this.tree.delete(keys[i], doc);
      }

      throw error;
    }
  }
};

/**
 * Insert an array of documents in the index
 * If a constraint is violated, the changes should be rolled back and an error thrown
 *
 * @API private
 */
Index.prototype.insertMultipleDocs = function (docs) {
  var i, error, failingI;

  for (i = 0; i < docs.length; i += 1) {
    try {
      this.insert(docs[i]);
    } catch (e) {
      error = e;
      failingI = i;
      break;
    }
  }

  if (error) {
    for (i = 0; i < failingI; i += 1) {
      this.remove(docs[i]);
    }

    throw error;
  }
};

/**
 * Remove a document from the index
 * If an array is passed, we remove all its elements
 * The remove operation is safe with regards to the 'unique' constraint
 * O(log(n))
 */
Index.prototype.remove = function (doc) {
  var key, self = this;

  if (util.isArray(doc)) { doc.forEach(function (d) { self.remove(d); }); return; }

  key = model.getDotValue(doc, this.fieldName);

  if (key === undefined && this.sparse) { return; }

  if (!util.isArray(key)) {
    this.tree.delete(key, doc);
  } else {
    _.uniq(key, projectForUnique).forEach(function (_key) {
      self.tree.delete(_key, doc);
    });
  }
};

/**
 * Update a document in the index
 * If a constraint is violated, changes are rolled back and an error thrown
 * Naive implementation, still in O(log(n))
 */
Index.prototype.update = function (oldDoc, newDoc) {
  if (util.isArray(oldDoc)) { this.updateMultipleDocs(oldDoc); return; }

  this.remove(oldDoc);

  try {
    this.insert(newDoc);
  } catch (e) {
    this.insert(oldDoc);
    throw e;
  }
};

/**
 * Update multiple documents in the index
 * If a constraint is violated, the changes need to be rolled back
 * and an error thrown
 * @param {Array of oldDoc, newDoc pairs} pairs
 *
 * @API private
 */
Index.prototype.updateMultipleDocs = function (pairs) {
  var i, failingI, error;

  for (i = 0; i < pairs.length; i += 1) {
    this.remove(pairs[i].oldDoc);
  }

  for (i = 0; i < pairs.length; i += 1) {
    try {
      this.insert(pairs[i].newDoc);
    } catch (e) {
      error = e;
      failingI = i;
      break;
    }
  }

  // If an error was raised, roll back changes in the inverse order
  if (error) {
    for (i = 0; i < failingI; i += 1) {
      this.remove(pairs[i].newDoc);
    }

    for (i = 0; i < pairs.length; i += 1) {
      this.insert(pairs[i].oldDoc);
    }

    throw error;
  }
};

/**
 * Revert an update
 */
Index.prototype.revertUpdate = function (oldDoc, newDoc) {
  var revert = [];

  if (!util.isArray(oldDoc)) {
    this.update(newDoc, oldDoc);
  } else {
    oldDoc.forEach(function (pair) {
      revert.push({ oldDoc: pair.newDoc, newDoc: pair.oldDoc });
    });
    this.update(revert);
  }
};

/**
 * Get all documents in index whose key match value (if it is a Thing) or one of the elements of value (if it is an array of Things)
 * @param {Thing} value Value to match the key against
 * @return {Array of documents}
 */
Index.prototype.getMatching = function (value) {
  var _res, res
    , self = this;

  if (!util.isArray(value)) {
    res = self.tree.search(value);
  } else {
    _res = {};
    res = [];

    value.forEach(function (v) {
      self.getMatching(v).forEach(function (doc) {
        _res[doc._id] = doc;
      });
    });

    Object.keys(_res).forEach(function (_id) {
      res.push(_res[_id]);
    });
  }
  return res;
};

/**
 * Get all documents in index whose key is between bounds are they are defined by query
 * Documents are sorted by key
 * @param {Query} query
 * @return {Array of documents}
 */
Index.prototype.getBetweenBounds = function (query) {
  return this.tree.betweenBounds(query);
};

/**
 * Get all elements in the index
 * @return {Array of documents}
 */
Index.prototype.getAll = function () {
  var res = [];

  this.tree.executeOnEveryNode(function (node) {
    var i;

    for (i = 0; i < node.data.length; i += 1) {
      res.push(node.data[i]);
    }
  });

  return res;
};

// Interface
module.exports = Index;
<commit_msg>Use my style in getMatching<commit_after>var BinarySearchTree = require('binary-search-tree').AVLTree
  , model = require('./model')
  , _ = require('underscore')
  , util = require('util')
  ;

/**
 * Two indexed pointers are equal iif they point to the same place
 */
function checkValueEquality (a, b) {
  return a === b;
}

/**
 * Type-aware projection
 */
function projectForUnique (elt) {
  if (elt === null) { return '$null'; }
  if (typeof elt === 'string') { return '$string' + elt; }
  if (typeof elt === 'boolean') { return '$boolean' + elt; }
  if (typeof elt === 'number') { return '$number' + elt; }
  if (util.isArray(elt)) { return '$date' + elt.getTime(); }

  return elt;   // Arrays and objects, will check for pointer equality
}

/**
 * Create a new index
 * All methods on an index guarantee that either the whole operation was successful and the index changed
 * or the operation was unsuccessful and an error is thrown while the index is unchanged
 * @param {String} options.fieldName On which field should the index apply (can use dot notation to index on sub fields)
 * @param {Boolean} options.unique Optional, enforce a unique constraint (default: false)
 * @param {Boolean} options.sparse Optional, allow a sparse index (we can have documents for which fieldName is undefined) (default: false)
 */
function Index (options) {
  this.fieldName = options.fieldName;
  this.unique = options.unique || false;
  this.sparse = options.sparse || false;

  this.treeOptions = { unique: this.unique, compareKeys: model.compareThings, checkValueEquality: checkValueEquality };

  this.reset();   // No data in the beginning
}

/**
 * Reset an index
 * @param {Document or Array of documents} newData Optional, data to initialize the index with
 *                                                 If an error is thrown during insertion, the index is not modified
 */
Index.prototype.reset = function (newData) {
  this.tree = new BinarySearchTree(this.treeOptions);

  if (newData) { this.insert(newData); }
};

/**
 * Insert a new document in the index
 * If an array is passed, we insert all its elements (if one insertion fails the index is not modified)
 * O(log(n))
 */
Index.prototype.insert = function (doc) {
  var key, self = this
    , keys, i, failingI, error
    ;

  if (util.isArray(doc)) { this.insertMultipleDocs(doc); return; }

  key = model.getDotValue(doc, this.fieldName);

  // We don't index documents that don't contain the field if the index is sparse
  if (key === undefined && this.sparse) { return; }

  if (!util.isArray(key)) {
    this.tree.insert(key, doc);
  } else {
    // If an insert fails due to a unique constraint, roll back all inserts before it
    keys = _.uniq(key, projectForUnique);

    for (i = 0; i < keys.length; i += 1) {
      try {
        this.tree.insert(keys[i], doc);
      } catch (e) {
        error = e;
        failingI = i;
        break;
      }
    }

    if (error) {
      for (i = 0; i < failingI; i += 1) {
        this.tree.delete(keys[i], doc);
      }

      throw error;
    }
  }
};

/**
 * Insert an array of documents in the index
 * If a constraint is violated, the changes should be rolled back and an error thrown
 *
 * @API private
 */
Index.prototype.insertMultipleDocs = function (docs) {
  var i, error, failingI;

  for (i = 0; i < docs.length; i += 1) {
    try {
      this.insert(docs[i]);
    } catch (e) {
      error = e;
      failingI = i;
      break;
    }
  }

  if (error) {
    for (i = 0; i < failingI; i += 1) {
      this.remove(docs[i]);
    }

    throw error;
  }
};

/**
 * Remove a document from the index
 * If an array is passed, we remove all its elements
 * The remove operation is safe with regards to the 'unique' constraint
 * O(log(n))
 */
Index.prototype.remove = function (doc) {
  var key, self = this;

  if (util.isArray(doc)) { doc.forEach(function (d) { self.remove(d); }); return; }

  key = model.getDotValue(doc, this.fieldName);

  if (key === undefined && this.sparse) { return; }

  if (!util.isArray(key)) {
    this.tree.delete(key, doc);
  } else {
    _.uniq(key, projectForUnique).forEach(function (_key) {
      self.tree.delete(_key, doc);
    });
  }
};

/**
 * Update a document in the index
 * If a constraint is violated, changes are rolled back and an error thrown
 * Naive implementation, still in O(log(n))
 */
Index.prototype.update = function (oldDoc, newDoc) {
  if (util.isArray(oldDoc)) { this.updateMultipleDocs(oldDoc); return; }

  this.remove(oldDoc);

  try {
    this.insert(newDoc);
  } catch (e) {
    this.insert(oldDoc);
    throw e;
  }
};

/**
 * Update multiple documents in the index
 * If a constraint is violated, the changes need to be rolled back
 * and an error thrown
 * @param {Array of oldDoc, newDoc pairs} pairs
 *
 * @API private
 */
Index.prototype.updateMultipleDocs = function (pairs) {
  var i, failingI, error;

  for (i = 0; i < pairs.length; i += 1) {
    this.remove(pairs[i].oldDoc);
  }

  for (i = 0; i < pairs.length; i += 1) {
    try {
      this.insert(pairs[i].newDoc);
    } catch (e) {
      error = e;
      failingI = i;
      break;
    }
  }

  // If an error was raised, roll back changes in the inverse order
  if (error) {
    for (i = 0; i < failingI; i += 1) {
      this.remove(pairs[i].newDoc);
    }

    for (i = 0; i < pairs.length; i += 1) {
      this.insert(pairs[i].oldDoc);
    }

    throw error;
  }
};

/**
 * Revert an update
 */
Index.prototype.revertUpdate = function (oldDoc, newDoc) {
  var revert = [];

  if (!util.isArray(oldDoc)) {
    this.update(newDoc, oldDoc);
  } else {
    oldDoc.forEach(function (pair) {
      revert.push({ oldDoc: pair.newDoc, newDoc: pair.oldDoc });
    });
    this.update(revert);
  }
};

/**
 * Get all documents in index whose key match value (if it is a Thing) or one of the elements of value (if it is an array of Things)
 * @param {Thing} value Value to match the key against
 * @return {Array of documents}
 */
Index.prototype.getMatching = function (value) {
  var self = this;

  if (!util.isArray(value)) {
    return self.tree.search(value);
  } else {
    var _res = {}, res = [];

    value.forEach(function (v) {
      self.getMatching(v).forEach(function (doc) {
        _res[doc._id] = doc;
      });
    });

    Object.keys(_res).forEach(function (_id) {
      res.push(_res[_id]);
    });

    return res;
  }
};

/**
 * Get all documents in index whose key is between bounds are they are defined by query
 * Documents are sorted by key
 * @param {Query} query
 * @return {Array of documents}
 */
Index.prototype.getBetweenBounds = function (query) {
  return this.tree.betweenBounds(query);
};

/**
 * Get all elements in the index
 * @return {Array of documents}
 */
Index.prototype.getAll = function () {
  var res = [];

  this.tree.executeOnEveryNode(function (node) {
    var i;

    for (i = 0; i < node.data.length; i += 1) {
      res.push(node.data[i]);
    }
  });

  return res;
};

// Interface
module.exports = Index;
<|endoftext|>


<commit_before>describe("uzi", function () {

  describe("Variable", function () {

    it("should not be able to set a value twice in the same digest", function () {
      var v = new uzi.Variable("name");

      v.setValue(123, 0);

      // can't set a value twice in the same digest
      expect(function () { v.setValue(321, 0); }).toThrow();

      // ok to set a new value in a new digest
      v.setValue(321, 1);
      expect(v.getValue()).toEqual(321);
    });

    it("should not be ok to destroy if it was assigned", function () {
      var v = new uzi.Variable("name");

      v.setValue(123, 0);
      expect(v.canDestroy()).toBe(true);

      v.assignValue(123);
      expect(v.canDestroy()).toBe(false);
    });

  });

  describe("GeneratedParser", function () {

    function parseWith(input, startRule) {
      try {
        return uzi.Parser.parse(input, { startRule: startRule });
      } catch (e) {
        if (e instanceof uzi.Parser.SyntaxError) {
          throw new Error(uzi.Utils.formatParserError(e, input));
        } else {
          throw e;
        }
      }
    }

    describe("Expressions", function () {

      function parse(input) {
        return parseWith(input, "expression");
      }

      it("should parse a number", function () {
        var node = parse("100");
        expect(node.value).toEqual(100);
      });

      it("should parse a negative number", function () {
        var node = parse("-1");
        expect(node.value).toEqual(-1);
      });

      it("should parse decimal numbers", function () {
        var node = parse("1.1");
        expect(node.value).toEqual(1.1);
      });

      it("should parse negative decimal numbers", function () {
        var node = parse("-5.43");
        expect(node.value).toEqual(-5.43);
      });

      it("should parse a variable name", function () {
        var node = parse("$fo-o_");
        expect(node.value).toEqual("$fo-o_");
      });

      it("should parse basic arithmetic", function () {
        var node = parse("a + 1");
        expect(node.tag).toEqual("op");
        expect(node.op).toEqual("+");
        expect(node.left.value).toEqual("a");
        expect(node.right.value).toEqual(1);
      });

      it("should parse function calls", function () {
        var node = parse("foo(1)");
        expect(node.tag).toEqual("func_call");
        expect(node.name).toEqual("foo");
        expect(node.params[0].value).toEqual(1);
      });

      it("should parse function call with multiple parameters", function () {
        var node = parse("foo(1, 2)");
        expect(node.tag).toEqual("func_call");
        expect(node.params.length).toEqual(2);
        expect(node.params[0].value).toEqual(1);
        expect(node.params[1].value).toEqual(2);
      });

    });

    describe("StyleSheet", function () {

      function parse(input) {
        return parseWith(input, "stylesheet");
      }

      it("should parse comments", function () {
        var stylesheet = parse(" /* foo bar */ ");
        expect(stylesheet.rulesets.length).toEqual(0);
        expect(stylesheet.variables.length).toEqual(0);
      });

      it("should parse an empty commented ruleset", function () {
        var stylesheet = parse(" /* hi */ #A { /* cool */ } /* no */");
        expect(stylesheet.rulesets.length).toEqual(1);
        expect(stylesheet.variables.length).toEqual(0);
      });

    });

  });

  describe("System", function () {

    var system = new uzi.System();

    beforeEach(function () {
      system.reset();

      system.func("min", function (a, b) {
        return Math.min(a, b);
      });

      system.func("max", function (a, b) {
        return Math.max(a, b);
      });
    });

    describe("Relationships", function () {

      it("should do equality", function () {
        system.equals("A", "B");
        system.$.A = 321;
        expect(system.$.B).toEqual(321);
      });

      it("should do addition", function () {
        system.add("A", "B", "C");
        system.$.B = 1;
        system.$.C = 2;
        expect(system.$.A).toEqual(3);
      });

      it("should do subtraction", function () {
        system.subtract("A", "B", "C");
        system.$.B = 10;
        system.$.C = 5;
        expect(system.$.A).toEqual(5);
      });

      it("should do multiplication", function () {
        system.multiply("A", "B", "C");
        system.$.B = 5;
        system.$.C = 5;
        expect(system.$.A).toEqual(25);
      });

      it("should do division", function () {
        system.divide("A", "B", "C");
        system.$.B = 10;
        system.$.C = 2;
        expect(system.$.A).toEqual(5);
      });

    });

    describe("Expressions", function () {

      it("should correctly parse expressions", function () {
        system.$.A = "B + 1";
        system.$.B = 10;
        expect(system.$.A).toEqual(11);
      });

      it("should correctly parse complex expressions", function () {
        system.$.W = "R - L";
        system.$.C = "L + (W / 2)";
        system.$.L = 10;
        system.$.R = 20;
        expect(system.$.W).toEqual(10);
        expect(system.$.C).toEqual(15);
      });

      it("should correctly parse expression with negative numbers", function () {
        system.$.A = "-1 + B";
        system.$.B = 10;
        expect(system.$.A).toEqual(9);
      });

      it("should parse expressions with functions", function () {
        system.$.A = "1 + min(C, 5)";
        system.$.C = 3;
        expect(system.$.A).toEqual(4);
      });

    });

    describe("Variables", function () {

      it("should delete a variable", function () {
        system.$.A = 100;
        system.destroy("A");
        expect(system.has("A")).toBe(false);
      });

    });

  });

  describe("Engine", function () {

    var engine;
    var system;

    beforeEach(function () {
      engine = new uzi.Engine();
      system = engine.getSystem();

      system.func("add_five", function (x) {
        return x + 5;
      });

      engine.initialize();
      engine.updateNow();
    });

    afterEach(function () {
      engine.destroy();
    });

    it("should have applied the width style to A", function () {
      expect(system.get("A.width")).toEqual(100);
    });

    it("should have set B's height to half of A's width", function () {
      expect(system.get("B.height")).toEqual(50);
    });

    it("should work with variables", function () {
      system.set("x", 50);
      engine.updateNow();
      expect(system.get("A.height")).toEqual(100);
    });

    it("should work with basic user defined functions", function () {
      expect(system.get("B.left")).toEqual(10);
    });

    it("should destroy variables when they are not used anyore", function () {
      expect(system.has("B.top")).toBe(true);
      var bDiv = document.getElementById("B");
      engine.unmount(bDiv);
      expect(system.has("B.top")).toBe(false);
      expect(system.has("B.bottom")).toBe(false);
      expect(system.has("B.left")).toBe(false);
      expect(system.has("B.right")).toBe(false);
    });

    it("should find the actual values of B's unconstrained properties", function () {
      expect(system.get("B.width")).toEqual(10);
    });

    it("should parse inline styles", function () {
      expect(system.get("A.top")).toEqual(10);
    });

    it("should apply the correct classes", function () {
      expect(system.get("B.top")).toEqual(20);
    });

  });

});
<commit_msg>check the actual element position/size in the tests<commit_after>describe("uzi", function () {

  describe("Variable", function () {

    it("should not be able to set a value twice in the same digest", function () {
      var v = new uzi.Variable("name");

      v.setValue(123, 0);

      // can't set a value twice in the same digest
      expect(function () { v.setValue(321, 0); }).toThrow();

      // ok to set a new value in a new digest
      v.setValue(321, 1);
      expect(v.getValue()).toEqual(321);
    });

    it("should not be ok to destroy if it was assigned", function () {
      var v = new uzi.Variable("name");

      v.setValue(123, 0);
      expect(v.canDestroy()).toBe(true);

      v.assignValue(123);
      expect(v.canDestroy()).toBe(false);
    });

  });

  describe("GeneratedParser", function () {

    function parseWith(input, startRule) {
      try {
        return uzi.Parser.parse(input, { startRule: startRule });
      } catch (e) {
        if (e instanceof uzi.Parser.SyntaxError) {
          throw new Error(uzi.Utils.formatParserError(e, input));
        } else {
          throw e;
        }
      }
    }

    describe("Expressions", function () {

      function parse(input) {
        return parseWith(input, "expression");
      }

      it("should parse a number", function () {
        var node = parse("100");
        expect(node.value).toEqual(100);
      });

      it("should parse a negative number", function () {
        var node = parse("-1");
        expect(node.value).toEqual(-1);
      });

      it("should parse decimal numbers", function () {
        var node = parse("1.1");
        expect(node.value).toEqual(1.1);
      });

      it("should parse negative decimal numbers", function () {
        var node = parse("-5.43");
        expect(node.value).toEqual(-5.43);
      });

      it("should parse a variable name", function () {
        var node = parse("$fo-o_");
        expect(node.value).toEqual("$fo-o_");
      });

      it("should parse basic arithmetic", function () {
        var node = parse("a + 1");
        expect(node.tag).toEqual("op");
        expect(node.op).toEqual("+");
        expect(node.left.value).toEqual("a");
        expect(node.right.value).toEqual(1);
      });

      it("should parse function calls", function () {
        var node = parse("foo(1)");
        expect(node.tag).toEqual("func_call");
        expect(node.name).toEqual("foo");
        expect(node.params[0].value).toEqual(1);
      });

      it("should parse function call with multiple parameters", function () {
        var node = parse("foo(1, 2)");
        expect(node.tag).toEqual("func_call");
        expect(node.params.length).toEqual(2);
        expect(node.params[0].value).toEqual(1);
        expect(node.params[1].value).toEqual(2);
      });

    });

    describe("StyleSheet", function () {

      function parse(input) {
        return parseWith(input, "stylesheet");
      }

      it("should parse comments", function () {
        var stylesheet = parse(" /* foo bar */ ");
        expect(stylesheet.rulesets.length).toEqual(0);
        expect(stylesheet.variables.length).toEqual(0);
      });

      it("should parse an empty commented ruleset", function () {
        var stylesheet = parse(" /* hi */ #A { /* cool */ } /* no */");
        expect(stylesheet.rulesets.length).toEqual(1);
        expect(stylesheet.variables.length).toEqual(0);
      });

    });

  });

  describe("System", function () {

    var system = new uzi.System();

    beforeEach(function () {
      system.reset();

      system.func("min", function (a, b) {
        return Math.min(a, b);
      });

      system.func("max", function (a, b) {
        return Math.max(a, b);
      });
    });

    describe("Relationships", function () {

      it("should do equality", function () {
        system.equals("A", "B");
        system.$.A = 321;
        expect(system.$.B).toEqual(321);
      });

      it("should do addition", function () {
        system.add("A", "B", "C");
        system.$.B = 1;
        system.$.C = 2;
        expect(system.$.A).toEqual(3);
      });

      it("should do subtraction", function () {
        system.subtract("A", "B", "C");
        system.$.B = 10;
        system.$.C = 5;
        expect(system.$.A).toEqual(5);
      });

      it("should do multiplication", function () {
        system.multiply("A", "B", "C");
        system.$.B = 5;
        system.$.C = 5;
        expect(system.$.A).toEqual(25);
      });

      it("should do division", function () {
        system.divide("A", "B", "C");
        system.$.B = 10;
        system.$.C = 2;
        expect(system.$.A).toEqual(5);
      });

    });

    describe("Expressions", function () {

      it("should correctly parse expressions", function () {
        system.$.A = "B + 1";
        system.$.B = 10;
        expect(system.$.A).toEqual(11);
      });

      it("should correctly parse complex expressions", function () {
        system.$.W = "R - L";
        system.$.C = "L + (W / 2)";
        system.$.L = 10;
        system.$.R = 20;
        expect(system.$.W).toEqual(10);
        expect(system.$.C).toEqual(15);
      });

      it("should correctly parse expression with negative numbers", function () {
        system.$.A = "-1 + B";
        system.$.B = 10;
        expect(system.$.A).toEqual(9);
      });

      it("should parse expressions with functions", function () {
        system.$.A = "1 + min(C, 5)";
        system.$.C = 3;
        expect(system.$.A).toEqual(4);
      });

    });

    describe("Variables", function () {

      it("should delete a variable", function () {
        system.$.A = 100;
        system.destroy("A");
        expect(system.has("A")).toBe(false);
      });

    });

  });

  describe("Engine", function () {

    var engine;
    var system;

    beforeEach(function () {
      engine = new uzi.Engine();
      system = engine.getSystem();

      system.func("add_five", function (x) {
        return x + 5;
      });

      engine.initialize();
      engine.updateNow();
    });

    afterEach(function () {
      engine.destroy();
    });

    function getRect(id) {
      var el = document.getElementById(id);
      return el.getBoundingClientRect();
    }

    it("should have applied the width style to A", function () {
      expect(system.get("A.width")).toEqual(100);
      expect(getRect("A").width).toEqual(100);
    });

    it("should have set B's height to half of A's width", function () {
      expect(system.get("B.height")).toEqual(50);
      expect(getRect("B").height).toEqual(50);
    });

    it("should work with variables", function () {
      system.set("x", 50);
      engine.updateNow();
      expect(system.get("A.height")).toEqual(100);
      expect(getRect("A").height).toEqual(100);
    });

    it("should work with basic user defined functions", function () {
      expect(system.get("B.left")).toEqual(10);
      expect(getRect("B").left).toEqual(10);
    });

    it("should destroy variables when they are not used anyore", function () {
      expect(system.has("B.top")).toBe(true);
      var bDiv = document.getElementById("B");
      engine.unmount(bDiv);
      expect(system.has("B.top")).toBe(false);
      expect(system.has("B.bottom")).toBe(false);
      expect(system.has("B.left")).toBe(false);
      expect(system.has("B.right")).toBe(false);
    });

    it("should find the actual values of B's unconstrained properties", function () {
      expect(system.get("B.width")).toEqual(10);
      expect(getRect("B").width).toEqual(10);
    });

    it("should parse inline styles", function () {
      expect(system.get("A.top")).toEqual(10);
      expect(getRect("A").top).toEqual(10);
    });

    it("should apply the correct classes", function () {
      expect(system.get("B.top")).toEqual(20);
      expect(getRect("B").top).toEqual(20);
    });

  });

});
<|endoftext|>