<commit_before>
(function($) {

    window.console = (window.console || { log: function() {}, error: function() {}});

    var app = window.pig = {
        categories: {},
        $messageNode: null,

        init: function(page, options) {
            options = (options || {});

            console.log("Starting piggy client on '" + page + "' page", options);

            app.$messageNode = $("aside.messages");

            if (app[page] && app[page].init && app[page].init.apply) {
                app[page].init.apply(app[page], [options]);
            }

        },

        main: {
            init: function() {
                app.main.$acctBalance = $(".balance");
                app.main.$recentTrans = $("div.recentTrans");
                app.main.$confirmDelete = $("#confirmDelete");
                app.main.$totals = $(".totals");
                app.main.$reportChart = $(".reportChart");
                app.main.$reportTrans = $(".reportTrans");
                app.main.$reportDateStart = $("#dateStart");
                app.main.$reportDateEnd = $("#dateEnd");
                app.main.$reportCategory = $("#filterCategory");

                app.main.pieOptions = {
                    series: {
                        pie: {
                            show: true,
                            label: {
                                show: true,
                                formatter: app.main.pieLabelFormatter
                            }
                        }
                    },
                    legend: {
                        show: false
                    }
                };

                $("form[action=\\/transaction\\/add]")
                    .on("submit", this.handleAddTransaction)
                    .find("[required]")
                        .on("invalid", function() {
                            $(this)
                                .closest("form")
                                    .find(".ui-submit")
                                        .removeClass("ui-btn-active");
                        });

                $("body").on("click", ".deleteTrans", this.handleDeleteTransaction);

                $("form[action=\\/account\\/report]").on("submit", this.handleReportFilter);

                // Get initial stats/report
                app.main.getAccountReport();
            },

            handleAddTransaction: function(e) {
                var form = $(this),
                    data = {};

                e.preventDefault();

                app.alerts.clearAll();

                form.serializeArray().forEach(function(item) {
                    data[item.name] = item.value;
                });

                if (!Number(data.amount)) {
                    app.alerts.error("Please enter an amount (greater than zero)");
                    form.find(".ui-submit").removeClass("ui-btn-active");
                    return false;
                }
                data.amount = Number(data.amount);

                // if an expense, flip the amount to negative
                if (form.find("[name=amount]").hasClass("expense") && data.amount > 0) {
                    data.amount *= -1;
                }

                app.trans.add(data, {
                    success: function(result) {
                        app.main.handleTransactionAddSuccess(result, form);
                    },
                    error: function(err) {
                        app.alerts.error(err);
                        form.find(".ui-submit").removeClass("ui-btn-active");
                    }
                });

                return false;
            },

            handleTransactionAddSuccess: function(data, form) {
                var trans = data.transaction,
                    acct = data.account;

                if (!trans || !acct) {
                    console.warn("Bad data returned from transaction/add: ", data);
                    app.alerts.error("There may have been a problem adding this transaction. Please check your account.");
                    return;
                }

                console.log("Transactiona added: ", data);

                var transDiv = $("
");

                app.main.$acctBalance.text("$" + acct.balance.toFixed(2));
                if (acct.balance > 0) {
                    app.main.$acctBalance.addClass("positive");
                    app.main.$acctBalance.removeClass("negative");
                } else {
                    app.main.$acctBalance.addClass("negative");
                    app.main.$acctBalance.removeClass("positive");
                }

                app.main.$recentTrans.find(".ui-first-child").removeClass("ui-first-child");

                transDiv.append(
                    "
$" + trans.amount.toFixed(2) + " " + trans.dateDisplay + "
" +
                    "
" + trans.description + "
" +
                    "
" + app.categories[trans.category] + "
"
                );
                app.main.$recentTrans.prepend(transDiv);
                transDiv.collapsible();

                app.alerts.success("Transaction added successfully!");
                form.find("[name=description]").val("");
                form.find("[name=amount]").val("").focus();
                form.find(".ui-submit").removeClass("ui-btn-active");
            },

            handleDeleteTransaction: function(e) {
                var $transNode = $(e.target).closest(".transactionDetail"),
                    id = $transNode.data("transid");

                e.preventDefault();

                $.mobile.changePage( "#confirmDelete", { role: "dialog" } );

                app.main.$confirmDelete.one("click", "a", function() {
                    app.main.$confirmDelete.dialog("close");

                    if ($(this).hasClass("doDelete")) {
                        app.main.confirmDeleteTransaction(id, $transNode);
                    }
                });
            },

            confirmDeleteTransaction: function(id, $transNode) {
                app.trans.remove(id, {
                    success: function(result) {
                        app.main.handleTransactionDeleteSuccess(result, $transNode);
                    },
                    error: function(err) {
                        app.alerts.error(err);
                    }
                });
            },

            handleTransactionDeleteSuccess: function(data, $node) {
                var acct = data.account;

                if (!acct) {
                    console.warn("Bad data returned from transaction/delete: ", data);
                    app.alerts.error("There may have been a problem deleting this transaction. Please check your account.");
                    return;
                }

                console.log("Transactiona deleted: ", data);

                app.main.$acctBalance.text("$" + acct.balance.toFixed(2));
                if (acct.balance > 0) {
                    app.main.$acctBalance.addClass("positive");
                    app.main.$acctBalance.removeClass("negative");
                } else {
                    app.main.$acctBalance.addClass("negative");
                    app.main.$acctBalance.removeClass("positive");
                }

                $node.remove();
                app.alerts.success("Transaction deleted successfully!");
            },

            handleReportFilter: function(e) {
                var start = new Date(app.main.$reportDateStart.val()),
                    end = new Date(app.main.$reportDateEnd.val()),
                    category = Number(app.main.$reportCategory.val());

                e.preventDefault();

                category = (category || null);
                if (!start) {
                    app.alerts.error("Please select a valid start date for filtering");
                    return;
                }
                if (!end) {
                    app.alerts.error("Please select a valid end date for filtering");
                    return;
                }

                // app.main.$reportChart.html("

");

                app.main.getAccountReport({
                    category: category,
                    dateStart: app.main.$reportDateStart.val(),
                    dateEnd: app.main.$reportDateEnd.val() + " 23:59:59"
                });

                $(this).find(".ui-submit").removeClass("ui-btn-active");

                return false;
            },

            getAccountReport: function(options) {
                options = (options || {});

                options.category = (options.category || null);
                options.dateStart = (options.dateStart || null);
                options.dateEnd = (options.dateEnd || null);

                $.ajax({
                    url: "/account/report",
                    type: "get",
                    data: options,
                    dataType: "json",
                    success: app.ajaxSuccess(app.main.handleReportResults, app.main.handleReportError),
                    error: app.ajaxError(app.main.handleReportError)
                });
            },

            handleReportResults: function(data) {
                app.main.$totals
                    .find(".deposits")
                        .text("$" + data.totals.deposits)
                        .end()
                    .find(".expenses")
                        .text("$" + data.totals.expenses);

                if (data.type === "pie") {

                    app.main.$reportTrans.hide();
                    app.main.$reportChart.show();

                    $.plot(app.main.$reportChart, data.categories.slice(1), app.main.pieOptions);
                }
            },

            handleReportError: function(err) {
                app.alerts.clearAll();
                app.alerts.error(err);
            },

            pieLabelFormatter: function(label, series) {
                return "
" + label + "
$" + Math.round(series.data[0][1]) + "
";
            }

        },

        login: {
            $account: null,
            $lock: null,

            init: function() {
                var loc;

                $("form[action=\\/account\\/login]").on("submit", function(e) {
                    console.error("Login form submission (should not occur)");
                    e.preventDefault();
                    return false;
                });

                app.login.$account = $("[name=account]");
                app.login.$lock = $(".pattern-lock");

                if (window.location.hash) {
                    loc = $("[name=location]");
                    loc.val(loc.val() + window.location.hash);
                }

                if (localStorage.account && !app.login.$account.val().length) {
                    app.login.$account.val(localStorage.account).blur();
                }

                app.login.$lock.patternInput({
                    onFinish: this.handleLoginPattern
                });

                $("body").on("touchmove", function(e) {
                    // makes the pattern lock UI work on touch devices
                    e.preventDefault();
                });
            },

            handleLoginPattern: function(pattern) {
                app.alerts.clearAll();

                app.login.$account.attr("disabled", "disabled");
                app.login.$lock.hide();

                app.alerts.info("Working...");

                $.ajax({
                    url: "/account/login",
                    type: "post",
                    data: {
                        owner: app.login.$account.val(),
                        pattern: pattern.join("")
                    },
                    dataType: "json",
                    success: app.ajaxSuccess(app.login.success, app.login.error),
                    error: app.ajaxError(app.login.error)
                });
            },

            success: function() {
                localStorage.account = app.login.$account.val();
                window.location.replace($("[name=location]").val());
            },

            error: function(err) {
                app.alerts.clearAll();
                app.alerts.error(err);
                app.login.$lock.show();
                app.login.$account.removeAttr("disabled");
            }
        },

        trans: {
            add: function(data, cb) {
                var cat,
                    valid = true;

                console.log("adding transaction with", data);

                cb = (cb || {});

                if (!data.amount || !Number(data.amount)) {
                    app.alerts.error("Please enter a number for the amount");
                    valid = false;
                }
                if (!data.date || !Date(data.date)) {
                    app.alerts.error("Please enter a valid date");
                    valid = false;
                }
                cat = Number(data.category);
                if (!cat && cat !== 0) {
                    app.alerts.error("Please select a valid category");
                    valid = false;
                }

                if (!valid) { return; }

                $.ajax({
                    url: "/transaction/add",
                    type: "post",
                    data: data,
                    dataType: "json",
                    success: app.ajaxSuccess(cb.success, cb.error),
                    error: app.ajaxError(cb.error)
                });
            },

            remove: function(id, cb) {
                console.log("deleting transaction: ", id);

                cb = (cb || {});

                if (!id || !id.length) {
                    app.alerts.error("Please select a transaction to delete");
                    return;
                }

                $.ajax({
                    url: "/transaction/" + id,
                    type: "delete",
                    dataType: "json",
                    success: app.ajaxSuccess(cb.success, cb.error),
                    error: app.ajaxError(cb.error)
                });
            }
        },

        ajaxSuccess: function(success, error) {
            success = (success || function() {});
            error = (error || function() {});
            return function(data) {
                data = (data || {});
                if (data.error) {
                    (app.ajaxError(error))({
                        responseText: JSON.stringify(data),
                        status: data.status
                    });
                } else {
                    success(data);
                }
            };
        },

        ajaxError: function(handler) {
            handler = (handler || function() {});
            return function(xhr) {
                var errJSON;
                try {
                    errJSON = JSON.parse(xhr.responseText);
                } catch(err) {
                    errJSON = {
                        error: xhr.responseText,
                        status: xhr.status
                    };
                }

                if (errJSON.status === 404) {
                    console.error("Unable to find endpoint (404)", errJSON.error);
                    handler("Sorry, but I couldn't complete that request");
                } else if (errJSON.status && errJSON.status < 500) {
                    handler(errJSON.error);
                } else {
                    console.error("Server error: ", errJSON.error);
                    handler("Sorry, but I couldn't complete that request");
                }
            };
        },

        alerts: {
            error: function(msg) { this.add("error", msg); },
            warn: function(msg) { this.add("warn", msg); },
            info: function(msg) { this.add("info", msg); },
            success: function(msg) { this.add("success", msg); },

            add: function(cls, msg) {
                app.$messageNode.append("
" + msg + "
");
            },

            clearAll: function() {
                app.$messageNode.empty();
            }
        }

    };

})(window.jQuery);<commit_msg>Fix pie chart<commit_after>
(function($) {

    window.console = (window.console || { log: function() {}, error: function() {}});

    var app = window.pig = {
        categories: {},
        $messageNode: null,

        init: function(page, options) {
            options = (options || {});

            console.log("Starting piggy client on '" + page + "' page", options);

            app.$messageNode = $("aside.messages");

            if (app[page] && app[page].init && app[page].init.apply) {
                app[page].init.apply(app[page], [options]);
            }

        },

        main: {
            init: function() {
                app.main.$acctBalance = $(".balance");
                app.main.$recentTrans = $("div.recentTrans");
                app.main.$confirmDelete = $("#confirmDelete");
                app.main.$totals = $(".totals");
                app.main.$reportChart = $(".reportChart");
                app.main.$reportTrans = $(".reportTrans");
                app.main.$reportDateStart = $("#dateStart");
                app.main.$reportDateEnd = $("#dateEnd");
                app.main.$reportCategory = $("#filterCategory");

                app.main.pieOptions = {
                    series: {
                        pie: {
                            show: true,
                            label: {
                                show: true,
                                formatter: app.main.pieLabelFormatter
                            }
                        }
                    },
                    legend: {
                        show: false
                    }
                };

                $("form[action=\\/transaction\\/add]")
                    .on("submit", this.handleAddTransaction)
                    .find("[required]")
                        .on("invalid", function() {
                            $(this)
                                .closest("form")
                                    .find(".ui-submit")
                                        .removeClass("ui-btn-active");
                        });

                $("body").on("click", ".deleteTrans", this.handleDeleteTransaction);

                $("form[action=\\/account\\/report]").on("submit", this.handleReportFilter);

                // Get initial stats/report
                app.main.getAccountReport();
            },

            handleAddTransaction: function(e) {
                var form = $(this),
                    data = {};

                e.preventDefault();

                app.alerts.clearAll();

                form.serializeArray().forEach(function(item) {
                    data[item.name] = item.value;
                });

                if (!Number(data.amount)) {
                    app.alerts.error("Please enter an amount (greater than zero)");
                    form.find(".ui-submit").removeClass("ui-btn-active");
                    return false;
                }
                data.amount = Number(data.amount);

                // if an expense, flip the amount to negative
                if (form.find("[name=amount]").hasClass("expense") && data.amount > 0) {
                    data.amount *= -1;
                }

                app.trans.add(data, {
                    success: function(result) {
                        app.main.handleTransactionAddSuccess(result, form);
                    },
                    error: function(err) {
                        app.alerts.error(err);
                        form.find(".ui-submit").removeClass("ui-btn-active");
                    }
                });

                return false;
            },

            handleTransactionAddSuccess: function(data, form) {
                var trans = data.transaction,
                    acct = data.account;

                if (!trans || !acct) {
                    console.warn("Bad data returned from transaction/add: ", data);
                    app.alerts.error("There may have been a problem adding this transaction. Please check your account.");
                    return;
                }

                console.log("Transactiona added: ", data);

                var transDiv = $("
");

                app.main.$acctBalance.text("$" + acct.balance.toFixed(2));
                if (acct.balance > 0) {
                    app.main.$acctBalance.addClass("positive");
                    app.main.$acctBalance.removeClass("negative");
                } else {
                    app.main.$acctBalance.addClass("negative");
                    app.main.$acctBalance.removeClass("positive");
                }

                app.main.$recentTrans.find(".ui-first-child").removeClass("ui-first-child");

                transDiv.append(
                    "
$" + trans.amount.toFixed(2) + " " + trans.dateDisplay + "
" +
                    "
" + trans.description + "
" +
                    "
" + app.categories[trans.category] + "
"
                );
                app.main.$recentTrans.prepend(transDiv);
                transDiv.collapsible();

                app.alerts.success("Transaction added successfully!");
                form.find("[name=description]").val("");
                form.find("[name=amount]").val("").focus();
                form.find(".ui-submit").removeClass("ui-btn-active");
            },

            handleDeleteTransaction: function(e) {
                var $transNode = $(e.target).closest(".transactionDetail"),
                    id = $transNode.data("transid");

                e.preventDefault();

                $.mobile.changePage( "#confirmDelete", { role: "dialog" } );

                app.main.$confirmDelete.one("click", "a", function() {
                    app.main.$confirmDelete.dialog("close");

                    if ($(this).hasClass("doDelete")) {
                        app.main.confirmDeleteTransaction(id, $transNode);
                    }
                });
            },

            confirmDeleteTransaction: function(id, $transNode) {
                app.trans.remove(id, {
                    success: function(result) {
                        app.main.handleTransactionDeleteSuccess(result, $transNode);
                    },
                    error: function(err) {
                        app.alerts.error(err);
                    }
                });
            },

            handleTransactionDeleteSuccess: function(data, $node) {
                var acct = data.account;

                if (!acct) {
                    console.warn("Bad data returned from transaction/delete: ", data);
                    app.alerts.error("There may have been a problem deleting this transaction. Please check your account.");
                    return;
                }

                console.log("Transactiona deleted: ", data);

                app.main.$acctBalance.text("$" + acct.balance.toFixed(2));
                if (acct.balance > 0) {
                    app.main.$acctBalance.addClass("positive");
                    app.main.$acctBalance.removeClass("negative");
                } else {
                    app.main.$acctBalance.addClass("negative");
                    app.main.$acctBalance.removeClass("positive");
                }

                $node.remove();
                app.alerts.success("Transaction deleted successfully!");
            },

            handleReportFilter: function(e) {
                var start = new Date(app.main.$reportDateStart.val()),
                    end = new Date(app.main.$reportDateEnd.val()),
                    category = Number(app.main.$reportCategory.val());

                e.preventDefault();

                category = (category || null);
                if (!start) {
                    app.alerts.error("Please select a valid start date for filtering");
                    return;
                }
                if (!end) {
                    app.alerts.error("Please select a valid end date for filtering");
                    return;
                }

                // app.main.$reportChart.html("

");

                app.main.getAccountReport({
                    category: category,
                    dateStart: app.main.$reportDateStart.val(),
                    dateEnd: app.main.$reportDateEnd.val() + " 23:59:59"
                });

                $(this).find(".ui-submit").removeClass("ui-btn-active");

                return false;
            },

            getAccountReport: function(options) {
                options = (options || {});

                options.category = (options.category || null);
                options.dateStart = (options.dateStart || null);
                options.dateEnd = (options.dateEnd || null);

                $.ajax({
                    url: "/account/report",
                    type: "get",
                    data: options,
                    dataType: "json",
                    success: app.ajaxSuccess(app.main.handleReportResults, app.main.handleReportError),
                    error: app.ajaxError(app.main.handleReportError)
                });
            },

            handleReportResults: function(data) {
                app.main.$totals
                    .find(".deposits")
                        .text("$" + data.totals.deposits)
                        .end()
                    .find(".expenses")
                        .text("$" + data.totals.expenses);

                if (data.type === "pie") {

                    app.main.$reportTrans.hide();
                    app.main.$reportChart.show();

                    $.plot(app.main.$reportChart, data.categories.slice(0), app.main.pieOptions);
                }
            },

            handleReportError: function(err) {
                app.alerts.clearAll();
                app.alerts.error(err);
            },

            pieLabelFormatter: function(label, series) {
                return "
" + label + "
$" + Math.round(series.data[0][1]) + "
";
            }

        },

        login: {
            $account: null,
            $lock: null,

            init: function() {
                var loc;

                $("form[action=\\/account\\/login]").on("submit", function(e) {
                    console.error("Login form submission (should not occur)");
                    e.preventDefault();
                    return false;
                });

                app.login.$account = $("[name=account]");
                app.login.$lock = $(".pattern-lock");

                if (window.location.hash) {
                    loc = $("[name=location]");
                    loc.val(loc.val() + window.location.hash);
                }

                if (localStorage.account && !app.login.$account.val().length) {
                    app.login.$account.val(localStorage.account).blur();
                }

                app.login.$lock.patternInput({
                    onFinish: this.handleLoginPattern
                });

                $("body").on("touchmove", function(e) {
                    // makes the pattern lock UI work on touch devices
                    e.preventDefault();
                });
            },

            handleLoginPattern: function(pattern) {
                app.alerts.clearAll();

                app.login.$account.attr("disabled", "disabled");
                app.login.$lock.hide();

                app.alerts.info("Working...");

                $.ajax({
                    url: "/account/login",
                    type: "post",
                    data: {
                        owner: app.login.$account.val(),
                        pattern: pattern.join("")
                    },
                    dataType: "json",
                    success: app.ajaxSuccess(app.login.success, app.login.error),
                    error: app.ajaxError(app.login.error)
                });
            },

            success: function() {
                localStorage.account = app.login.$account.val();
                window.location.replace($("[name=location]").val());
            },

            error: function(err) {
                app.alerts.clearAll();
                app.alerts.error(err);
                app.login.$lock.show();
                app.login.$account.removeAttr("disabled");
            }
        },

        trans: {
            add: function(data, cb) {
                var cat,
                    valid = true;

                console.log("adding transaction with", data);

                cb = (cb || {});

                if (!data.amount || !Number(data.amount)) {
                    app.alerts.error("Please enter a number for the amount");
                    valid = false;
                }
                if (!data.date || !Date(data.date)) {
                    app.alerts.error("Please enter a valid date");
                    valid = false;
                }
                cat = Number(data.category);
                if (!cat && cat !== 0) {
                    app.alerts.error("Please select a valid category");
                    valid = false;
                }

                if (!valid) { return; }

                $.ajax({
                    url: "/transaction/add",
                    type: "post",
                    data: data,
                    dataType: "json",
                    success: app.ajaxSuccess(cb.success, cb.error),
                    error: app.ajaxError(cb.error)
                });
            },

            remove: function(id, cb) {
                console.log("deleting transaction: ", id);

                cb = (cb || {});

                if (!id || !id.length) {
                    app.alerts.error("Please select a transaction to delete");
                    return;
                }

                $.ajax({
                    url: "/transaction/" + id,
                    type: "delete",
                    dataType: "json",
                    success: app.ajaxSuccess(cb.success, cb.error),
                    error: app.ajaxError(cb.error)
                });
            }
        },

        ajaxSuccess: function(success, error) {
            success = (success || function() {});
            error = (error || function() {});
            return function(data) {
                data = (data || {});
                if (data.error) {
                    (app.ajaxError(error))({
                        responseText: JSON.stringify(data),
                        status: data.status
                    });
                } else {
                    success(data);
                }
            };
        },

        ajaxError: function(handler) {
            handler = (handler || function() {});
            return function(xhr) {
                var errJSON;
                try {
                    errJSON = JSON.parse(xhr.responseText);
                } catch(err) {
                    errJSON = {
                        error: xhr.responseText,
                        status: xhr.status
                    };
                }

                if (errJSON.status === 404) {
                    console.error("Unable to find endpoint (404)", errJSON.error);
                    handler("Sorry, but I couldn't complete that request");
                } else if (errJSON.status && errJSON.status < 500) {
                    handler(errJSON.error);
                } else {
                    console.error("Server error: ", errJSON.error);
                    handler("Sorry, but I couldn't complete that request");
                }
            };
        },

        alerts: {
            error: function(msg) { this.add("error", msg); },
            warn: function(msg) { this.add("warn", msg); },
            info: function(msg) { this.add("info", msg); },
            success: function(msg) { this.add("success", msg); },

            add: function(cls, msg) {
                app.$messageNode.append("
" + msg + "
");
            },

            clearAll: function() {
                app.$messageNode.empty();
            }
        }

    };

})(window.jQuery);<|endoftext|>


<commit_before>module.exports = (function() {
  var def = require('./def');
  return def.type().def_method(function on_message(ev) { return ev; });
})();
<commit_msg>Refactor for clarity.<commit_after>module.exports = (function() {
  var def = require('./def');
  var Handler = def.type();
  Handler.def_method(function on_message(ev) { return ev; });
  return Handler;
})();
<|endoftext|>


<commit_before>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildProgressModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null) {
                result.realm++;
            }
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    var config = applicationStorage.config;

    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0) {
            limit = 50;
        }

        if (limit > 50) {
            limit = 50;
        }

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function (callback) {
                        //GET GUILD SIDE and add it
                        guildModel.getGuildInfo(rankArray[0], rankArray[1], rankArray[2], function (error, guild) {
                            if (guild && guild.bnet && guild.bnet.side != null) {
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            if (guild && guild.ad && guild.ad.lfg == true) {
                                finalRanking[start + counter]["lfg"] = true;
                            }
                            callback(error);
                        });
                    },
                    function (callback) {
                        //GET GUILD Progress and add it
                        var project = {};
                        config.progress.raids.forEach(function (raid) {
                            if (raid.tier == parseInt(req.params.tier, 10)) {
                                var difficulties = ["normal", "heroic", "mythic"];
                                difficulties.forEach(function (difficulty) {
                                    raid.bosses.forEach(function (boss) {
                                        project["progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name] = {$size: "$progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name + ".timestamps"};
                                    });
                                });
                            }
                        });
                        project["progress.tier_" + req.params.tier + ".normalCount"] = 1;
                        project["progress.tier_" + req.params.tier + ".heroicCount"] = 1;
                        project["progress.tier_" + req.params.tier + ".mythicCount"] = 1;

                        guildProgressModel.aggregate({region:rankArray[0],realm:rankArray[1],name:rankArray[2]},project,function(error,result){

                            if (result && result.length > 0&& result[0]['progress'] && result[0]['progress']["tier_" + req.params.tier]) {

                                finalRanking[start + counter]["progress"] = result[0]["progress"]["tier_" + req.params.tier];
                            }
                            callback(error);
                        });

                    }
                ], function (error) {
                    counter++;
                    callback(error);
                })

            }, function (error) {
                if (error) {
                    logger.error(error.message);
                    res.status(500).send(error.message)
                } else {
                    res.json(finalRanking);
                }
            });
        } else {
            next();
        }
    });

};<commit_msg>Fix bug on progress when boss is null...<commit_after>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildProgressModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null) {
                result.realm++;
            }
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    var config = applicationStorage.config;

    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0) {
            limit = 50;
        }

        if (limit > 50) {
            limit = 50;
        }

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function (callback) {
                        //GET GUILD SIDE and add it
                        guildModel.getGuildInfo(rankArray[0], rankArray[1], rankArray[2], function (error, guild) {
                            if (guild && guild.bnet && guild.bnet.side != null) {
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            if (guild && guild.ad && guild.ad.lfg == true) {
                                finalRanking[start + counter]["lfg"] = true;
                            }
                            callback(error);
                        });
                    },
                    function (callback) {
                        //GET GUILD Progress and add it
                        var project = {};
                        config.progress.raids.forEach(function (raid) {
                            if (raid.tier == parseInt(req.params.tier, 10)) {
                                var difficulties = ["normal", "heroic", "mythic"];
                                difficulties.forEach(function (difficulty) {
                                    raid.bosses.forEach(function (boss) {
                                        project["progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name] = {$ifNull:[{$size: "$progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name + ".timestamps"},0]};
                                    });
                                });
                            }
                        });
                        project["progress.tier_" + req.params.tier + ".normalCount"] = 1;
                        project["progress.tier_" + req.params.tier + ".heroicCount"] = 1;
                        project["progress.tier_" + req.params.tier + ".mythicCount"] = 1;

                        guildProgressModel.aggregate({region:rankArray[0],realm:rankArray[1],name:rankArray[2]},project,function(error,result){

                            if (result && result.length > 0&& result[0]['progress'] && result[0]['progress']["tier_" + req.params.tier]) {
                                finalRanking[start + counter]["progress"] = result[0]["progress"]["tier_" + req.params.tier];
                            }
                            callback(error);
                        });

                    }
                ], function (error) {
                    counter++;
                    callback(error);
                })

            }, function (error) {
                if (error) {
                    logger.error(error.message);
                    res.status(500).send(error.message)
                } else {
                    res.json(finalRanking);
                }
            });
        } else {
            next();
        }
    });

};<|endoftext|>


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

sc_require('components/graph/plots/plot_view');

/** @class DG.CasePlotView - A plot of dots placed according to numeric values

 @extends DG.PlotView
 */
DG.CasePlotView = DG.PlotView.extend(
    /** @scope DG.CasePlotView.prototype */
    {
      dataRangeDidChange: function (iSource, iQuestion, iKey, iChanges) {
        var this_ = this,
            tPlotElementLength = this.get('plottedElements').length,
            tCases = this.getPath('model.cases'),
            tRC = this.createRenderContext();

        // iChanges can be a single index or an array of indices
        var tChanges = (SC.typeOf(iChanges) === SC.T_NUMBER ? [iChanges] : iChanges);
        tChanges = tChanges || [];
        tChanges.forEach(function (iIndex) {
          var tCase = tCases.at(iIndex);
          if (iIndex >= tPlotElementLength)
            this_.callCreateElement(tCase, iIndex, this_._createAnimationOn);
          this_.setCircleCoordinate(tRC, tCase, iIndex);
        });
        sc_super();
      },

      /**
       * Set the coordinates and other attributes of the case circle (a Rafael element in this.get('plottedElements')).
       * @param iRC {} case-invariant Render Context
       * @param iCase {DG.Case} the case data
       * @param iIndex {number} index of case in collection
       * @param iAnimate {Boolean} (optional) want changes to be animated into place?
       * @param iCallback {Function} Will be called when animation finished
       * @returns {cx {Number},cy {Number}} final coordinates or null if not defined (hidden plot element)
       */
      setCircleCoordinate: function (iRC, iCase, iIndex, iAnimate, iCallback) {
        DG.assert(iRC && iRC.xAxisView, 'incomplete render context');
        DG.assert(iCase, 'no case');
        DG.assert(DG.MathUtilities.isInIntegerRange(iIndex, 0, this.get('plottedElements').length),
            'out of bounds', 'Index for circle element');

        function dataToCoordinateWithMargin(iAxisView, iData, iMargin) {
          // position icons in view, with margin to prevent circle edge from being clipped.
          // compare to DG.AxisView.dataToCoordinate().
          var tPixelMin = iAxisView.get('pixelMin'),
              tPixelMax = iAxisView.get('pixelMax');
          if (tPixelMin < tPixelMax)
            return tPixelMin + iMargin + iData * (tPixelMax - tPixelMin - 2 * iMargin);
          else
            return tPixelMin - iMargin + iData * (tPixelMax - tPixelMin + 2 * iMargin);
        }

        var tCircle = this.get('plottedElements')[iIndex],
            tRadius = this.radiusForCircleElement(tCircle),
            tWorldCoords = this.get('model').getWorldCoords(iIndex),
            tCoordX = dataToCoordinateWithMargin(iRC.xAxisView, tWorldCoords.x, tRadius),
            tCoordY = dataToCoordinateWithMargin(iRC.yAxisView, tWorldCoords.y, tRadius),
            tIsMissingCase = SC.none(tCoordX) || SC.none(tCoordY);

        // show or hide if needed, then update if shown.
        if (this.showHidePlottedElement(tCircle, tIsMissingCase)) {
          var tAttrs = {
            cx: tCoordX, cy: tCoordY, r: tRadius, fill: iRC.calcCaseColorString(iCase),
            stroke: iRC.strokeColor, 'fill-opacity': iRC.transparency, 'stroke-opacity': iRC.strokeTransparency
          };
          this.updatePlottedElement(tCircle, tAttrs, iAnimate, iCallback);
          return {cx: tCoordX, cy: tCoordY, r: tRadius};
        }
        return null;
      },

      createElement: function (iDatum, iIndex, iAnimate) {
        var this_ = this;

        function changeCaseValues(iIndex, iWorldValues) {
          this_.get('model').setWorldCoords(iIndex, iWorldValues);
        }

        var tIsDragging = false,
            kOpaque = 1,
            tInitialTransform = null,
            tPaper = this.get('paper'),
            tCircle = tPaper.circle(tPaper.width / 2, tPaper.height / 2, this._pointRadius)
                .attr({cursor: "pointer"})
                .addClass(DG.PlotUtilities.kDotClassName)
                .hover(function (event) {  // over
                      // Note that Firefox can come through here repeatedly so we have to check for existence
                      if (!tIsDragging && SC.none(tInitialTransform)) {
                        tInitialTransform = this.transform();
                        this.animate({
                          opacity: kOpaque,
                          transform: DG.PlotUtilities.kDataHoverTransform
                        }, DG.PlotUtilities.kDataTipShowTime);
                      }
                    },
                    function (event) { // out
                      if (!tIsDragging) {
                        this.stop();
                        this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                        tInitialTransform = null;
                      }
                    })
                .mousedown(function (iEvent) {
                  this_.get('model').selectCaseByIndex(iIndex, iEvent.shiftKey);
                })
                .drag(function (dx, dy) { // continue
                      // TODO: drag all selected cases, not just this case.
                      var tWorldX = this_.get('xAxisView').coordinateToData(this.ox + dx),
                          tWorldY = this_.get('yAxisView').coordinateToData(this.oy + dy),
                          tPoint = {x: tWorldX, y: tWorldY},
                          tRC = this_.createRenderContext(),
                          tCurrTransform = this.transform();
                      if (isFinite(tPoint.x) && isFinite(tPoint.y)) {
                        // Put the element into the initial transformed state so that changing case values
                        // will not be affected by the scaling in the current transform.
                        this.transform(tInitialTransform);
                        changeCaseValues(this.index, tPoint);
                        this.transform(tCurrTransform);
                      }
                      this_.setCircleCoordinate(tRC, this_.getPath('model.cases').at(this.index), this.index);
                    },
                    function (x, y) { // begin
                      tIsDragging = true;
                      this.ox = this.attr("cx");
                      this.oy = this.attr("cy");
                      this.animate({opacity: kOpaque}, DG.PlotUtilities.kDataTipShowTime, "bounce");
                      this.toFront();
                    },
                    function () {  // end
                      this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                      tIsDragging = false;
                    })
        ;
        tCircle.index = iIndex;
        tCircle.node.setAttribute('shape-rendering', 'geometric-precision');
        return tCircle;
      },

      /**
       We may clear and draw everything from scratch if required.
       */
      drawData: function () {
        var animatePoints = function () {
          // If the user closes the graph component while the animation is happening, we're likely to
          // crash because we no longer have paper to draw points on. Detect and bail!
          // If the user drags an attribute to an axis during animation, this can destroy our model. Likewise, bail.
          if (!this.get('paper') || !this.get('model'))
            return;

          var loopDoF = function (iCase, iIndex) {
                var tContinue = this.getPath('model.isAnimating'),
                    tPoint;
                if (tContinue) {
                  if (iIndex < tPlottedElements.length) {
                    tPoint = tPlottedElements[iIndex];
                  }
                  else {
                    tPoint = this.callCreateElement(null, iIndex);
                  }
                  tPoint.attr({'fill-opacity': 0, fill: 'yellow'});
                  this.setCircleCoordinate(tRC, iCase, iIndex, true);
                }
                return tContinue;
              }.bind(this),

              loopEndF = function () {
                this.setPath('model.isAnimating', false);
                this.updateSelection();
              }.bind(this);
          tCases.forEachWithInvokeLater(loopDoF, loopEndF);

        }.bind(this);

        if (this.getPath('model.isAnimating'))
          return; // Points are animating to new position

        if (!SC.none(this.get('transferredElementCoordinates'))) {
          this.animateFromTransferredElements();
          return;
        }

        var tCases = this.getPath('model.cases'),
            tPlottedElements = this.get('plottedElements'),
            tRC = this.createRenderContext();

        this._pointRadius = this.calcPointRadius(); // make sure created circles are of right size
        this.setPath('model.isAnimating', true); // So the animation can finish
        animatePoints();  // will loop through all points using invokeLater
      },

      /**
       Generate the svg needed to display the plot
       */
      doDraw: function doDraw() {
        sc_super();

        this.drawData();

        this.updateSelection();

      },

      /**
       * The array object my model uses to store world coords has been swapped out. We want to animate to new positions.
       */
      worldValuesChanged: function () {
        this.prepareForConfigurationChange();
        this.set('transferredElementCoordinates', this.get('cachedPointCoordinates'));
        this._isRenderingValid = false;
        this.displayDidChange();
      }.observes('.model.worldValues')

    });

<commit_msg>[#160034361] Bug fix: Default graph with 13K points takes overlong to show.<commit_after>// ==========================================================================
//                          DG.CasePlotView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph/plots/plot_view');

/** @class DG.CasePlotView - A plot of dots placed according to numeric values

 @extends DG.PlotView
 */
DG.CasePlotView = DG.PlotView.extend(
    /** @scope DG.CasePlotView.prototype */
    {
      dataRangeDidChange: function (iSource, iQuestion, iKey, iChanges) {
        var this_ = this,
            tPlotElementLength = this.get('plottedElements').length,
            tCases = this.getPath('model.cases'),
            tRC = this.createRenderContext();

        // iChanges can be a single index or an array of indices
        var tChanges = (SC.typeOf(iChanges) === SC.T_NUMBER ? [iChanges] : iChanges);
        tChanges = tChanges || [];
        tChanges.forEach(function (iIndex) {
          var tCase = tCases.at(iIndex);
          if (iIndex >= tPlotElementLength)
            this_.callCreateElement(tCase, iIndex, this_._createAnimationOn);
          this_.setCircleCoordinate(tRC, tCase, iIndex);
        });
        sc_super();
      },

      /**
       * Set the coordinates and other attributes of the case circle (a Rafael element in this.get('plottedElements')).
       * @param iRC {} case-invariant Render Context
       * @param iCase {DG.Case} the case data
       * @param iIndex {number} index of case in collection
       * @param iAnimate {Boolean} (optional) want changes to be animated into place?
       * @param iCallback {Function} Will be called when animation finished
       * @returns {cx {Number},cy {Number}} final coordinates or null if not defined (hidden plot element)
       */
      setCircleCoordinate: function (iRC, iCase, iIndex, iAnimate, iCallback) {
        DG.assert(iRC && iRC.xAxisView, 'incomplete render context');
        DG.assert(iCase, 'no case');
        DG.assert(DG.MathUtilities.isInIntegerRange(iIndex, 0, this.get('plottedElements').length),
            'out of bounds', 'Index for circle element');

        function dataToCoordinateWithMargin(iAxisView, iData, iMargin) {
          // position icons in view, with margin to prevent circle edge from being clipped.
          // compare to DG.AxisView.dataToCoordinate().
          var tPixelMin = iAxisView.get('pixelMin'),
              tPixelMax = iAxisView.get('pixelMax');
          if (tPixelMin < tPixelMax)
            return tPixelMin + iMargin + iData * (tPixelMax - tPixelMin - 2 * iMargin);
          else
            return tPixelMin - iMargin + iData * (tPixelMax - tPixelMin + 2 * iMargin);
        }

        var tCircle = this.get('plottedElements')[iIndex],
            tRadius = this.radiusForCircleElement(tCircle),
            tWorldCoords = this.get('model').getWorldCoords(iIndex),
            tCoordX = dataToCoordinateWithMargin(iRC.xAxisView, tWorldCoords.x, tRadius),
            tCoordY = dataToCoordinateWithMargin(iRC.yAxisView, tWorldCoords.y, tRadius),
            tIsMissingCase = SC.none(tCoordX) || SC.none(tCoordY);

        // show or hide if needed, then update if shown.
        if (this.showHidePlottedElement(tCircle, tIsMissingCase)) {
          var tAttrs = {
            cx: tCoordX, cy: tCoordY, r: tRadius, fill: iRC.calcCaseColorString(iCase),
            stroke: iRC.strokeColor, 'fill-opacity': iRC.transparency, 'stroke-opacity': iRC.strokeTransparency
          };
          this.updatePlottedElement(tCircle, tAttrs, iAnimate, iCallback);
          return {cx: tCoordX, cy: tCoordY, r: tRadius};
        }
        return null;
      },

      createElement: function (iDatum, iIndex, iAnimate) {
        var this_ = this;

        function changeCaseValues(iIndex, iWorldValues) {
          this_.get('model').setWorldCoords(iIndex, iWorldValues);
        }

        var tIsDragging = false,
            kOpaque = 1,
            tInitialTransform = null,
            tPaper = this.get('paper'),
            tCircle = tPaper.circle(tPaper.width / 2, tPaper.height / 2, this._pointRadius)
                .attr({cursor: "pointer"})
                .addClass(DG.PlotUtilities.kDotClassName)
                .hover(function (event) {  // over
                      // Note that Firefox can come through here repeatedly so we have to check for existence
                      if (!tIsDragging && SC.none(tInitialTransform)) {
                        tInitialTransform = this.transform();
                        this.animate({
                          opacity: kOpaque,
                          transform: DG.PlotUtilities.kDataHoverTransform
                        }, DG.PlotUtilities.kDataTipShowTime);
                      }
                    },
                    function (event) { // out
                      if (!tIsDragging) {
                        this.stop();
                        this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                        tInitialTransform = null;
                      }
                    })
                .mousedown(function (iEvent) {
                  this_.get('model').selectCaseByIndex(iIndex, iEvent.shiftKey);
                })
                .drag(function (dx, dy) { // continue
                      // TODO: drag all selected cases, not just this case.
                      var tWorldX = this_.get('xAxisView').coordinateToData(this.ox + dx),
                          tWorldY = this_.get('yAxisView').coordinateToData(this.oy + dy),
                          tPoint = {x: tWorldX, y: tWorldY},
                          tRC = this_.createRenderContext(),
                          tCurrTransform = this.transform();
                      if (isFinite(tPoint.x) && isFinite(tPoint.y)) {
                        // Put the element into the initial transformed state so that changing case values
                        // will not be affected by the scaling in the current transform.
                        this.transform(tInitialTransform);
                        changeCaseValues(this.index, tPoint);
                        this.transform(tCurrTransform);
                      }
                      this_.setCircleCoordinate(tRC, this_.getPath('model.cases').at(this.index), this.index);
                    },
                    function (x, y) { // begin
                      tIsDragging = true;
                      this.ox = this.attr("cx");
                      this.oy = this.attr("cy");
                      this.animate({opacity: kOpaque}, DG.PlotUtilities.kDataTipShowTime, "bounce");
                      this.toFront();
                    },
                    function () {  // end
                      this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                      tIsDragging = false;
                    })
        ;
        tCircle.index = iIndex;
        tCircle.node.setAttribute('shape-rendering', 'geometric-precision');
        return tCircle;
      },

      /**
       We may clear and draw everything from scratch if required.
       */

      drawData: function () {
        if (!this.get('paper') || !this.get('model') || !this.getPath('model.cases'))
          return;

        function dataCoordinate(iValue, iMin, iMax) {
          return iMin + kMargin + iValue * (iMax - iMin - 2 * kMargin);
        }

        function createCircleDescription(iIndex) {
          var tWorldCoords = tModel.getWorldCoords(iIndex);
          return {
            type: 'circle',
            cx: dataCoordinate(tWorldCoords.x, tXPixelMin, tXPixelMax),
            cy: dataCoordinate(tWorldCoords.y, tYPixelMin, tYPixelMax),
            r: tR / 2,
            fill: 'yellow',
            stroke: tStrokeParams.strokeColor,
            'stroke-opacity': tStrokeParams.strokeTransparency,
            cursor: 'pointer'
          };
        }

        var finishPointSet = function () {
              function changeCaseValues(iIndex, iWorldValues) {
                this_.get('model').setWorldCoords(iIndex, iWorldValues);
              }

              var this_ = this,
                  tIsDragging = false,
                  kOpaque = 1,
                  tInitialTransform = null;
              tPointSet
                  .hover(function (event) {  // over
                        // Note that Firefox can come through here repeatedly so we have to check for existence
                        if (!tIsDragging && SC.none(tInitialTransform)) {
                          tInitialTransform = this.transform();
                          this.animate({
                            opacity: kOpaque,
                            transform: DG.PlotUtilities.kDataHoverTransform
                          }, DG.PlotUtilities.kDataTipShowTime);
                        }
                      },
                      function (event) { // out
                        if (!tIsDragging) {
                          this.stop();
                          this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                          tInitialTransform = null;
                        }
                      })
                  .mousedown(function (iEvent) {
                    this_.get('model').selectCaseByIndex(this.index, iEvent.shiftKey);
                  })
                  .drag(function (dx, dy) { // continue
                        // TODO: drag all selected cases, not just this case.
                        var tWorldX = this_.get('xAxisView').coordinateToData(this.ox + dx),
                            tWorldY = this_.get('yAxisView').coordinateToData(this.oy + dy),
                            tPoint = {x: tWorldX, y: tWorldY},
                            tRC = this_.createRenderContext(),
                            tCurrTransform = this.transform();
                        if (isFinite(tPoint.x) && isFinite(tPoint.y)) {
                          // Put the element into the initial transformed state so that changing case values
                          // will not be affected by the scaling in the current transform.
                          this.transform(tInitialTransform);
                          changeCaseValues(this.index, tPoint);
                          this.transform(tCurrTransform);
                        }
                        this_.setCircleCoordinate(tRC, this_.getPath('model.cases').at(this.index), this.index);
                      },
                      function (x, y) { // begin
                        tIsDragging = true;
                        this.ox = this.attr("cx");
                        this.oy = this.attr("cy");
                        this.animate({opacity: kOpaque}, DG.PlotUtilities.kDataTipShowTime, "bounce");
                        this.toFront();
                      },
                      function () {  // end
                        this.animate({transform: tInitialTransform}, DG.PlotUtilities.kHighlightHideTime);
                        tIsDragging = false;
                      })
              ;
            }.bind(this),

            tXPixelMin = this.getPath('xAxisView.pixelMin'),
            tXPixelMax = this.getPath('xAxisView.pixelMax'),
            tYPixelMax = this.getPath('yAxisView.pixelMin'),
            tYPixelMin = this.getPath('yAxisView.pixelMax'),

            tPointSet,
            tPlottedElements = this.get('plottedElements'),
            tModel = this.get('model'),
            tNumCases = tModel.get('cases').length(),
            kMargin = 10,
            tR = this.calcPointRadius(),
            tColor = tModel.getPointColor ? tModel.getPointColor() : DG.PlotUtilities.kDefaultPointColor,
            tStrokeParams = this.getStrokeParams();
        if (!tPlottedElements || tPlottedElements.length === 0) {
          var tStart = 0,
              tInc = 200,
              loop = function () {
                var tDescriptions = [];
                tXPixelMax = this.getPath('xAxisView.pixelMax');
                tYPixelMax = this.getPath('yAxisView.pixelMin');
                if( tXPixelMax < 30 || tYPixelMax < 30) {
                  this.invokeLater( loop, 100);
                  return;
                }
                for (var i = tStart; i < tStart + tInc && i < tNumCases; i++) {
                  tDescriptions.push(createCircleDescription(i));
                }
                tPointSet = this.get('paper').add(tDescriptions).forEach(function (iElement, iIndex) {
                  tPlottedElements.push(iElement);
                  iElement.addClass(DG.PlotUtilities.kDotClassName);
                  iElement.index = iIndex;
                  iElement.node.setAttribute('shape-rendering', 'geometric-precision');
                  return true;
                });
                finishPointSet();
                this.setPath('model.isAnimating', true);
                tPointSet.animate({
                      r: tR,
                      fill: tColor
                    },
                    DG.PlotUtilities.kDefaultAnimationTime, '<>',
                    function () {
                      this.setPath('model.isAnimating', false);
                    }.bind(this));
                tInc *= 2;
                tStart += tInc;
                if (i < tNumCases) {
                  this.invokeLater(loop, 10);
                }
                tPointSet.clear();
              }.bind(this);
          loop();
        }
        else if (tPlottedElements.length > 0 && !this.getPath('model.isAnimating')) {
          tPlottedElements.forEach(function (iCircle, iIndex) {
            var tCoords = tModel.getWorldCoords(iIndex);
            iCircle.attr({
              cx: dataCoordinate(tCoords.x, tXPixelMin, tXPixelMax),
              cy: dataCoordinate(tCoords.y, tYPixelMin, tYPixelMax)
            });
          });
        }
      },

      /**
       Generate the svg needed to display the plot
       */
      doDraw: function doDraw() {
        sc_super();

        this.drawData();

        this.updateSelection();

      },

      /**
       * The array object my model uses to store world coords has been swapped out. We want to animate to new positions.
       */
      worldValuesChanged: function () {
        this.prepareForConfigurationChange();
        this.set('transferredElementCoordinates', this.get('cachedPointCoordinates'));
        this._isRenderingValid = false;
        this.displayDidChange();
      }.observes('.model.worldValues')

    });

<|endoftext|>


<commit_before>const path = require('path');

module.exports = {
  mixins: [__dirname],
  aws: {
    memorySize: 128,
    region: 'us-east-1',
    domainName: '',
    certificateArn: '',
    stageName: 'prod',
    uniqueName: 'hops-lambda-<name>',
    cloudformationTemplateFile: path.resolve(__dirname, 'cloudformation.yaml'),
    include: ['<serverDir>/**'],
    exclude: ['flow-typed/**', 'typings/**'],
  },
  configSchema: {
    aws: {
      memorySize: { type: 'number' },
      region: { type: 'string', minLength: 1 },
      domainName: { type: 'string', format: 'hostname' },
      certificateArn: { type: 'string' },
      stageName: { type: 'string', minLength: 1 },
      uniqueName: { type: 'string', minLength: 1 },
      cloudformationTemplateFile: { type: 'string', absolutePath: true },
      include: {
        type: 'array',
        items: {
          type: 'string',
        },
      },
      exclude: {
        type: 'array',
        items: {
          type: 'string',
        },
      },
    },
  },
};
<commit_msg>refactor(lambda): use object type for schema validation<commit_after>const path = require('path');

module.exports = {
  mixins: [__dirname],
  aws: {
    memorySize: 128,
    region: 'us-east-1',
    stageName: 'prod',
    uniqueName: 'hops-lambda-<name>',
    cloudformationTemplateFile: path.resolve(__dirname, 'cloudformation.yaml'),
    include: ['<serverDir>/**'],
    exclude: ['flow-typed/**', 'typings/**'],
  },
  configSchema: {
    aws: {
      type: 'object',
      properties: {
        memorySize: { type: 'number' },
        region: { type: 'string', minLength: 1 },
        domainName: { type: 'string', format: 'hostname' },
        certificateArn: { type: 'string' },
        stageName: { type: 'string', minLength: 1 },
        uniqueName: { type: 'string', minLength: 1 },
        cloudformationTemplateFile: { type: 'string', absolutePath: true },
        include: {
          type: 'array',
          items: {
            type: 'string',
          },
        },
        exclude: {
          type: 'array',
          items: {
            type: 'string',
          },
        },
      },
    },
  },
};
<|endoftext|>


<commit_before><commit_msg>Enable JSON backend while indexedDB is populating<commit_after><|endoftext|>


<commit_before>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildProgressModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null) {
                result.realm++;
            }
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    var config = applicationStorage.config;

    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0) {
            limit = 500;
        }

        if (limit > 1000) {
            limit = 1000;
        }

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function (callback) {
                        //GET GUILD SIDE and add it
                        guildModel.getGuildInfo(rankArray[0], rankArray[1], rankArray[2], function (error, guild) {
                            if (guild && guild.bnet && guild.bnet.side != null) {
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            if (guild && guild.ad && guild.ad.lfg == true) {
                                finalRanking[start + counter]["lfg"] = true;
                            }
                            callback(error);
                        });
                    },
                    function (callback) {
                        //GET GUILD Progress and add it
                        var projection = {};

                        config.progress.raids.forEach(function (raid) {
                            if (raid.tier == parseInt(req.params.tier, 10)) {
                                var difficulties = ["normal", "heroic", "mythic"];
                                difficulties.forEach(function (difficulty) {
                                    raid.bosses.forEach(function (boss) {
                                        projection["progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name + ".timestamps"] = 1;
                                    });
                                });
                            }
                        });
                        guildProgressModel.find({
                            region: rankArray[0],
                            realm: rankArray[1],
                            name: rankArray[2]
                        }, projection, function (error, guilds) {

                            if (guilds && guilds.length > 0 && guilds[0]['progress'] && guilds[0]['progress']["tier_" + req.params.tier]) {
                                finalRanking[start + counter]["progress"] = guilds[0]["progress"]["tier_" + req.params.tier];
                            }
                            callback(error);

                        });

                    }
                ], function (error) {
                    counter++;
                    callback(error);
                })

            }, function () {
                res.json(finalRanking);
            });
        } else {
            next();
        }
    });

};<commit_msg>Display a normalCount, heroicCount and mythicCount on all progress.<commit_after>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildProgressModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null) {
                result.realm++;
            }
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    var config = applicationStorage.config;

    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0) {
            limit = 500;
        }

        if (limit > 1000) {
            limit = 1000;
        }

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function (callback) {
                        //GET GUILD SIDE and add it
                        guildModel.getGuildInfo(rankArray[0], rankArray[1], rankArray[2], function (error, guild) {
                            if (guild && guild.bnet && guild.bnet.side != null) {
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            if (guild && guild.ad && guild.ad.lfg == true) {
                                finalRanking[start + counter]["lfg"] = true;
                            }
                            callback(error);
                        });
                    },
                    function (callback) {
                        //GET GUILD Progress and add it
                        var projection = {};

                        config.progress.raids.forEach(function (raid) {
                            if (raid.tier == parseInt(req.params.tier, 10)) {
                                var difficulties = ["normal", "heroic", "mythic"];
                                difficulties.forEach(function (difficulty) {
                                    raid.bosses.forEach(function (boss) {
                                        projection["progress.tier_" + req.params.tier + "." + difficulty + "." + boss.name + ".timestamps"] = 1;
                                    });
                                });
                            }
                        });
                        projection["progress.tier_"+req.params.tier+".normalCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".heroicCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".mythicCount"] = 1;

                        guildProgressModel.find({
                            region: rankArray[0],
                            realm: rankArray[1],
                            name: rankArray[2]
                        }, projection, function (error, guilds) {

                            if (guilds && guilds.length > 0 && guilds[0]['progress'] && guilds[0]['progress']["tier_" + req.params.tier]) {
                                finalRanking[start + counter]["progress"] = guilds[0]["progress"]["tier_" + req.params.tier];
                            }
                            callback(error);

                        });

                    }
                ], function (error) {
                    counter++;
                    callback(error);
                })

            }, function () {
                res.json(finalRanking);
            });
        } else {
            next();
        }
    });

};<|endoftext|>