<commit_before>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

function Cart(module) {

    var self;
    var config;
    var container;
    var template;
    var items = [];

    function processConfig(config) {
        config.options = config.options || {};
        config.options.type = config.options.type || "cookie";

        if (config.options.type === "cookie") {
            config.options.cookie = "cart";
        }

        config.options.priceKey = config.options.priceKey || "price";
        config.options.quantityKey = config.options.quantityKey || "quantity";

        config.template.binds = config.template.binds || [];

        var optClasses = config.options.classes || {}
        optClasses.item = optClasses.item || "item";
        optClasses.empty = optClasses.empty || "empty";
        optClasses.content = optClasses.content || "content";
        optClasses.confirm = optClasses.config || "confirm";
        config.options.classes = optClasses;

        return config;
    }

    function init(conf) {

        // initialize the globals
        self = this;
        config = processConfig(conf);
        if (config.container) {
            container = $(config.container, module.dom);
        } else {
            container = module.dom;
        }
        template = $(config.template.value, module.dom);

        // **************************************
        // generate general binds from the config
        var binds = [];

        for (var i in config.controls) {
            switch (i) {
                case "add":
                    binds.push({
                        target: config.controls[i],
                        context: ".controls",
                        on: [{
                            name: "click",
                            emit: "requestNewItem"
                        }]
                    });
                    break;
                case "delete":
                    binds.push({
                        target: config.controls[i],
                        context: ".controls",
                        on: [{
                            name: "click",
                            handler: "removeSelected"
                        }]
                    });
                    break;
            }
        }

        // run the internal binds
        for (var i in binds) {
            Bind.call(self, binds[i]);
        }

        // run the binds
        for (var i in config.binds) {
            Bind.call(self, config.binds[i]);
        }

        Events.call(self, config);

        showEmpty(true);

        self.read();
    }

    // TODO call this from a checkout operation link response if success
    function checkoutDone() {
        clearList();
        items = [];
        if (config.options.type === "cookie") {
            $.cookie.json = true;
            $.cookie("cart", items, { path: "/" });
        }

        showError();
        showOrdered();
    }

    function checkout() {

        var agb = $(".agb", self.dom).find("input").andSelf().find("input[type='checkbox']");
        if (agb.length) {
            if (agb.is(":checked")) {
                agb.first().prop('checked', false);
                checkoutDone();
            }
            else {
                showError("Please agree to the terms and conditions");
            }
            return;
        }
        checkoutDone();
    }

    var renderers = {
        selector: function(item) {
            // TODO where do we know the key is the _id
            var existingItem = container.find("#" + item._id);

            // new item type in the cart
            if (!existingItem.length) {

                var newItem = $(template).clone();
                newItem
                    .removeClass("template")
                    .addClass(config.options.classes.item)
                    .appendTo(container)
                    .show();

                for (var i in config.template.binds) {
                    var bindObj = config.template.binds[i];
                    bindObj.context = newItem;
                    Bind.call(self, bindObj, item);
                }

                if (item.amountError) {

                    debugger;
                    showError(item.amountError);

                    // var p = $("<p>");
                    // p.text(item.amountError);
                    // p.addClass("alert")
                    //  .addClass("alert-error");
                    // newItem.after(p);
                }

                // now there should be an element with this id
                existingItem = container.find("#" + item._id);
            }

            var qKey = config.options.quantityKey;
            existingItem.find(".quantity").each(function() {

                var elem = $(this);

                switch (this.tagName) {
                    case "INPUT":
                        var initialAdd = elem.val() == 0;
                        elem.attr("value", item[qKey]);
                        elem.val(item[qKey]);
                        if (initialAdd) {
                            elem.on("change", function() {
                                blockCart();
                                var newVal = parseInt($(this).val());
                                if (isNaN(newVal) || newVal < 0) {
                                    $(this).val(item.quantity);
                                    unblockCart();
                                } else if (newVal === 0) {
                                    existingItem.fadeOut(function() {
                                        item.quantity = newVal;
                                        removeItem(item);
                                        unblockCart();
                                    });
                                } else {
                                    item.quantity = newVal;
                                    updateItem(item);
                                }
                            });
                        }
                        break;
                    default:
                        elem.text(item[qKey]);
                }
            });

            return;
        }
    };

    function blockCart() {
        container.find('input').prop('disabled', true);
    }

    function unblockCart() {
        container.find('input').prop('disabled', false);
    }

    function render(items) {
        if (!items || !items.length) {
            showEmpty(true);
            return;
        }

        showEmpty(false);

        var renderer = renderers[config.template.type];
        if (!renderer) {
            showError("No renderer found for templates of type: " + config.template.type);
        }
        for (var i in items) {
            renderer.call(self, items[i]);
        }
    }

    function renderItem(item) {

        if (!item) {
            return;
        }

        var renderer = renderers[config.template.type];
        if (!renderer) {
            showError("No renderer found for templates of type: " + config.template.type);
        }
        showEmpty(false);
        renderer.call(self, item);
    }

    function clearList() {
        $("." + config.options.classes.item, container).remove();
    }

    // ********************************
    // Public functions ***************
    // ********************************

    var readFrom = {
        memory: function(callback) {
            callback(null, items);
        },
        cookie: function(callback) {
            $.cookie.json = true;
            items = $.cookie(config.options.cookie) || [];
            callback(null, items);
        },
        server: function(callback) {
            var options = {
                data: {
                    amount: config.options.amount
                }
            };

            self.link(config.crud.read, options, function(err, data) {
                if (err) { return callback(err); }
                items = [];
                for (var i in data) {
                    items.push(data[i]);
                }
                callback(null, items);
            });
        }
    }

    function read() {

        clearList();

        var reader = readFrom[config.options.type];
        if (reader) {
            reader.call(self, function(err, items) {
                if (err) {
                    showError(JSON.stringify(err));
                    return;
                }
                render.call(self, items);
                updateTotal();
            });
        }
    }

    function showEmpty(empty) {
        if (empty) {
            $("." + config.options.classes.empty, self.dom).show();
            $("." + config.options.classes.content, self.dom).hide();
            $("." + config.options.classes.confirm, self.dom).hide();
            $(".agb", self.dom).find("input[type='checkbox']").first().prop('checked', false);
        } else {
            $("." + config.options.classes.empty, self.dom).hide();
            $("." + config.options.classes.content, self.dom).show();
            $("." + config.options.classes.confirm, self.dom).hide();
        }
    }

    function showOrdered() {
        $("." + config.options.classes.empty, self.dom).hide();
        $("." + config.options.classes.content, self.dom).hide();
        $("." + config.options.classes.confirm, self.dom).show();
    }

    function showError(message) {
        if (!message) {
            $(".error").text("").hide();
        } else {
            $(".error").text(message).show();
        }
    }

    var adders = {
        cookie: function(item, callback) {
            $.cookie.json = true;
            var cart = $.cookie("cart") || [];
            var found = false;
            for (var i in cart) {
                if (cart[i]._id === item._id) {
                    // TODO support incomming item.quantity
                    ++(cart[i][config.options.quantityKey]);
                    item = cart[i];
                    found = true;
                    break;
                }
            }
            if (!found) {
                item[config.options.quantityKey] = 1;
                cart.push(item);
                item = cart[cart.length - 1];
            }

            $.cookie.json = true;
            $.cookie("cart", cart, { path: "/" });
            items = cart;

            callback(null, item);
        },

        server: function(item, callback) {
            self.link(config.crud.create, { data: item }, function(err, confirmedItem) {
                if (err) { return callback(err); }

                var found = false;
                for (var i in items) {
                    if (items[i]._id === confirmedItem._id) {
                        items[i] = confirmedItem;
                        found = true;
                    }
                }

                if (!found) {
                    items.push(confirmedItem);
                }

                callback(null, confirmedItem);
            });
        },

        memory: function(item, callback) {
            // TODO in memory type the quantity is saved in the memory object and will
            // be reused later, which is wrong
            var newItem = {};
            var qKey = config.options.quantityKey;

            for (var i in item) {
                newItem[i] = item[i];
            }
            item = newItem;

            var found = false;
            for (var i in items) {
                if (items[i]._id === item._id) {
                    item = items[i];
                    ++(item[qKey]);
                    found = true;
                }
            }

            if (!found) {
                item[qKey] = 1;
                items.push(item);
            }

            callback(null, item);
        }
    };

    function addItem(itemData) {
        showError();

        var adder = adders[config.options.type];
        if (!adder) {
            showError("No item adder was found for cart type: " + config.options.type);
            return;
        }
        adder.call(self, itemData, function(err, item) {
            if (err) {
                showError(JSON.stringify(err));
                return;
            }
            renderItem(item);
            updateTotal();
        });
    }

    function updateTotal() {
        var total = 0;
        for (var i in items) {
            var priceStr = items[i][config.options.priceKey];
            var quantityStr = items[i][config.options.quantityKey];
            var price = parseInt(priceStr);
            var quantity = parseInt(quantityStr);

            if (!isNaN(price) && !isNaN(quantity)) {
                total += price * quantity;
            }
        }
        $("#total", self.dom).text(total.toFixed(2));
    }

    function readQuantityFromItem(elem) {

        var quantityElem = elem.find(".quantity").first();
        if (!quantityElem.length) {
            return 1;
        }

        var value = 1;
        var textVal = null;

        switch (quantityElem[0].tagName) {
            case "INPUT":
                textVal = quantityElem.val();
                break;
            default:
                textVal = quantityElem.text();
        }
        var parsedVal = parseInt(textVal);
        if (!isNaN(parsedVal)) {
            value = parsedVal + 1;
        }

        return value;
    }

    function emptyCart() {
        $("." + config.options.classes.item, self.dom).each(function() {
            var item = {
                _id: $(this).attr("id")
            }
            removeItem(item);
        });
    }

    function updateItem(item) {
        self.link(config.crud.update, { data: item }, function (err, data) {
            unblockCart();
            showError(err);
            updateTotal();
        });
    }

    function removeItem(itemData) {
        showError();

        function removeUiItem() {
            // TODO where do we get the _id key from?
            container.find("#" + itemData._id).remove();
            updateTotal();

            // if no more items, show empty cart
            if (container.find("." + config.options.classes.item).length == 0) {
                showEmpty(true);
            }
        }

        function removeCacheItem() {
            var newItems = [];
            for (var i in items) {
                if (items[i]._id !== itemData._id) {
                    newItems.push(items[i]);
                }
            }
            items = newItems;
            updateTotal();
        }

        switch (config.options.type) {
            case "server":
                self.link(config.crud.delete, { data: { _id: itemData._id } }, function(err, data) {
                    if (err) { return; }
                    removeUiItem();
                    removeCacheItem();
                });
                break;

            case "memory":
                // a memory-based cart should not make any requests, just remove the item
                removeUiItem();
                removeCacheItem();
                // and show an empty cart when no more items in it
                var itemElems = container.find("." + config.options.classes.item);
                showEmpty(!itemElems.length);
                break;

            case "cookie":
                // search in the cart cookie for the item with this ID and remove it
                var cart = $.cookie("cart");
                for (var i in cart) {
                    if (cart[i]._id === itemData._id) {
                        cart.splice(i, 1);
                        break;
                    }
                }
                // if the cart is already empty it should be displayed as so
                showEmpty(!cart || !cart.length);
                // save back the updated cart in the cookie
                $.cookie("cart", cart, { path: "/"});
                // and also remove the item from the UI
                removeUiItem();
                removeCacheItem();
                break;
        }
    }

    function show() {
        $(self.dom).parent().show();
    }

    function hide() {
        $(self.dom).parent().hide();
    }

     return {
        init: init,
        read: read,
        addItem: addItem,
        updateItem: updateItem,
        removeItem: removeItem,
        emptyCart: emptyCart,
        checkout: checkout,
        show: show,
        hide: hide
    };
}

module.exports = function(module, config) {

    var cart = new Cart(module);
    for (var i in cart) {
        cart[i] = module[i] || cart[i];
    }
    cart = Object.extend(cart, module);

    cart.init(config);

    return cart;
};
<commit_msg>the cart considers now the prices in cents<commit_after>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

function Cart(module) {

    var self;
    var config;
    var container;
    var template;
    var items = [];

    function processConfig(config) {
        config.options = config.options || {};
        config.options.type = config.options.type || "cookie";

        if (config.options.type === "cookie") {
            config.options.cookie = "cart";
        }

        config.options.priceKey = config.options.priceKey || "price";
        config.options.quantityKey = config.options.quantityKey || "quantity";

        config.template.binds = config.template.binds || [];

        var optClasses = config.options.classes || {}
        optClasses.item = optClasses.item || "item";
        optClasses.empty = optClasses.empty || "empty";
        optClasses.content = optClasses.content || "content";
        optClasses.confirm = optClasses.config || "confirm";
        config.options.classes = optClasses;

        return config;
    }

    function init(conf) {

        // initialize the globals
        self = this;
        config = processConfig(conf);
        if (config.container) {
            container = $(config.container, module.dom);
        } else {
            container = module.dom;
        }
        template = $(config.template.value, module.dom);

        // **************************************
        // generate general binds from the config
        var binds = [];

        for (var i in config.controls) {
            switch (i) {
                case "add":
                    binds.push({
                        target: config.controls[i],
                        context: ".controls",
                        on: [{
                            name: "click",
                            emit: "requestNewItem"
                        }]
                    });
                    break;
                case "delete":
                    binds.push({
                        target: config.controls[i],
                        context: ".controls",
                        on: [{
                            name: "click",
                            handler: "removeSelected"
                        }]
                    });
                    break;
            }
        }

        // run the internal binds
        for (var i in binds) {
            Bind.call(self, binds[i]);
        }

        // run the binds
        for (var i in config.binds) {
            Bind.call(self, config.binds[i]);
        }

        Events.call(self, config);

        showEmpty(true);

        self.read();
    }

    // TODO call this from a checkout operation link response if success
    function checkoutDone() {
        clearList();
        items = [];
        if (config.options.type === "cookie") {
            $.cookie.json = true;
            $.cookie("cart", items, { path: "/" });
        }

        showError();
        showOrdered();
    }

    function checkout() {

        var agb = $(".agb", self.dom).find("input").andSelf().find("input[type='checkbox']");
        if (agb.length) {
            if (agb.is(":checked")) {
                agb.first().prop('checked', false);
                checkoutDone();
            }
            else {
                showError("Please agree to the terms and conditions");
            }
            return;
        }
        checkoutDone();
    }

    var renderers = {
        selector: function(item) {
            // TODO where do we know the key is the _id
            var existingItem = container.find("#" + item._id);

            // new item type in the cart
            if (!existingItem.length) {

                var newItem = $(template).clone();
                newItem
                    .removeClass("template")
                    .addClass(config.options.classes.item)
                    .appendTo(container)
                    .show();

                for (var i in config.template.binds) {
                    var bindObj = config.template.binds[i];
                    bindObj.context = newItem;
                    Bind.call(self, bindObj, item);
                }

                if (item.amountError) {

                    debugger;
                    showError(item.amountError);

                    // var p = $("<p>");
                    // p.text(item.amountError);
                    // p.addClass("alert")
                    //  .addClass("alert-error");
                    // newItem.after(p);
                }

                // now there should be an element with this id
                existingItem = container.find("#" + item._id);
            }

            var qKey = config.options.quantityKey;
            existingItem.find(".quantity").each(function() {

                var elem = $(this);

                switch (this.tagName) {
                    case "INPUT":
                        var initialAdd = elem.val() == 0;
                        elem.attr("value", item[qKey]);
                        elem.val(item[qKey]);
                        if (initialAdd) {
                            elem.on("change", function() {
                                blockCart();
                                var newVal = parseInt($(this).val());
                                if (isNaN(newVal) || newVal < 0) {
                                    $(this).val(item.quantity);
                                    unblockCart();
                                } else if (newVal === 0) {
                                    existingItem.fadeOut(function() {
                                        item.quantity = newVal;
                                        removeItem(item);
                                        unblockCart();
                                    });
                                } else {
                                    item.quantity = newVal;
                                    updateItem(item);
                                }
                            });
                        }
                        break;
                    default:
                        elem.text(item[qKey]);
                }
            });

            return;
        }
    };

    function blockCart() {
        container.find('input').prop('disabled', true);
    }

    function unblockCart() {
        container.find('input').prop('disabled', false);
    }

    function render(items) {
        if (!items || !items.length) {
            showEmpty(true);
            return;
        }

        showEmpty(false);

        var renderer = renderers[config.template.type];
        if (!renderer) {
            showError("No renderer found for templates of type: " + config.template.type);
        }
        for (var i in items) {
            renderer.call(self, items[i]);
        }
    }

    function renderItem(item) {

        if (!item) {
            return;
        }

        var renderer = renderers[config.template.type];
        if (!renderer) {
            showError("No renderer found for templates of type: " + config.template.type);
        }
        showEmpty(false);
        renderer.call(self, item);
    }

    function clearList() {
        $("." + config.options.classes.item, container).remove();
    }

    // ********************************
    // Public functions ***************
    // ********************************

    var readFrom = {
        memory: function(callback) {
            callback(null, items);
        },
        cookie: function(callback) {
            $.cookie.json = true;
            items = $.cookie(config.options.cookie) || [];
            callback(null, items);
        },
        server: function(callback) {
            var options = {
                data: {
                    amount: config.options.amount
                }
            };

            self.link(config.crud.read, options, function(err, data) {
                if (err) { return callback(err); }
                items = [];
                for (var i in data) {
                    items.push(data[i]);
                }
                callback(null, items);
            });
        }
    }

    function read() {

        clearList();

        var reader = readFrom[config.options.type];
        if (reader) {
            reader.call(self, function(err, items) {
                if (err) {
                    showError(JSON.stringify(err));
                    return;
                }
                render.call(self, items);
                updateTotal();
            });
        }
    }

    function showEmpty(empty) {
        if (empty) {
            $("." + config.options.classes.empty, self.dom).show();
            $("." + config.options.classes.content, self.dom).hide();
            $("." + config.options.classes.confirm, self.dom).hide();
            $(".agb", self.dom).find("input[type='checkbox']").first().prop('checked', false);
        } else {
            $("." + config.options.classes.empty, self.dom).hide();
            $("." + config.options.classes.content, self.dom).show();
            $("." + config.options.classes.confirm, self.dom).hide();
        }
    }

    function showOrdered() {
        $("." + config.options.classes.empty, self.dom).hide();
        $("." + config.options.classes.content, self.dom).hide();
        $("." + config.options.classes.confirm, self.dom).show();
    }

    function showError(message) {
        if (!message) {
            $(".error").text("").hide();
        } else {
            $(".error").text(message).show();
        }
    }

    var adders = {
        cookie: function(item, callback) {
            $.cookie.json = true;
            var cart = $.cookie("cart") || [];
            var found = false;
            for (var i in cart) {
                if (cart[i]._id === item._id) {
                    // TODO support incomming item.quantity
                    ++(cart[i][config.options.quantityKey]);
                    item = cart[i];
                    found = true;
                    break;
                }
            }
            if (!found) {
                item[config.options.quantityKey] = 1;
                cart.push(item);
                item = cart[cart.length - 1];
            }

            $.cookie.json = true;
            $.cookie("cart", cart, { path: "/" });
            items = cart;

            callback(null, item);
        },

        server: function(item, callback) {
            self.link(config.crud.create, { data: item }, function(err, confirmedItem) {
                if (err) { return callback(err); }

                var found = false;
                for (var i in items) {
                    if (items[i]._id === confirmedItem._id) {
                        items[i] = confirmedItem;
                        found = true;
                    }
                }

                if (!found) {
                    items.push(confirmedItem);
                }

                callback(null, confirmedItem);
            });
        },

        memory: function(item, callback) {
            // TODO in memory type the quantity is saved in the memory object and will
            // be reused later, which is wrong
            var newItem = {};
            var qKey = config.options.quantityKey;

            for (var i in item) {
                newItem[i] = item[i];
            }
            item = newItem;

            var found = false;
            for (var i in items) {
                if (items[i]._id === item._id) {
                    item = items[i];
                    ++(item[qKey]);
                    found = true;
                }
            }

            if (!found) {
                item[qKey] = 1;
                items.push(item);
            }

            callback(null, item);
        }
    };

    function addItem(itemData) {
        showError();

        var adder = adders[config.options.type];
        if (!adder) {
            showError("No item adder was found for cart type: " + config.options.type);
            return;
        }
        adder.call(self, itemData, function(err, item) {
            if (err) {
                showError(JSON.stringify(err));
                return;
            }
            renderItem(item);
            updateTotal();
        });
    }

    function updateTotal() {
        var total = 0;
        for (var i in items) {
            var priceStr = items[i][config.options.priceKey];
            var quantityStr = items[i][config.options.quantityKey];
            var price = parseInt(priceStr) / 100;
            var quantity = parseInt(quantityStr);

            if (!isNaN(price) && !isNaN(quantity)) {
                total += price * quantity;
            }
        }
        $("#total", self.dom).text(total.toFixed(2));
    }

    function readQuantityFromItem(elem) {

        var quantityElem = elem.find(".quantity").first();
        if (!quantityElem.length) {
            return 1;
        }

        var value = 1;
        var textVal = null;

        switch (quantityElem[0].tagName) {
            case "INPUT":
                textVal = quantityElem.val();
                break;
            default:
                textVal = quantityElem.text();
        }
        var parsedVal = parseInt(textVal);
        if (!isNaN(parsedVal)) {
            value = parsedVal + 1;
        }

        return value;
    }

    function emptyCart() {
        $("." + config.options.classes.item, self.dom).each(function() {
            var item = {
                _id: $(this).attr("id")
            }
            removeItem(item);
        });
    }

    function updateItem(item) {
        self.link(config.crud.update, { data: item }, function (err, data) {
            unblockCart();
            showError(err);
            updateTotal();
        });
    }

    function removeItem(itemData) {
        showError();

        function removeUiItem() {
            // TODO where do we get the _id key from?
            container.find("#" + itemData._id).remove();
            updateTotal();

            // if no more items, show empty cart
            if (container.find("." + config.options.classes.item).length == 0) {
                showEmpty(true);
            }
        }

        function removeCacheItem() {
            var newItems = [];
            for (var i in items) {
                if (items[i]._id !== itemData._id) {
                    newItems.push(items[i]);
                }
            }
            items = newItems;
            updateTotal();
        }

        switch (config.options.type) {
            case "server":
                self.link(config.crud.delete, { data: { _id: itemData._id } }, function(err, data) {
                    if (err) { return; }
                    removeUiItem();
                    removeCacheItem();
                });
                break;

            case "memory":
                // a memory-based cart should not make any requests, just remove the item
                removeUiItem();
                removeCacheItem();
                // and show an empty cart when no more items in it
                var itemElems = container.find("." + config.options.classes.item);
                showEmpty(!itemElems.length);
                break;

            case "cookie":
                // search in the cart cookie for the item with this ID and remove it
                var cart = $.cookie("cart");
                for (var i in cart) {
                    if (cart[i]._id === itemData._id) {
                        cart.splice(i, 1);
                        break;
                    }
                }
                // if the cart is already empty it should be displayed as so
                showEmpty(!cart || !cart.length);
                // save back the updated cart in the cookie
                $.cookie("cart", cart, { path: "/"});
                // and also remove the item from the UI
                removeUiItem();
                removeCacheItem();
                break;
        }
    }

    function show() {
        $(self.dom).parent().show();
    }

    function hide() {
        $(self.dom).parent().hide();
    }

     return {
        init: init,
        read: read,
        addItem: addItem,
        updateItem: updateItem,
        removeItem: removeItem,
        emptyCart: emptyCart,
        checkout: checkout,
        show: show,
        hide: hide
    };
}

module.exports = function(module, config) {

    var cart = new Cart(module);
    for (var i in cart) {
        cart[i] = module[i] || cart[i];
    }
    cart = Object.extend(cart, module);

    cart.init(config);

    return cart;
};

<|endoftext|>


<commit_before>/**
 * Created by Samuel Gratzl on 08.06.2015.
 */

define(['exports', '../caleydo_web/main'], function (exports, C) {
  exports.list = function () {
    return C.getAPIJSON('/ccle');
  };
  exports.data = function (dataset, row_ids, col_ids, method) {
    row_ids = row_ids || [];
    col_ids = col_ids || [];
    method = method || 'data';
    var param = {};
    if (row_ids.length > 0) {
      param['rows'] = row_ids;
    }
    if (col_ids.length > 0) {
      param['cols'] = col_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/'+method, param);
  };
  exports.stats = function(dataset, row_ids, col_ids) {
    return exports.data(dataset, row_ids, col_ids, 'stats');
  };
  exports.rows = function (dataset, row_ids) {
    row_ids = row_ids || [];
    var param = {};
    if (row_ids.length > 0) {
      param['rows'] = row_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/rows', param);
  };
  exports.cols = function (dataset, col_ids) {
    col_ids = col_ids || [];
    var param = {};
    if (row_ids.length > 0) {
      param['cols'] = col_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/cols', param);
  };
  exports.group = function (dataset, group) {
    if (typeof group !== 'undefined') {
      return C.getAPIJSON('/ccle/' + dataset + '/group/'+group);
    } else {
      return C.getAPIJSON('/ccle/' + dataset + '/group');
    }
  };

  function boxplotImpl(genes, summaryOfGroups) {
    genes = genes || null;
    summaryOfGroups = summaryOfGroups || null;
    var param = {};
    if (genes) {
      param['g'] = genes.join(',');
    }
    if (summaryOfGroups) {
      param['groups'] = summaryOfGroups
    }
    return C.getAPIJSON('/ccle/boxplot', param);
  }

  var cache = {};

  function resolveImpl(key, summaryOfGroups) {
    var t = cache[key];
    var promise = boxplotImpl(t.to_be_queried.map(function(d) { return d.gene; }), summaryOfGroups);
    var sent = t.to_be_queried;
    sent.forEach(function(g) {
      t.cache[g.gene] = promise;
    });
    promise.then(function(output) {
      sent.forEach(function(g) {
        g.callback(output[g.gene]);
      });
    });
    t.to_be_queried = [];
  }
  exports.boxplot_of = function(gene, callback, summaryOfGroups) {
    var key = summaryOfGroups ? summaryOfGroups.join('_') : '_individual';
    var t;
    if (key in cache) {
      t = cache[key]
    } else {
      t = { to_be_queried : [], cache : {}, wait_timer : -1};
      cache[key] = t
    }
    if (gene in t.cache) {
      t.cache[gene].then(function(output) {
        callback(output[gene]);
      });
    } else {
      t.to_be_queried.push({ gene: gene, callback: callback});
      if (t.wait_timer > 0) {
        clearTimeout(t.wait_timer);
        t.wait_timer = -1;
      }
      if (t.to_be_queried.length > 10) {
        resolveImpl(key, summaryOfGroups)
      } else {
        t.wait_timer = setTimeout(function() {
          resolveImpl(key, summaryOfGroups)
        }, 200); //wait and collect 200 milliseconds
      }
    }
  };
});<commit_msg>rename caleydo_web to caleydo_core<commit_after>/**
 * Created by Samuel Gratzl on 08.06.2015.
 */

define(['exports', '../caleydo_core/main'], function (exports, C) {
  exports.list = function () {
    return C.getAPIJSON('/ccle');
  };
  exports.data = function (dataset, row_ids, col_ids, method) {
    row_ids = row_ids || [];
    col_ids = col_ids || [];
    method = method || 'data';
    var param = {};
    if (row_ids.length > 0) {
      param['rows'] = row_ids;
    }
    if (col_ids.length > 0) {
      param['cols'] = col_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/'+method, param);
  };
  exports.stats = function(dataset, row_ids, col_ids) {
    return exports.data(dataset, row_ids, col_ids, 'stats');
  };
  exports.rows = function (dataset, row_ids) {
    row_ids = row_ids || [];
    var param = {};
    if (row_ids.length > 0) {
      param['rows'] = row_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/rows', param);
  };
  exports.cols = function (dataset, col_ids) {
    col_ids = col_ids || [];
    var param = {};
    if (row_ids.length > 0) {
      param['cols'] = col_ids;
    }
    return C.getAPIJSON('/ccle/' + dataset + '/cols', param);
  };
  exports.group = function (dataset, group) {
    if (typeof group !== 'undefined') {
      return C.getAPIJSON('/ccle/' + dataset + '/group/'+group);
    } else {
      return C.getAPIJSON('/ccle/' + dataset + '/group');
    }
  };

  function boxplotImpl(genes, summaryOfGroups) {
    genes = genes || null;
    summaryOfGroups = summaryOfGroups || null;
    var param = {};
    if (genes) {
      param['g'] = genes.join(',');
    }
    if (summaryOfGroups) {
      param['groups'] = summaryOfGroups
    }
    return C.getAPIJSON('/ccle/boxplot', param);
  }

  var cache = {};

  function resolveImpl(key, summaryOfGroups) {
    var t = cache[key];
    var promise = boxplotImpl(t.to_be_queried.map(function(d) { return d.gene; }), summaryOfGroups);
    var sent = t.to_be_queried;
    sent.forEach(function(g) {
      t.cache[g.gene] = promise;
    });
    promise.then(function(output) {
      sent.forEach(function(g) {
        g.callback(output[g.gene]);
      });
    });
    t.to_be_queried = [];
  }
  exports.boxplot_of = function(gene, callback, summaryOfGroups) {
    var key = summaryOfGroups ? summaryOfGroups.join('_') : '_individual';
    var t;
    if (key in cache) {
      t = cache[key]
    } else {
      t = { to_be_queried : [], cache : {}, wait_timer : -1};
      cache[key] = t
    }
    if (gene in t.cache) {
      t.cache[gene].then(function(output) {
        callback(output[gene]);
      });
    } else {
      t.to_be_queried.push({ gene: gene, callback: callback});
      if (t.wait_timer > 0) {
        clearTimeout(t.wait_timer);
        t.wait_timer = -1;
      }
      if (t.to_be_queried.length > 10) {
        resolveImpl(key, summaryOfGroups)
      } else {
        t.wait_timer = setTimeout(function() {
          resolveImpl(key, summaryOfGroups)
        }, 200); //wait and collect 200 milliseconds
      }
    }
  };
});<|endoftext|>


<commit_before>'use strict';
var usersMetadata = require('../../../../src/routes/users').metadata;
var itemsMetadata = require('../../../../src/routes/users/items').metadata;

var specification = {
    title: "User Items",
    baseUrl: '/users/:' + usersMetadata.identifierName + '/items',
    metadata: itemsMetadata,
    cases: {
        "Creation": {
            "Happy": {},
            'Fails if empty': {},
            'No Auth Header': {}
        },
        "Retrieval": {
            verb: 'GET',
            "By Id": {
                urlTemplate: '/:' + itemsMetadata.identifierName,
                "Happy": {
                    urlData: {}
                }
            },
            "Query": {}
        },
        "Update": {
            verb: 'PUT',
            "Replace": {
                urlTemplate: '/:' + itemsMetadata.identifierName,
                "Happy": {
                    statusCode: 204,
                    urlData: {},
                    send: 'generate-update',
                    result: 'success'
                }
            }
        }
    }
};
var updateHappy = specification.cases.Update.Replace.Happy;
var getByIdHappy = specification.cases.Retrieval['By Id'].Happy;
setUserId(getByIdHappy);
setUserId(updateHappy);
setUserId(specification.cases.Creation.Happy);
setUserId(specification.cases.Creation['Fails if empty']);
setUserId(specification.cases.Creation['No Auth Header']);
setUserId(specification.cases.Retrieval.Query);
setUserId(specification.cases.Update.Replace);
//setting itemId
setItemId(getByIdHappy, 'item1');
setItemId(updateHappy, 'item1');
module.exports = specification;

function setUserId(testCaseSpec) {
    testCaseSpec.urlData = testCaseSpec.urlData || {};
    testCaseSpec.urlData[usersMetadata.identifierName] = '580d9f45622d510b044fb6a8';
}

function setItemId(testCaseSpec, itemId) {
    testCaseSpec.urlData = testCaseSpec.urlData || {};
    testCaseSpec.urlData[itemsMetadata.identifierName] = itemId;
}<commit_msg>cleaning up.<commit_after>'use strict';
var usersMetadata = require('../../../../src/routes/users').metadata;
var itemsMetadata = require('../../../../src/routes/users/items').metadata;

var specification = {
    title: "User Items",
    baseUrl: '/users/:' + usersMetadata.identifierName + '/items',
    metadata: itemsMetadata,
    cases: {
        "Creation": {
            "Happy": {},
            'Fails if empty': {},
            'No Auth Header': {}
        },
        "Retrieval": {
            verb: 'GET',
            "By Id": {
                urlTemplate: '/:' + itemsMetadata.identifierName,
                "Happy": {
                    urlData: {}
                }
            },
            "Query": {}
        },
        "Update": {
            verb: 'PUT',
            "Replace": {
                urlTemplate: '/:' + itemsMetadata.identifierName,
                "Happy": {
                    statusCode: 204,
                    urlData: {},
                    send: 'generate-update',
                    result: 'success'
                }
            }
        }
    }
};
var updateHappy = specification.cases.Update.Replace.Happy;
var getByIdHappy = specification.cases.Retrieval['By Id'].Happy;
setUserId(getByIdHappy);
setUserId(updateHappy);
setUserId(specification.cases.Creation.Happy);
setUserId(specification.cases.Creation['Fails if empty']);
setUserId(specification.cases.Creation['No Auth Header']);
setUserId(specification.cases.Retrieval.Query);
setUserId(specification.cases.Update.Replace);
//setting itemId
setItemId(getByIdHappy);
setItemId(updateHappy);
module.exports = specification;

function setUserId(testCaseSpec, userId) {
    if (userId === undefined) {
        userId = '580d9f45622d510b044fb6a8';
    }
    testCaseSpec.urlData = testCaseSpec.urlData || {};
    testCaseSpec.urlData[usersMetadata.identifierName] = userId;
}

function setItemId(testCaseSpec, itemId) {
    if (itemId === undefined) {
        itemId = 'item1';
    }
    testCaseSpec.urlData = testCaseSpec.urlData || {};
    testCaseSpec.urlData[itemsMetadata.identifierName] = itemId;
}<|endoftext|>


<commit_before>import {sharedIdSystemSubmodule, storage} from 'modules/sharedIdSystem.js';
import {coppaDataHandler} from 'src/adapterManager';

import sinon from 'sinon';
import * as utils from 'src/utils.js';

let expect = require('chai').expect;

describe('SharedId System', function () {
  const UUID = '15fde1dc-1861-4894-afdf-b757272f3568';

  before(function () {
    sinon.stub(utils, 'generateUUID').returns(UUID);
    sinon.stub(utils, 'logInfo');
  });

  after(function () {
    utils.generateUUID.restore();
    utils.logInfo.restore();
  });
  describe('SharedId System getId()', function () {
    const callbackSpy = sinon.spy();

    let coppaDataHandlerDataStub
    let sandbox;

    beforeEach(function () {
      sandbox = sinon.sandbox.create();
      coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa');
      sandbox.stub(utils, 'hasDeviceAccess').returns(true);
      coppaDataHandlerDataStub.returns('');
      callbackSpy.resetHistory();
    });

    afterEach(function () {
      sandbox.restore();
    });

    it('should call UUID', function () {
      let config = {
        storage: {
          type: 'cookie',
          name: '_pubcid',
          expires: 10
        }
      };

      let submoduleCallback = sharedIdSystemSubmodule.getId(config, undefined).callback;
      submoduleCallback(callbackSpy);
      expect(callbackSpy.calledOnce).to.be.true;
      expect(callbackSpy.lastCall.lastArg).to.equal(UUID);
    });
    it('should log message if coppa is set', function () {
      coppaDataHandlerDataStub.returns('true');
      sharedIdSystemSubmodule.getId({});
      expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId');
    });
  });
  describe('SharedId System extendId()', function () {
    const callbackSpy = sinon.spy();
    let coppaDataHandlerDataStub;
    let sandbox;

    beforeEach(function () {
      sandbox = sinon.sandbox.create();
      coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa');
      sandbox.stub(utils, 'hasDeviceAccess').returns(true);
      callbackSpy.resetHistory();
      coppaDataHandlerDataStub.returns('');
    });
    afterEach(function () {
      sandbox.restore();
    });
    it('should call UUID', function () {
      let config = {
        params: {
          extend: true
        },
        storage: {
          type: 'cookie',
          name: '_pubcid',
          expires: 10
        }
      };
      let pubcommId = sharedIdSystemSubmodule.extendId(config, undefined, 'TestId').id;
      expect(pubcommId).to.equal('TestId');
    });
    it('should log message if coppa is set', function () {
      coppaDataHandlerDataStub.returns('true');
      sharedIdSystemSubmodule.extendId({}, undefined, 'TestId');
      expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId');
    });
  });
});
<commit_msg>SharedID module: update tests to assert on behavior rather than logs (#7738)<commit_after>import {sharedIdSystemSubmodule, storage} from 'modules/sharedIdSystem.js';
import {coppaDataHandler} from 'src/adapterManager';

import sinon from 'sinon';
import * as utils from 'src/utils.js';

let expect = require('chai').expect;

describe('SharedId System', function () {
  const UUID = '15fde1dc-1861-4894-afdf-b757272f3568';

  before(function () {
    sinon.stub(utils, 'generateUUID').returns(UUID);
    sinon.stub(utils, 'logInfo');
  });

  after(function () {
    utils.generateUUID.restore();
    utils.logInfo.restore();
  });
  describe('SharedId System getId()', function () {
    const callbackSpy = sinon.spy();

    let coppaDataHandlerDataStub
    let sandbox;

    beforeEach(function () {
      sandbox = sinon.sandbox.create();
      coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa');
      sandbox.stub(utils, 'hasDeviceAccess').returns(true);
      coppaDataHandlerDataStub.returns('');
      callbackSpy.resetHistory();
    });

    afterEach(function () {
      sandbox.restore();
    });

    it('should call UUID', function () {
      let config = {
        storage: {
          type: 'cookie',
          name: '_pubcid',
          expires: 10
        }
      };

      let submoduleCallback = sharedIdSystemSubmodule.getId(config, undefined).callback;
      submoduleCallback(callbackSpy);
      expect(callbackSpy.calledOnce).to.be.true;
      expect(callbackSpy.lastCall.lastArg).to.equal(UUID);
    });
    it('should abort if coppa is set', function () {
      coppaDataHandlerDataStub.returns('true');
      const result = sharedIdSystemSubmodule.getId({});
      expect(result).to.be.undefined;
    });
  });
  describe('SharedId System extendId()', function () {
    const callbackSpy = sinon.spy();
    let coppaDataHandlerDataStub;
    let sandbox;

    beforeEach(function () {
      sandbox = sinon.sandbox.create();
      coppaDataHandlerDataStub = sandbox.stub(coppaDataHandler, 'getCoppa');
      sandbox.stub(utils, 'hasDeviceAccess').returns(true);
      callbackSpy.resetHistory();
      coppaDataHandlerDataStub.returns('');
    });
    afterEach(function () {
      sandbox.restore();
    });
    it('should call UUID', function () {
      let config = {
        params: {
          extend: true
        },
        storage: {
          type: 'cookie',
          name: '_pubcid',
          expires: 10
        }
      };
      let pubcommId = sharedIdSystemSubmodule.extendId(config, undefined, 'TestId').id;
      expect(pubcommId).to.equal('TestId');
    });
    it('should abort if coppa is set', function () {
      coppaDataHandlerDataStub.returns('true');
      const result = sharedIdSystemSubmodule.extendId({params: {extend: true}}, undefined, 'TestId');
      expect(result).to.be.undefined;
    });
  });
});
<|endoftext|>


<commit_before>var fetchValue = require("../fetch/value.coffee"),
    print       = require("../console/print.coffee"),
    validObject = require("../../object/validate.coffee")
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Restricts data based on Solo/Mute filters
//------------------------------------------------------------------------------
module.exports = function( vars , data ) {

  if ( vars.dev.value ) print.time("filtering data")

  var availableKeys = d3.keys(vars.data.keys || {})

  if ( "attrs" in vars ) {
    availableKeys = availableKeys.concat(d3.keys(vars.attrs.keys || {}))
  }

  data = data.filter(function(d){
    var val = fetchValue(vars, d, vars.id.value);
    return val !== null;
  });

  var typeReqs = vars.types[vars.type.value].requirements || [];

  vars.data.filters.forEach( function( key ) {

    if (availableKeys.indexOf(vars[key].value) >= 0 &&
        typeReqs.indexOf(key) >= 0) {

      data = data.filter( function( d ) {

        var val = fetchValue(vars,d,vars[key].value)
        if ( key === "size" ) {
          return typeof val === "number"
        }
        else {
          return val !== null
        }

      })

    }

  });

  // if "solo", only check against "solo" (disregard "mute")
  var key = vars.data.solo.length ? "solo" : "mute"

  vars.data[key].forEach(function(v) {

    function test_value( val ) {

      var arr = vars[v][key].value;

      var match = false;
      arr.forEach(function(f){
        if (typeof f === "function") {
          match = f(val);
        }
        else if ( f === val ) {
          match = true;
        }

      })

      return match
    }

    function nest_check(d) {

      // if the variable has nesting, check all levels
      var match = false
      if (vars[v].nesting) {
        var nesting = vars[v].nesting
        if (validObject(nesting)) {
          nesting = d3.values(nesting)
        }
        nesting.forEach(function(n){
          if (!match) {
            match = test_value(fetchValue(vars,d,n))
          }
        })
      }
      else {
        match = test_value(fetchValue(vars,d,vars[v].value))
      }

      return key === "solo" ? match : !match

    }

    data = data.filter(nest_check)

    if ( v === "id" ) {

      if ("nodes" in vars && vars.nodes.value) {
        if ( vars.dev.value ) print.time("filtering nodes")
        vars.nodes.restricted = vars.nodes.value.filter(nest_check)
        if ( vars.dev.value ) print.timeEnd("filtering nodes")
      }

      if ("edges" in vars && vars.edges.value) {
        if ( vars.dev.value ) print.time("filtering edges")
        vars.edges.restricted = vars.edges.value.filter(function(d){
          var first_match = nest_check(d[vars.edges.source]),
              second_match = nest_check(d[vars.edges.target])
          return first_match && second_match
        })
        if ( vars.dev.value ) print.timeEnd("filtering edges")
      }

    }

  })

  if ( vars.dev.value ) print.timeEnd("filtering data")

  return data

}
<commit_msg>fixed issue w/ same parent filtering (closes #327)<commit_after>var fetchValue = require("../fetch/value.coffee"),
    print       = require("../console/print.coffee"),
    validObject = require("../../object/validate.coffee")
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Restricts data based on Solo/Mute filters
//------------------------------------------------------------------------------
module.exports = function( vars , data ) {

  if ( vars.dev.value ) print.time("filtering data")

  var availableKeys = d3.keys(vars.data.keys || {})

  if ( "attrs" in vars ) {
    availableKeys = availableKeys.concat(d3.keys(vars.attrs.keys || {}))
  }

  data = data.filter(function(d){
    var val = fetchValue(vars, d, vars.id.value);
    return val !== null;
  });

  var typeReqs = vars.types[vars.type.value].requirements || [];

  vars.data.filters.forEach( function( key ) {

    if (availableKeys.indexOf(vars[key].value) >= 0 &&
        typeReqs.indexOf(key) >= 0) {

      data = data.filter( function( d ) {

        var val = fetchValue(vars,d,vars[key].value)
        if ( key === "size" ) {
          return typeof val === "number"
        }
        else {
          return val !== null
        }

      })

    }

  });

  // if "solo", only check against "solo" (disregard "mute")
  var key = vars.data.solo.length ? "solo" : "mute"

  vars.data[key].forEach(function(v) {

    function test_value(val) {

      var arr = vars[v][key].value;

      var match = false;
      arr.forEach(function(f){
        if (typeof f === "function") {
          match = f(val);
        }
        else if (f === val) {
          match = true;
        }

      })

      return key === "solo" ? match : !match;

    }

    function filter_data(d) {
      if (vars[v].nesting) {
        var nesting = vars[v].nesting;
        if (validObject(nesting)) {
          nesting = d3.values(nesting);
        }
        for (var n = 0; n < nesting.length; n++) {
          var new_data = d.filter(function(dd){
            return test_value(fetchValue(vars, dd, nesting[n]))
          });
          if (new_data.length) d = new_data;
        }
      }
      return d;
    }

    data = filter_data(data);

    if ( v === "id" ) {

      if ("nodes" in vars && vars.nodes.value) {
        if ( vars.dev.value ) print.time("filtering nodes")
        vars.nodes.restricted = filter_data(vars.nodes.value)
        if ( vars.dev.value ) print.timeEnd("filtering nodes")
      }

      if ("edges" in vars && vars.edges.value) {
        if ( vars.dev.value ) print.time("filtering edges")
        vars.edges.restricted = vars.edges.value.filter(function(d){
          var points = filter_data([d[vars.edges.source], d[vars.edges.target]]);
          return points.length === 2;
        })
        if ( vars.dev.value ) print.timeEnd("filtering edges")
      }

    }

  })

  if ( vars.dev.value ) print.timeEnd("filtering data")

  return data

}
<|endoftext|>


<commit_before>var _ = require('underscore');
var Backbone = require('backbone');
var MapView = require('../../../src/geo/map-view');
var Map = require('../../../src/geo/map');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var InfowindowManager = require('../../../src/vis/infowindow-manager');

describe('src/vis/infowindow-manager.js', function () {
  beforeEach(function () {
    var windshaftMap = new Backbone.Model({});
    this.map = new Map({}, {
      windshaftMap: windshaftMap
    });
    this.layerView = new Backbone.Model();
    var layerViewFactory = jasmine.createSpyObj('layerViewFactory', ['createLayerView']);
    layerViewFactory.createLayerView.and.returnValue(this.layerView);

    this.mapView = new MapView({
      map: this.map,
      layerViewFactory: layerViewFactory,
      layerGroupModel: new Backbone.Model()
    });
    this.mapView.getNativeMap = function () {};
    this.mapView._addLayerToMap = function () {};
    this.mapView.latLonToPixel = function () { return { x: 0, y: 0 }; };
    this.mapView.getSize = function () { return { x: 1000, y: 1000 }; };

    this.vis = {
      mapView: this.mapView
    };
  });

  it('should add a new infowindow view to the map view when new layers are reseted', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
  });

  it('should add a new infowindow view to the map view when new layers are added', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
  });

  it('should NOT add a new infowindow view to the map view when new layers share the same layerView', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1, layer2 ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
    expect(this.mapView.addInfowindow.calls.count()).toEqual(1);
  });

  it('should correctly bind the featureClick event to the corresponding layerView', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        template: 'template2',
        template_type: 'underscore',
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names2'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1, layer2 ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    var infowindowModel = infowindowView.model;

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.callFake(function (layerIndex, cartoDBId, callback) {
        callback({ name: 'juan' });
      }),
      getLayerAt: function (index) {
        if (index === 0) {
          return layer1;
        }
        return layer2;
      },

      getIndexOf: function (layerModel) {
        if (layerModel === layer1) {
          return 0;
        }
        return 1;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event for layer #0
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    // A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
    expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
    expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(0, 10, jasmine.any(Function));
    this.layerView.model.fetchAttributes.calls.reset();

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template1',
      'alternative_names': 'alternative_names1',
      'fields': [
        {
          'name': 'name',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'title': 'name',
            'value': 'juan',
            'index': 0
          }
        ],
        'data': {
          'name': 'juan'
        }
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });

    // Simulate the featureClick event for layer #1
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 100 }, 1);

    // A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
    expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
    expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(1, 100, jasmine.any(Function));
    this.layerView.model.fetchAttributes.calls.reset();

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template2',
      'alternative_names': 'alternative_names2',
      'fields': [
        {
          'name': 'description',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'title': null,
            'value': 'No data available',
            'index': 0,
            'type': 'empty'
          }
        ],
        'data': {
          'name': 'juan'
        }
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });
  });

  it('should bind the featureClick event to the corresponding layerView only once', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        template: 'template2',
        template_type: 'underscore',
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names2'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    spyOn(this.layerView, 'bind');

    this.map.layers.reset([ layer1, layer2 ]);

    var featureClickBinds = _.select(this.layerView.bind.calls.all(), function (call) {
      return call.args[0] === 'featureClick';
    });
    expect(featureClickBinds.length).toEqual(1);
  });

  it('should set a filter on the tooltipView if the layer has tooltip too', function () {
    // Simulate that the layerView has been added a tooltipView
    var tooltipView = jasmine.createSpyObj('tooltipView', ['setFilter', 'hide']);
    tooltipView.setFilter.and.returnValue(tooltipView);
    this.layerView.tooltipView = tooltipView;

    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.returnValue({ name: 'Juan' }),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    expect(this.layerView.tooltipView.setFilter).toHaveBeenCalled();
    var filterFunction = this.layerView.tooltipView.setFilter.calls.mostRecent().args[0];

    expect(filterFunction({ cartodb_id: 10 })).toBeFalsy();
    expect(filterFunction({ cartodb_id: 0 })).toBeTruthy();
  });

  it('should reload the map when the infowindow template gets new fields', function () {
    spyOn(this.map, 'reload');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.returnValue({ description: 'THE DESCRIPTION' }),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };

    layer.infowindow.set('fields', [
      {
        'name': 'description',
        'title': true,
        'position': 1
      }
    ]);

    expect(this.map.reload).toHaveBeenCalledWith({});
  });

  it('should reload the map and fetch attributes when the infowindow template is active (visible) and it gets fields', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);

    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    spyOn(infowindowView, 'adjustPan');

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.returnValue({ description: 'THE DESCRIPTION' }),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };

    infowindowManager._infowindowModel.setInfowindowTemplate(layer.infowindow);
    infowindowManager._infowindowModel.set('visibility', true);

    spyOn(this.map, 'reload');

    layer.infowindow.set('fields', [
      {
        'name': 'description',
        'title': true,
        'position': 1
      }
    ]);

    expect(this.map.reload.calls.argsFor(0)[0].success).toEqual(jasmine.any(Function));
  });
});
<commit_msg>Added more tests<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var MapView = require('../../../src/geo/map-view');
var Map = require('../../../src/geo/map');
var CartoDBLayer = require('../../../src/geo/map/cartodb-layer');
var InfowindowManager = require('../../../src/vis/infowindow-manager');

describe('src/vis/infowindow-manager.js', function () {
  beforeEach(function () {
    var windshaftMap = new Backbone.Model({});
    this.map = new Map({}, {
      windshaftMap: windshaftMap
    });
    this.layerView = new Backbone.Model();
    var layerViewFactory = jasmine.createSpyObj('layerViewFactory', ['createLayerView']);
    layerViewFactory.createLayerView.and.returnValue(this.layerView);

    this.mapView = new MapView({
      map: this.map,
      layerViewFactory: layerViewFactory,
      layerGroupModel: new Backbone.Model()
    });
    this.mapView.getNativeMap = function () {};
    this.mapView._addLayerToMap = function () {};
    this.mapView.latLonToPixel = function () { return { x: 0, y: 0 }; };
    this.mapView.getSize = function () { return { x: 1000, y: 1000 }; };

    this.vis = {
      mapView: this.mapView
    };
  });

  it('should add a new infowindow view to the map view when new layers are reseted', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
  });

  it('should add a new infowindow view to the map view when new layers are added', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
  });

  it('should NOT add a new infowindow view to the map view when new layers share the same layerView', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }]
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }]
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1, layer2 ]);
    expect(this.mapView.addInfowindow).toHaveBeenCalled();
    expect(this.mapView.addInfowindow.calls.count()).toEqual(1);
  });

  it('should correctly bind the featureClick event to the corresponding layerView', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        template: 'template2',
        template_type: 'underscore',
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names2'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1, layer2 ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    var infowindowModel = infowindowView.model;

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.callFake(function (layerIndex, cartoDBId, callback) {
        callback({ name: 'juan' });
      }),
      getLayerAt: function (index) {
        if (index === 0) {
          return layer1;
        }
        return layer2;
      },

      getIndexOf: function (layerModel) {
        if (layerModel === layer1) {
          return 0;
        }
        return 1;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event for layer #0
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    // A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
    expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
    expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(0, 10, jasmine.any(Function));
    this.layerView.model.fetchAttributes.calls.reset();

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template1',
      'alternative_names': 'alternative_names1',
      'fields': [
        {
          'name': 'name',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'title': 'name',
            'value': 'juan',
            'index': 0
          }
        ],
        'data': {
          'name': 'juan'
        }
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });

    // Simulate the featureClick event for layer #1
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 100 }, 1);

    // A request to fetch the attributes for the right cartodb_id and layerIndex has been triggered
    expect(this.layerView.model.fetchAttributes.calls.count()).toEqual(1);
    expect(this.layerView.model.fetchAttributes).toHaveBeenCalledWith(1, 100, jasmine.any(Function));
    this.layerView.model.fetchAttributes.calls.reset();

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template2',
      'alternative_names': 'alternative_names2',
      'fields': [
        {
          'name': 'description',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'title': null,
            'value': 'No data available',
            'index': 0,
            'type': 'empty'
          }
        ],
        'data': {
          'name': 'juan'
        }
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });
  });

  it('should bind the featureClick event to the corresponding layerView only once', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var layer2 = new CartoDBLayer({
      infowindow: {
        template: 'template2',
        template_type: 'underscore',
        fields: [{
          'name': 'description',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names2'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    spyOn(this.layerView, 'bind');

    this.map.layers.reset([ layer1, layer2 ]);

    var featureClickBinds = _.select(this.layerView.bind.calls.all(), function (call) {
      return call.args[0] === 'featureClick';
    });
    expect(featureClickBinds.length).toEqual(1);
  });

  it('should set loading content while loading', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1 ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    var infowindowModel = infowindowView.model;

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes'),
      getLayerAt: function (index) {
        return layer1;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event for layer #0
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template1',
      'alternative_names': 'alternative_names1',
      'fields': [
        {
          'name': 'name',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'type': 'loading',
            'title': 'loading',
            'value': '…'
          }
        ]
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });
  });

  it('should set error content if no attributes are returned', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer1 = new CartoDBLayer({
      infowindow: {
        template: 'template1',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names1'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer1 ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    var infowindowModel = infowindowView.model;

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.callFake(function (layerIndex, cartoDBId, callback) {
        callback();
      }),

      getLayerAt: function (index) {
        return layer1;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event for layer #0
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    // InfowindowModel has been updated
    expect(infowindowModel.attributes).toEqual({
      'template': 'template1',
      'alternative_names': 'alternative_names1',
      'fields': [
        {
          'name': 'name',
          'title': true,
          'position': 1
        }
      ],
      'template_name': 'infowindow_light',
      'template_type': 'underscore',
      'offset': [
        28,
        0
      ],
      'maxHeight': 180,
      'autoPan': true,
      'content': {
        'fields': [
          {
            'title': null,
            'alternative_name': null,
            'value': 'There has been an error...',
            'index': null,
            'type': 'error'
          }
        ],
        'data': {}
      },
      'latlng': [
        100,
        200
      ],
      'visibility': true
    });
  });

  it('should set a filter on the tooltipView if the layer has tooltip too', function () {
    // Simulate that the layerView has been added a tooltipView
    var tooltipView = jasmine.createSpyObj('tooltipView', ['setFilter', 'hide']);
    tooltipView.setFilter.and.returnValue(tooltipView);
    this.layerView.tooltipView = tooltipView;

    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);
    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes'),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };
    spyOn(infowindowView, 'adjustPan');

    // Simulate the featureClick event
    this.layerView.trigger('featureClick', {}, [100, 200], undefined, { cartodb_id: 10 }, 0);

    expect(this.layerView.tooltipView.setFilter).toHaveBeenCalled();
    var filterFunction = this.layerView.tooltipView.setFilter.calls.mostRecent().args[0];

    expect(filterFunction({ cartodb_id: 10 })).toBeFalsy();
    expect(filterFunction({ cartodb_id: 0 })).toBeTruthy();
  });

  it('should reload the map when the infowindow template gets new fields', function () {
    spyOn(this.map, 'reload');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.returnValue({ description: 'THE DESCRIPTION' }),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };

    layer.infowindow.set('fields', [
      {
        'name': 'description',
        'title': true,
        'position': 1
      }
    ]);

    expect(this.map.reload).toHaveBeenCalledWith({});
  });

  it('should reload the map and fetch attributes when the infowindow template is active (visible) and it gets fields', function () {
    spyOn(this.mapView, 'addInfowindow');

    var layer = new CartoDBLayer({
      infowindow: {
        template: 'template',
        template_type: 'underscore',
        fields: [{
          'name': 'name',
          'title': true,
          'position': 1
        }],
        alternative_names: 'alternative_names'
      }
    });

    var infowindowManager = new InfowindowManager(this.vis);
    infowindowManager.manage(this.mapView, this.map);

    this.map.layers.reset([ layer ]);

    var infowindowView = this.mapView.addInfowindow.calls.mostRecent().args[0];
    spyOn(infowindowView, 'adjustPan');

    this.layerView.model = {
      fetchAttributes: jasmine.createSpy('fetchAttributes').and.returnValue({ description: 'THE DESCRIPTION' }),
      getLayerAt: function (index) {
        return layer;
      },

      getIndexOf: function (layerModel) {
        return 0;
      }
    };

    infowindowManager._infowindowModel.setInfowindowTemplate(layer.infowindow);
    infowindowManager._infowindowModel.set('visibility', true);

    spyOn(this.map, 'reload');

    layer.infowindow.set('fields', [
      {
        'name': 'description',
        'title': true,
        'position': 1
      }
    ]);

    expect(this.map.reload.calls.argsFor(0)[0].success).toEqual(jasmine.any(Function));
  });
});
<|endoftext|>


<commit_before>webfont.FontWatcher = function(domHelper, eventDispatcher, fontSizer,
    asyncCall, getTime) {
  this.domHelper_ = domHelper;
  this.eventDispatcher_ = eventDispatcher;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.currentlyWatched_ = 0;
  this.last_ = false;
  this.success_ = false;
  this.nameHelper_ = new webfont.CssFontFamilyName();
  this.fvd_ = new webfont.FontVariationDescription();
};

webfont.FontWatcher.DEFAULT_FONT = 'DEFAULT_FONT';
webfont.FontWatcher.DEFAULT_FVD = 'n4';

webfont.FontWatcher.prototype.watch = function(fontFamilies, fontDescriptions, last) {
  var originalSize = this.getDefaultFontSize_();
  var length = fontFamilies.length;

  this.currentlyWatched_ += this.calculateFontNumberToWatch(fontFamilies, fontDescriptions);
  if (last) {
    this.last_ = last;
  }
  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    var availableDescriptions = fontDescriptions[fontFamily] || [ '' ];
    var fontDescriptionsLength = availableDescriptions.length;

    for (var j = 0; j < fontDescriptionsLength; j++) {
      var fontDescription = availableDescriptions[j] || webfont.FontWatcher.DEFAULT_FVD;

      this.watch_(fontFamily, fontDescription, originalSize);
    }
  }
};

webfont.FontWatcher.prototype.calculateFontNumberToWatch = function(
    fontFamilies, fontDescriptions) {
  var length = fontFamilies.length;
  var number = 0;

  for (var i = 0; i < length; i++) {
    var availableDescriptions = fontDescriptions[fontFamilies[i]];

    if (availableDescriptions) {
      number += availableDescriptions.length;
    } else {
      number++;
    }
  }
  return number;
};

webfont.FontWatcher.prototype.watch_ = function(fontFamily, fontDescription, originalSize) {
  this.eventDispatcher_.dispatchFontLoading(fontFamily, fontDescription);
  var requestedFont = this.createHiddenElementWithFont_(fontFamily, fontDescription);
  var size = this.fontSizer_.getWidth(requestedFont);

  if (originalSize != size) {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else {
    this.asyncCheck_(this.getTime_(), originalSize, requestedFont,
        fontFamily, fontDescription);
  }
};

webfont.FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
  if (--this.currentlyWatched_ == 0 && this.last_) {
    if (this.success_) {
      this.eventDispatcher_.dispatchActive();
    } else {
      this.eventDispatcher_.dispatchInactive();
    }
  }
};

webfont.FontWatcher.prototype.check_ = function(started, originalSize,
    requestedFont, fontFamily, fontDescription) {
  var size = this.fontSizer_.getWidth(requestedFont);

  if (originalSize != size) {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else if ((this.getTime_() - started) < 5000) {
    this.asyncCheck_(started, originalSize, requestedFont, fontFamily, fontDescription);
  } else {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontInactive(fontFamily, fontDescription);
    this.decreaseCurrentlyWatched_();
  }
};

webfont.FontWatcher.prototype.asyncCheck_ = function(started, originalSize,
    requestedFont, fontFamily, fontDescription) {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context, started, originalSize, requestedFont, fontFamily, fontDescription);
    }
  }(this, this.check_), 50);
};

webfont.FontWatcher.prototype.getDefaultFontSize_ = function() {
  var defaultFont = this.createHiddenElementWithFont_(
      webfont.FontWatcher.DEFAULT_FONT, 'n4');
  var size = this.fontSizer_.getWidth(defaultFont);

  this.domHelper_.removeElement(defaultFont);
  return size;
};

webfont.FontWatcher.prototype.createHiddenElementWithFont_ = function(
    fontFamily, fontDescription) {
  var quotedName = this.nameHelper_.quote(fontFamily);
  var styleString = "position:absolute;top:-999px;font-size:300px;font-family:" +
      quotedName + "," + webfont.FontWatcher.DEFAULT_FONT + ";";

  if (fontDescription) {
    styleString += this.fvd_.expand(fontDescription);
  }
  var span = this.domHelper_.createElement('span', {
    // IE must have a fallback font option, else sometimes the loaded font
    // won't be detected - typically in the fully cached case.
    'style': styleString }, 'Mm');

  this.domHelper_.insertInto('html', span);
  return span;
};
<commit_msg>maybe this is right<commit_after>webfont.FontWatcher = function(domHelper, eventDispatcher, fontSizer,
    asyncCall, getTime) {
  this.domHelper_ = domHelper;
  this.eventDispatcher_ = eventDispatcher;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.currentlyWatched_ = 0;
  this.last_ = false;
  this.success_ = false;
  this.nameHelper_ = new webfont.CssFontFamilyName();
  this.fvd_ = new webfont.FontVariationDescription();
};

webfont.FontWatcher.DEFAULT_FONT = 'DEFAULT_FONT';
webfont.FontWatcher.DEFAULT_FVD = 'n4';

webfont.FontWatcher.prototype.watch = function(fontFamilies, fontDescriptions, last) {
  var originalSize = this.getDefaultFontSize_();
  var length = fontFamilies.length;

  this.currentlyWatched_ += this.calculateFontNumberToWatch(fontFamilies, fontDescriptions);
  if (last) {
    this.last_ = last;
  }
  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    var availableDescriptions = fontDescriptions[fontFamily] || [webfont.FontWatcher.DEFAULT_FVD];
    var fontDescriptionsLength = availableDescriptions.length;

    for (var j = 0; j < fontDescriptionsLength; j++) {
      var fontDescription = availableDescriptions[j];

      this.watch_(fontFamily, fontDescription, originalSize);
    }
  }
};

webfont.FontWatcher.prototype.calculateFontNumberToWatch = function(
    fontFamilies, fontDescriptions) {
  var length = fontFamilies.length;
  var number = 0;

  for (var i = 0; i < length; i++) {
    var availableDescriptions = fontDescriptions[fontFamilies[i]];

    if (availableDescriptions) {
      number += availableDescriptions.length;
    } else {
      number++;
    }
  }
  return number;
};

webfont.FontWatcher.prototype.watch_ = function(fontFamily, fontDescription, originalSize) {
  this.eventDispatcher_.dispatchFontLoading(fontFamily, fontDescription);
  var requestedFont = this.createHiddenElementWithFont_(fontFamily, fontDescription);
  var size = this.fontSizer_.getWidth(requestedFont);

  if (originalSize != size) {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else {
    this.asyncCheck_(this.getTime_(), originalSize, requestedFont,
        fontFamily, fontDescription);
  }
};

webfont.FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
  if (--this.currentlyWatched_ == 0 && this.last_) {
    if (this.success_) {
      this.eventDispatcher_.dispatchActive();
    } else {
      this.eventDispatcher_.dispatchInactive();
    }
  }
};

webfont.FontWatcher.prototype.check_ = function(started, originalSize,
    requestedFont, fontFamily, fontDescription) {
  var size = this.fontSizer_.getWidth(requestedFont);

  if (originalSize != size) {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else if ((this.getTime_() - started) < 5000) {
    this.asyncCheck_(started, originalSize, requestedFont, fontFamily, fontDescription);
  } else {
    this.domHelper_.removeElement(requestedFont);
    this.eventDispatcher_.dispatchFontInactive(fontFamily, fontDescription);
    this.decreaseCurrentlyWatched_();
  }
};

webfont.FontWatcher.prototype.asyncCheck_ = function(started, originalSize,
    requestedFont, fontFamily, fontDescription) {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context, started, originalSize, requestedFont, fontFamily, fontDescription);
    }
  }(this, this.check_), 50);
};

webfont.FontWatcher.prototype.getDefaultFontSize_ = function() {
  var defaultFont = this.createHiddenElementWithFont_(
      webfont.FontWatcher.DEFAULT_FONT, 'n4');
  var size = this.fontSizer_.getWidth(defaultFont);

  this.domHelper_.removeElement(defaultFont);
  return size;
};

webfont.FontWatcher.prototype.createHiddenElementWithFont_ = function(
    fontFamily, fontDescription) {
  var quotedName = this.nameHelper_.quote(fontFamily);
  var styleString = "position:absolute;top:-999px;font-size:300px;font-family:" +
      quotedName + "," + webfont.FontWatcher.DEFAULT_FONT + ";";

  if (fontDescription) {
    styleString += this.fvd_.expand(fontDescription);
  }
  var span = this.domHelper_.createElement('span', {
    // IE must have a fallback font option, else sometimes the loaded font
    // won't be detected - typically in the fully cached case.
    'style': styleString }, 'Mm');

  this.domHelper_.insertInto('html', span);
  return span;
};
<|endoftext|>


<commit_before>const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

function base64ToIndex_withTable(base64ArrayBuffer, table_ByUint8) {
  let resultArrayBuffer = new ArrayBuffer( base64ArrayBuffer.byteLength );
  let source = new Uint8Array( base64ArrayBuffer );
  let result = new Uint8Array( resultArrayBuffer );
  for (let i = 0; i < source.length; ++i)
    result[ i ] = table_ByUint8[ source[ i ] ];
  return resultArrayBuffer;
}

function byArray() {
  //let table = new Array(256);
  let table = [];
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function byArrayBuffer() {
  let table = new Uint8Array( new ArrayBuffer(256) );
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function byHash() {
  let table = {};
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive
}

var dataArrayBuffer = new ArrayBuffer(4096);
let dataUint8Array = new Uint8Array( dataArrayBuffer );
for (let i = 0; i < dataUint8Array.length; ++i) {
  dataUint8Array[ i ] = base64String.codePointAt( getRandomIntInclusive(0, 63) );
}

var base64ToIndex_ByArray = byArray();
var base64ToIndex_ByArrayBuffer = byArrayBuffer();
var base64ToIndex_ByHash = byHash();
<commit_msg>Update Base64ToIndex.js<commit_after>const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

function base64ToIndex_withTable(base64ArrayBuffer, table_ByUint8) {
  let resultArrayBuffer = new ArrayBuffer( base64ArrayBuffer.byteLength );
  let source = new Uint8Array( base64ArrayBuffer );
  let result = new Uint8Array( resultArrayBuffer );
  for (let i = 0; i < source.length; ++i)
    result[ i ] = table_ByUint8[ source[ i ] ];
  return resultArrayBuffer;
}

function byArray() {
  let table = [];
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function byHash() {
  let table = {};
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function byArrayBuffer() {
  let table = new Uint8Array( new ArrayBuffer(256) );
  for (let i = 0; i < base64String.length; ++i)
    table[ base64String.codePointAt(i) ] = i;

  return function (base64_ArrayBuffer) {
    return base64ToIndex_withTable(base64_ArrayBuffer, table);
  };
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive
}

var dataArrayBuffer = new ArrayBuffer(4096);
let dataUint8Array = new Uint8Array( dataArrayBuffer );
for (let i = 0; i < dataUint8Array.length; ++i) {
  dataUint8Array[ i ] = base64String.codePointAt( getRandomIntInclusive(0, 63) );
}

var base64ToIndex_ByArray = byArray();
var base64ToIndex_ByHash = byHash();
var base64ToIndex_ByArrayBuffer = byArrayBuffer();
<|endoftext|>


<commit_before>/*
ICanHaz.js version 1.0 -- by @HenrikJoreteg
Licensed under the "You Must Follow Me on Twitter to use this" license. (Cause, apparently I'm a twitter whore like that)

This is a simple template storage and retrieval system.

Templates are stored in the document as script elements with type="text/html"

They are processed through Moustache.js for templating

Dependencies:
- Mustache.js
- jQuery

Documentation at: http://github.com/HenrikJoreteg/ICanHaz.js

*/

// JSLint Config
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true */
/*global $ jQuery Mustache */
var ICH = {};

ICH.init = function () {
	ICH.ICanHaz_cache = {};

	$('.template').each(function () {
		var title = $(this).attr('title');

		// build our cache
		ICH.ICanHaz_cache[title] = $(this).html();

		// build our retrieval function
		ICH[title] = function (data) {
			data = data || {};
			return $(Mustache.to_html(ICH.ICanHaz_cache[title], data));
		};
	});
};

// init itself on document ready, using jQuery instead of $ if people
// want to use other libraries that conflict.
jQuery(function () {
	ICH.init();
});<commit_msg>More documentation clean up.<commit_after>/*
ICanHaz.js version 1.0 -- by @HenrikJoreteg
Licensed under the "You should follow @HenrikJoreteg on Twitter to use this" license. (Cause, apparently I'm a twitter whore like that)

This is a simple template storage and retrieval system.

Templates are stored in the document as script elements with type="text/html". Cheers to @jeresig for this idea.

They are processed through Moustache.js for templating.

Dependencies:
- Mustache.js
- jQuery

Documentation at: http://github.com/HenrikJoreteg/ICanHaz.js

*/

// JSLint Config
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true */
/*global $ jQuery Mustache */
var ICH = {};

ICH.init = function () {
	ICH.ICanHaz_cache = {};

	$('.template').each(function () {
		var title = $(this).attr('title');

		// build our cache
		ICH.ICanHaz_cache[title] = $(this).html();

		// build our retrieval function
		ICH[title] = function (data) {
			data = data || {};
			return $(Mustache.to_html(ICH.ICanHaz_cache[title], data));
		};

		$(this).remove();
	});
};

// init itself on document ready, using jQuery instead of $ if people
// want to use other libraries that conflict.
jQuery(function () {
	ICH.init();
});<|endoftext|>