<commit_before>function screens_platform() {
    var platform = "browser";
    if (typeof module !== 'undefined' && this && this.module !== module) {
        platform = global.platform || "server";
    } else if (typeof importScripts !== 'undefined') {
        platform = "client";
    }
    return platform;
}

function screens_create_proxy(id) {
    /* Create component proxy */
    var component_obj = new Proxy(() => {
        return {};
    }, {
            get: function (object, property) {
                if (Reflect.has(object, property)) {
                    return Reflect.get(object, property);
                } else if (property in screens) {
                    return screens[property];
                } else {
                    var proxy = Reflect.get(object, "proxy");
                    if (proxy && proxy.get && proxy.get.enabled) {
                        return proxy.get(object, property);
                    }
                }
                return undefined;
            },
            apply: function (object, thisArg, argumentsList) {
                var proxy = Reflect.get(object, "proxy");
                if (proxy && proxy.apply) {
                    return proxy.apply.apply(thisArg, argumentsList);
                }
            }
        });
    component_obj.proxy = {};
    component_obj.id = id;
    return component_obj;
}

function screens_setup(package_name, component_name, child_name, node) {
    var children = [];
    var id = package_name + "." + component_name;
    var component_id = id;
    if (child_name) {
        node = node[child_name];
        id += "." + child_name;
    } else {
        node = screens(id);
        for (var key in node) {
            children.push(key);
        }
    }
    /* Create component proxy */
    var component_obj = screens_create_proxy(id);
    if (child_name) {
        screens[package_name][component_name][child_name] = component_obj;
        component_obj.upper = screens[package_name][component_name];
    } else {
        screens[package_name][component_name] = component_obj;
    }
    if (typeof node !== "function") {
        throw "Component " + id + " cannot be loaded stack: " + new Error().stack;
    }
    var platform = node(component_obj, child_name);
    var init = null;
    if (platform && screens.platform !== platform) {
        console.log(screens.platform + ": remote component: " + id + " = " + platform);
        component_obj = screens_create_proxy(id);
        component_obj.proxy.apply = function (object, thisArg, argumentsList) {
            return function () {
                var args = Array.prototype.slice.call(argumentsList);
                args.unshift(id);
                return component_obj.core.message["send_" + platform].apply(null, args);
            };
        };
        component_obj.proxy.get = function (object, property) {
            return function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(id + "." + property);
                return component_obj.core.message["send_" + platform].apply(null, args);
            };
        };
        if (child_name) {
            screens[package_name][component_name][child_name] = component_obj;
            component_obj.upper = screens[package_name][component_name];
        } else {
            screens[package_name][component_name] = component_obj;
        }
    }
    else {
        init = { callback: component_obj.init, args: [component_obj] };
    }
    component_obj.require = platform;
    if (component_obj.proxy && component_obj.proxy.get) {
        component_obj.proxy.get.enabled = true;
    }
    console.log(screens.platform + ": Loaded " + id);
    screens.components.push(id);
    /* Load child components */
    var initializers = children.map(function (child) {
        return screens_setup(package_name, component_name, child, node);
    });
    if(init) {
        initializers.unshift(init);
        initializers = initializers.reduce((a, b) => a.concat(b), []);
    }
    return initializers;
}

function screens_push(package_name, component_name, callback) {
    var file_name = package_name + "_" + component_name;
    var code_name = package_name + "." + component_name;
    console.log(screens.platform + ": Loading " + code_name);
    if (package_name in screens) {
        if (screens[package_name][component_name]) {
            if (callback) {
                callback();
                return null;
            }
        }
    }
    else {
        screens[package_name] = {};
    }
    var item = {
        package_name: package_name,
        component_name: component_name,
        callback: callback
    };
    return item;
}

async function screens_init(items) {
    for (item of items) {
        var initializers = item.initializers;
        if (initializers) {
            console.log(screens.platform + ": initializing: " + item.package_name + "." + item.component_name);
            do {
                var init = initializers.shift();
                if (init && init.callback) {
                    try {
                        var promise = init.callback.apply(null, init.args);
                        if (promise && promise.then) {
                            await promise;
                        }
                    }
                    catch (err) {
                        var message = err.message || err;
                        console.error(screens.platform + ": Failed to initialise component: " + item.package_name + "." + item.component_name + " with error: " + message + " stack: " + err.stack);
                    }
                }
            } while (init);
            console.log(screens.platform + ": initialized: " + item.package_name + "." + item.component_name);
        }
    }
}

async function screens_import(path) {
    if (screens.platform === "server" || screens.platform === "service") {
        require(path);
    }
    else if (screens.platform === "client") {
        importScripts(path);
    }
    else if (screens.platform === "browser") {
        var scripts = document.getElementsByTagName("script");
        for (var i = scripts.length; i--;) {
            if (scripts[i].src === path) {
                return;
            }
        }
        return new Promise((resolve, reject) => {
            var ref = scripts[0];
            var script = document.createElement("script");
            script.src = path;
            script.async = true;
            script.onload = resolve;
            ref.parentNode.appendChild(script);
        });
    }
}

async function screens_load(items) {
    if (items && items.length) {
        if (screens.platform === "server" || screens.platform === "service") {
            for (var item of items) {
                if (item.package_name in screens && item.component_name in screens[item.package_name]) {
                    return;
                }
                var path = "../code/" + item.package_name + "/" + item.package_name + "_" + item.component_name;
                await screens_import(path);
                item.initializers = screens_setup(item.package_name, item.component_name);
            }
        }
        else {
            var first = true;
            var paths = items.map((item) => {
                if (item.package_name in screens && item.component_name in screens[item.package_name]) {
                    return null;
                }
                var path = item.package_name + "_" + item.component_name;
                if (first) {
                    path = "/packages/code/" + item.package_name + "/" + path;
                    first = false;
                }
                return path + ".js";
            });
            paths = paths.filter(Boolean);
            if (paths.length) {
                var path = paths.join(",") + "?platform=" + screens.platform;
                await screens_import(path);
                var firstItem = items[0];
                if (firstItem.component_name === "*") {
                    items = Object.keys(screens[firstItem.package_name]).map((component_name) => {
                        return {
                            package_name: firstItem.package_name,
                            component_name: component_name
                        };
                    });
                }
                items.map((item) => {
                    item.initializers = screens_setup(item.package_name, item.component_name);
                });
            }
        }
    }
    return items;
}

async function screens_include(packages) {
    if (typeof packages === "string" && packages) {
        var names = packages.split(".");
        var package_name = names[0];
        var component_name = names[1];
        packages = {};
        packages[package_name] = [component_name];
    }
    var collection = {};
    for (var package_name in packages) {
        var components = packages[package_name];
        var items = [];
        components.forEach((component_name) => {
            items.push(screens_push(package_name, component_name));
        });
        items = await screens_load(items);
        collection[package_name] = items;
    }
    for (package_name in packages) {
        console.log(screens.platform + ": initializing package: " + package_name);
        await screens_init(collection[package_name]);
    }
}

var screens = new Proxy(() => {
    return {};
}, {
        apply: function (object, thisArg, argumentsList) {
            return argumentsList[0].split('.').reduce((parent, name) => {
                return parent[name];
            }, screens);
        }
    });

Object.assign(screens, {
    components: [],
    id: "package",
    platform: screens_platform(),
    include: screens_include
});

var platform = screens_platform();
if (platform === "server" || platform === "service") {
    global.screens = screens;
    global.__json__ = {};
}
<commit_msg>async loading of components<commit_after>function screens_platform() {
    var platform = "browser";
    if (typeof module !== 'undefined' && this && this.module !== module) {
        platform = global.platform || "server";
    } else if (typeof importScripts !== 'undefined') {
        platform = "client";
    }
    return platform;
}

function screens_create_proxy(id) {
    /* Create component proxy */
    var component_obj = new Proxy(() => {
        return {};
    }, {
            get: function (object, property) {
                if (Reflect.has(object, property)) {
                    return Reflect.get(object, property);
                } else if (property in screens) {
                    return screens[property];
                } else {
                    var proxy = Reflect.get(object, "proxy");
                    if (proxy && proxy.get && proxy.get.enabled) {
                        return proxy.get(object, property);
                    }
                }
                return undefined;
            },
            apply: function (object, thisArg, argumentsList) {
                var proxy = Reflect.get(object, "proxy");
                if (proxy && proxy.apply) {
                    return proxy.apply.apply(thisArg, argumentsList);
                }
            }
        });
    component_obj.proxy = {};
    component_obj.id = id;
    return component_obj;
}

function screens_setup(package_name, component_name, child_name, node) {
    var children = [];
    var id = package_name + "." + component_name;
    var component_id = id;
    if (child_name) {
        node = node[child_name];
        id += "." + child_name;
    } else {
        node = screens(id);
        for (var key in node) {
            children.push(key);
        }
    }
    if(node && node.id) {
        return [];
    }
    /* Create component proxy */
    var component_obj = screens_create_proxy(id);
    if (child_name) {
        screens[package_name][component_name][child_name] = component_obj;
        component_obj.upper = screens[package_name][component_name];
    } else {
        screens[package_name][component_name] = component_obj;
    }
    if (typeof node !== "function") {
        throw "Component " + id + " cannot be loaded stack: " + new Error().stack;
    }
    var platform = node(component_obj, child_name);
    var init = null;
    if (platform && screens.platform !== platform) {
        console.log(screens.platform + ": remote component: " + id + " = " + platform);
        component_obj = screens_create_proxy(id);
        component_obj.proxy.apply = function (object, thisArg, argumentsList) {
            return function () {
                var args = Array.prototype.slice.call(argumentsList);
                args.unshift(id);
                return component_obj.core.message["send_" + platform].apply(null, args);
            };
        };
        component_obj.proxy.get = function (object, property) {
            return function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(id + "." + property);
                return component_obj.core.message["send_" + platform].apply(null, args);
            };
        };
        if (child_name) {
            screens[package_name][component_name][child_name] = component_obj;
            component_obj.upper = screens[package_name][component_name];
        } else {
            screens[package_name][component_name] = component_obj;
        }
    }
    else {
        init = { callback: component_obj.init, args: [component_obj] };
    }
    component_obj.require = platform;
    if (component_obj.proxy && component_obj.proxy.get) {
        component_obj.proxy.get.enabled = true;
    }
    console.log(screens.platform + ": Loaded " + id);
    screens.components.push(id);
    /* Load child components */
    var initializers = children.map(function (child) {
        return screens_setup(package_name, component_name, child, node);
    });
    if (init) {
        initializers.unshift(init);
        initializers = initializers.reduce((a, b) => a.concat(b), []);
    }
    return initializers;
}

async function screens_init(items) {
    for (item of items) {
        var initializers = item.initializers;
        if (initializers) {
            console.log(screens.platform + ": initializing: " + item.package_name + "." + item.component_name);
            do {
                var init = initializers.shift();
                if (init && init.callback) {
                    try {
                        var promise = init.callback.apply(null, init.args);
                        if (promise && promise.then) {
                            await promise;
                        }
                    }
                    catch (err) {
                        var message = err.message || err;
                        console.error(screens.platform + ": Failed to initialise component: " + item.package_name + "." + item.component_name + " with error: " + message + " stack: " + err.stack);
                    }
                }
            } while (init);
            console.log(screens.platform + ": initialized: " + item.package_name + "." + item.component_name);
        }
    }
}

function screens_import(path) {
    if (screens.platform === "server" || screens.platform === "service") {
        require(path);
    }
    else if (screens.platform === "client") {
        importScripts(path);
    }
    else if (screens.platform === "browser") {
        var scripts = document.getElementsByTagName("script");
        for (var i = scripts.length; i--;) {
            if (scripts[i].src === path) {
                return;
            }
        }
        return new Promise((resolve, reject) => {
            var ref = scripts[0];
            var script = document.createElement("script");
            script.src = path;
            script.async = true;
            script.onload = resolve;
            ref.parentNode.appendChild(script);
        });
    }
}

function screens_load(items, state) {
    if (items && items.length) {
        if (screens.platform === "server" || screens.platform === "service") {
            for (var item of items) {
                if (state === "import") {
                    if (item.package_name in screens && item.component_name in screens[item.package_name]) {
                        continue;
                    }
                    var path = "../code/" + item.package_name + "/" + item.package_name + "_" + item.component_name;
                    console.log(screens.platform + ": Loading " + path);
                    item.promise = screens_import(path);
                }
                else if (state === "setup") {
                    item.initializers = screens_setup(item.package_name, item.component_name);
                }
            }
        }
        else {
            var first = true;
            var paths = items.map((item) => {
                if (state === "import") {
                    if (item.package_name in screens && item.component_name in screens[item.package_name]) {
                        return null;
                    }
                }
                var path = item.package_name + "_" + item.component_name;
                if (first) {
                    path = "/packages/code/" + item.package_name + "/" + path;
                    first = false;
                }
                return path + ".js";
            });
            paths = paths.filter(Boolean);
            if (paths.length) {
                var path = paths.join(",") + "?platform=" + screens.platform;
                if (state === "import") {
                    console.log(screens.platform + ": Loading " + path);
                    items[0].promise = screens_import(path);
                }
                else if (state === "setup") {
                    var firstItem = items[0];
                    if (firstItem.component_name === "*") {
                        items = Object.keys(screens[firstItem.package_name]).map((component_name) => {
                            return {
                                package_name: firstItem.package_name,
                                component_name: component_name
                            };
                        });
                    }
                    items.map((item) => {
                        item.initializers = screens_setup(item.package_name, item.component_name);
                    });
                }
            }
        }
    }
    return items;
}

async function screens_include(packages) {
    if (typeof packages === "string" && packages) {
        var names = packages.split(".");
        var package_name = names[0];
        var component_name = names[1];
        packages = {};
        packages[package_name] = [component_name];
    }
    var collection = {};
    for (var package_name in packages) {
        var components = packages[package_name];
        var items = [];
        if (!(package_name in screens)) {
            screens[package_name] = {};
        }
        components.forEach((component_name) => {
            var item = {
                package_name: package_name,
                component_name: component_name
            };
            items.push(item);
        });
        items = screens_load(items, "import");
        collection[package_name] = items;
    }
    var promises = [];
    for (package_name in packages) {
        for(var item of collection[package_name]) {
            if(item.promise) {
                promises.push(item.promise);
            }
        }
    }
    if(promises) {
        await Promise.all(promises);
    }
    for (package_name in packages) {
        collection[package_name] = screens_load(collection[package_name], "setup");
    }
    for (package_name in packages) {
        console.log(screens.platform + ": initializing package: " + package_name);
        await screens_init(collection[package_name]);
    }
}

var screens = new Proxy(() => {
    return {};
}, {
        apply: function (object, thisArg, argumentsList) {
            return argumentsList[0].split('.').reduce((parent, name) => {
                return parent[name];
            }, screens);
        }
    });

Object.assign(screens, {
    components: [],
    id: "package",
    platform: screens_platform(),
    include: screens_include
});

var platform = screens_platform();
if (platform === "server" || platform === "service") {
    global.screens = screens;
    global.__json__ = {};
}
<|endoftext|>


<commit_before>function requestData() {
    "use strict";
    var req = new XMLHttpRequest();
    req.open("GET", "/games", true);
    req.setRequestHeader("accept", "application/json");

    req.onload = function() {
        if (req.status >= 200 && req.status < 400){
            var gamesjson = req.responseText;
            var gamesData = JSON.parse(gamesjson);
            var gamesToHtml = "";
            var x = 0;

            for (var key in gamesData.games) {
                if (!gamesData.games.hasOwnProperty(key)) {
                    continue;
                }
                gamesToHtml += "
";
                var gameurl = "/#" + gamesData.games[x].id;
                var threadurl = gamesData.games[x].url;
                gamesToHtml +=  '' + gamesData.games[x].name + "	";
                gamesToHtml += '' + threadurl + "	";
                gamesToHtml += "" + '<button type="button" onclick="deleteGame(\'' + gamesData.games[x].id + '\');return false;" class="pure-button">Delete</button>' + "	";
                gamesToHtml += "
";
                x++;
            }

            document.getElementById("games").innerHTML = gamesToHtml;
        } else {
            console.log("Error. Status code " + req.status);
        }
    };

    req.send(null);
    locationHashChanged();
}

function locationHashChanged() {
    "use strict";
    if (window.location.hash != "") {
        var id = "" + window.location.hash;
        id = id.slice(1, id.length);
        showGame(id);
    }
}

function showCreateGame() {
    "use strict";
    $("#index").addClass("hidden");
    $("#creategame").removeClass("hidden");
}

function hideCreateGame() {
    "use strict";
    $("#creategame").addClass("hidden");
    $("#index").removeClass("hidden");
    window.location.hash = "";
}

function showGame(id) {
    "use strict";
    var req = new XMLHttpRequest();
    var url = "/game/" + id;
    req.open("GET", url, true);
    req.setRequestHeader("accept", "application/json");

    req.onload = function() {
        if (req.status >= 200 && req.status < 400){
            var gamejson = req.responseText;
            var gameData = JSON.parse(gamejson);
            var gameToHtml = "";

            document.getElementById("gameheader").innerHTML = gameData.game.name;

            gameToHtml += '' + gameData.game.url + "

";

            var hosts = [];

            for (var i = 0; i < gameData.hosts.length; i++) {
                hosts.push(gameData.hosts[i]);
            }

            gameToHtml += "Hosts
" + hosts.join(', ');

            var players = [];

            for (var i = 0; i < gameData.players.length; i++) {
                players.push(gameData.players[i].name);
            }

            var playerLinks = [];

            for (var i = 0; i < gameData.players.length; i++) {
                playerLinks.push('' + gameData.players[i].name + '');
            }

            gameToHtml += "

Players
" + playerLinks.join(', ');

            gameToHtml += "
Click on a player to permanently remove him from the game!
";

            gameToHtml += '
Add Player
<form id="AddPlayerForm">Name: <input type="text" id="newPlayer"> <input type="button" class="pure-button" onclick="addPlayer(\'' + id + '\')" value="Submit"></form>'

            var nicknames = getNicknames();

            var currentDay = 0;

            if (gameData.votes.length > 0) {
                currentDay = gameData.votes[gameData.votes.length - 1].day;

                var targets = [];

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay == gameData.votes[i].day) {
                        gameData.votes[i].target = gameData.votes[i].target.toLowerCase();
                        for (var key in nicknames) {
                            if (!nicknames.hasOwnProperty(key)) {
                                continue;
                            }
                            var nicknameslist = nicknames[key];
                            for (var j = 0; j < nicknameslist.length; j++) {
                                if (nicknameslist[j].toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                    // if a vote target equafls one of the nicknames, then change the vote to target the "real" name
                                    gameData.votes[i].target = key;
                                }
                                if (key.toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                    // if a vote target equals a name with the wrong capitalization, then change the vote to target the "real" name                                     // (if it's in the nicknames list)
                                    gameData.votes[i].target = key;
                                }
                            }
                        }
                        for (var j = 0; j < players.length; j++) {
                            if (players[j].toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                // if a vote target equals a name with the wrong capitalization, then change the vote to target the "real" name                                     // (if it's in the players list)
                                gameData.votes[i].target = players[j];
                            }
                        }
                        if (targets.indexOf(gameData.votes[i].target) == -1) {
                            // push every new unique vote target to the targets array
                            targets.push(gameData.votes[i].target);
                        }
                      }
                }

                for (var i = targets.length - 1; i >= 0; i--) {
                    if (targets[i] == "") {
                        // remove the null (unvote) target from the list of vote targets
                        targets.splice(i, 1);
                        break;
                    }
                }

                var voters = {}; // "voters" is a JavaScript object with targets as keys and their voters as values
                var activeVoters = {}; // "activeVoters" is a JavaScript object with targets as keys and ACTIVE (not unvoted) voters as values
                var nonvoters = players; // players who aren't voting for anyone at the current time

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay = gameData.votes[i].day) {
                        for (var j = 0; j < targets.length; j++) {
                            if (gameData.votes[i].target.toLowerCase() == targets[j].toLowerCase()) {
                                if(voters.hasOwnProperty(targets[j]) || activeVoters.hasOwnProperty(targets[j])){
                                    continue;
                                }
                                // add a new key (property) to the object for every vote target during the current day
                                voters[targets[j]] = [];
                                activeVoters[targets[j]] = [];
                            }
                        }
                    }
                }

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay == gameData.votes[i].day) {
                        for (var j = 0; j < targets.length; j++) {
                            var remainingVoters = activeVoters[targets[j]].slice(0);
                            for (var z = remainingVoters.length - 1; z >= 0; z--) {
                                if (remainingVoters[z] == gameData.votes[i].voter) {
                                    // every time a new vote is given, remove every previous vote from the active votes
                                    remainingVoters.splice(z, 1);
                                }
                            }
                            var markedVoters = voters[targets[j]].slice(0);
                            for (var z = markedVoters.length - 1; z >= 0; z--) {
                                if (markedVoters[z] == gameData.votes[i].voter) {
                                    // every time a new vote is given, mark every previous vote with the mark "!" (unvoted) in the general votes
                                    markedVoters[z] = "!" + markedVoters[z];
                                }
                            }
                            activeVoters[targets[j]] = remainingVoters;
                            voters[targets[j]] = markedVoters;
                            if (gameData.votes[i].target.toLowerCase() == targets[j].toLowerCase() && gameData.votes[i].target != "") {
                                // push every voter of the current day to the objects as a value of their vote target
                                voters[targets[j]].push(gameData.votes[i].voter);
                                activeVoters[targets[j]].push(gameData.votes[i].voter);
                            }
                        }
                    }
                }

                for (var i = 0; i < targets.length; i++) {
                    var activesList = activeVoters[targets[i]];
                    for (var j = 0; j < activesList.length; j++) {
                        for (var z = nonvoters.length - 1; z >= 0; z--) {
                            if (activesList[j] == nonvoters[z]) {
                                // remove every currently active voter from the "Not Voting" list
                                nonvoters.splice(z, 1);
                            }
                        }
                    }
                }

                gameToHtml += "

Day " + currentDay + " Votecount
";

                for (var i = 0; i < targets.length; i++) {
                    var mainVotersList = voters[targets[i]].slice(0);
                    for (var j = 0; j < mainVotersList.length; j++) {
                            if (mainVotersList[j].indexOf("!") != -1) {
                                var nameWithoutMark = mainVotersList[j].substring(1);
                                mainVotersList[j] = "<s>" + nameWithoutMark + "</s>";
                            }
                    }

                    gameToHtml += "
" + targets[i] + " (" + activeVoters[targets[i]].length + "): " + mainVotersList.join(', ');
            }

                gameToHtml += '

Not Voting (' + nonvoters.length + '): ' + nonvoters.join(', ');
        }

            var firstNewDayVote = false;
            currentDay = 0;

            for (var i = 0; i < gameData.votes.length; i++) {
                var post = gameData.votes[i].index;
                var page = Math.floor(post/20) + 1;
                var address = gameData.game.url + "?page=" + page + "#" + post;

                if (currentDay != gameData.votes[i].day) {
                    firstNewDayVote = true;
                    currentDay = gameData.votes[i].day;
                }
                if (firstNewDayVote === true) {
                    gameToHtml += "

Day " + gameData.votes[i].day + "
";
                    firstNewDayVote = false;
                }
                if (gameData.votes[i].target == "") {
                    gameToHtml += '
' + gameData.votes[i].voter + " unvoted";
                } else if (gameData.votes[i].target == null) {
                    gameToHtml += '
' + gameData.votes[i].voter + " gave an invalid vote!";
                } else {
                    gameToHtml += '
' + gameData.votes[i].voter +
                        " voted for " + gameData.votes[i].target + "";
                }
            }

            if (gameData.votes.length > 0) {
                gameToHtml += '

<textarea id="reply_area" cols=80 rows=25 name="bericht" style="font:10pt Arial; margin-bottom: 4px; width:732px">';
                currentDay = gameData.votes[gameData.votes.length - 1].day;

                gameToHtml += "[blue][b][u][big]Day " + currentDay + " Votecount[/big][/u][/b][/blue]\r\n\r\n";

                for (var i = 0; i < targets.length; i++) {
                    var votersList = voters[targets[i]].slice(0);
                    for (var j = 0; j < votersList.length; j++) {
                            if (votersList[j].indexOf("!") != -1) {
                                var nameWithoutMark = votersList[j].substring(1);
                                votersList[j] = "[s]" + nameWithoutMark + "[/s]";
                            }
                    }

                    gameToHtml += "[b]" + targets[i] + " (" + activeVoters[targets[i]].length + "):[/b] " + votersList.join(', ') + "\r\n";
            }

                gameToHtml += '\r\n' + '[b]Not Voting (' + nonvoters.length + '):[/b] ' + nonvoters.join(', ');
                gameToHtml += '</textarea>';
        }

            if (gameData.votes.length === 0) {
                gameToHtml += "
No votes have been extracted from the given thread yet (this may take more time or there may have been an error while reading the thread)
";
            }

            document.getElementById("gamedata").innerHTML = gameToHtml;
        } else {
            console.log("Error. Status code " + req.status);
        }
    };

    req.send(null);
    $("#index").addClass("hidden");
    $("#gameview").removeClass("hidden");
}

function deletePlayer(id) {
    "use strict";
    $.post("/delete-player", {id: id});
    window.location.reload();
}

function deleteGame(id) {
    "use strict";
    $.post("/delete-game", {id: id});
}

function hideGame() {
    "use strict";
    $("#gameview").addClass("hidden");
    $("#index").removeClass("hidden");
    window.location.hash = "";
}

function addPlayer(gameid) {
	"use strict";
	if (document.getElementById("newPlayer").value) {
		var player = {"game": gameid, "name": document.getElementById("newPlayer").value}
		$.post("/add-player", player);
		window.location.reload();
	}
}
requestData();<commit_msg>changed the add player button text<commit_after>function requestData() {
    "use strict";
    var req = new XMLHttpRequest();
    req.open("GET", "/games", true);
    req.setRequestHeader("accept", "application/json");

    req.onload = function() {
        if (req.status >= 200 && req.status < 400){
            var gamesjson = req.responseText;
            var gamesData = JSON.parse(gamesjson);
            var gamesToHtml = "";
            var x = 0;

            for (var key in gamesData.games) {
                if (!gamesData.games.hasOwnProperty(key)) {
                    continue;
                }
                gamesToHtml += "
";
                var gameurl = "/#" + gamesData.games[x].id;
                var threadurl = gamesData.games[x].url;
                gamesToHtml +=  '' + gamesData.games[x].name + "	";
                gamesToHtml += '' + threadurl + "	";
                gamesToHtml += "" + '<button type="button" onclick="deleteGame(\'' + gamesData.games[x].id + '\');return false;" class="pure-button">Delete</button>' + "	";
                gamesToHtml += "
";
                x++;
            }

            document.getElementById("games").innerHTML = gamesToHtml;
        } else {
            console.log("Error. Status code " + req.status);
        }
    };

    req.send(null);
    locationHashChanged();
}

function locationHashChanged() {
    "use strict";
    if (window.location.hash != "") {
        var id = "" + window.location.hash;
        id = id.slice(1, id.length);
        showGame(id);
    }
}

function showCreateGame() {
    "use strict";
    $("#index").addClass("hidden");
    $("#creategame").removeClass("hidden");
}

function hideCreateGame() {
    "use strict";
    $("#creategame").addClass("hidden");
    $("#index").removeClass("hidden");
    window.location.hash = "";
}

function showGame(id) {
    "use strict";
    var req = new XMLHttpRequest();
    var url = "/game/" + id;
    req.open("GET", url, true);
    req.setRequestHeader("accept", "application/json");

    req.onload = function() {
        if (req.status >= 200 && req.status < 400){
            var gamejson = req.responseText;
            var gameData = JSON.parse(gamejson);
            var gameToHtml = "";

            document.getElementById("gameheader").innerHTML = gameData.game.name;

            gameToHtml += '' + gameData.game.url + "

";

            var hosts = [];

            for (var i = 0; i < gameData.hosts.length; i++) {
                hosts.push(gameData.hosts[i]);
            }

            gameToHtml += "Hosts
" + hosts.join(', ');

            var players = [];

            for (var i = 0; i < gameData.players.length; i++) {
                players.push(gameData.players[i].name);
            }

            var playerLinks = [];

            for (var i = 0; i < gameData.players.length; i++) {
                playerLinks.push('' + gameData.players[i].name + '');
            }

            gameToHtml += "

Players
" + playerLinks.join(', ');

            gameToHtml += "
Click on a player to permanently remove him from the game!
";

            gameToHtml += '
Add Player
<form id="AddPlayerForm">Name: <input type="text" id="newPlayer"> <input type="button" class="pure-button" onclick="addPlayer(\'' + id + '\')" value="Add"></form>'

            var nicknames = getNicknames();

            var currentDay = 0;

            if (gameData.votes.length > 0) {
                currentDay = gameData.votes[gameData.votes.length - 1].day;

                var targets = [];

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay == gameData.votes[i].day) {
                        gameData.votes[i].target = gameData.votes[i].target.toLowerCase();
                        for (var key in nicknames) {
                            if (!nicknames.hasOwnProperty(key)) {
                                continue;
                            }
                            var nicknameslist = nicknames[key];
                            for (var j = 0; j < nicknameslist.length; j++) {
                                if (nicknameslist[j].toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                    // if a vote target equafls one of the nicknames, then change the vote to target the "real" name
                                    gameData.votes[i].target = key;
                                }
                                if (key.toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                    // if a vote target equals a name with the wrong capitalization, then change the vote to target the "real" name                                     // (if it's in the nicknames list)
                                    gameData.votes[i].target = key;
                                }
                            }
                        }
                        for (var j = 0; j < players.length; j++) {
                            if (players[j].toLowerCase() == gameData.votes[i].target.toLowerCase()) {
                                // if a vote target equals a name with the wrong capitalization, then change the vote to target the "real" name                                     // (if it's in the players list)
                                gameData.votes[i].target = players[j];
                            }
                        }
                        if (targets.indexOf(gameData.votes[i].target) == -1) {
                            // push every new unique vote target to the targets array
                            targets.push(gameData.votes[i].target);
                        }
                      }
                }

                for (var i = targets.length - 1; i >= 0; i--) {
                    if (targets[i] == "") {
                        // remove the null (unvote) target from the list of vote targets
                        targets.splice(i, 1);
                        break;
                    }
                }

                var voters = {}; // "voters" is a JavaScript object with targets as keys and their voters as values
                var activeVoters = {}; // "activeVoters" is a JavaScript object with targets as keys and ACTIVE (not unvoted) voters as values
                var nonvoters = players; // players who aren't voting for anyone at the current time

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay = gameData.votes[i].day) {
                        for (var j = 0; j < targets.length; j++) {
                            if (gameData.votes[i].target.toLowerCase() == targets[j].toLowerCase()) {
                                if(voters.hasOwnProperty(targets[j]) || activeVoters.hasOwnProperty(targets[j])){
                                    continue;
                                }
                                // add a new key (property) to the object for every vote target during the current day
                                voters[targets[j]] = [];
                                activeVoters[targets[j]] = [];
                            }
                        }
                    }
                }

                for (var i = 0; i < gameData.votes.length; i++) {
                    if (currentDay == gameData.votes[i].day) {
                        for (var j = 0; j < targets.length; j++) {
                            var remainingVoters = activeVoters[targets[j]].slice(0);
                            for (var z = remainingVoters.length - 1; z >= 0; z--) {
                                if (remainingVoters[z] == gameData.votes[i].voter) {
                                    // every time a new vote is given, remove every previous vote from the active votes
                                    remainingVoters.splice(z, 1);
                                }
                            }
                            var markedVoters = voters[targets[j]].slice(0);
                            for (var z = markedVoters.length - 1; z >= 0; z--) {
                                if (markedVoters[z] == gameData.votes[i].voter) {
                                    // every time a new vote is given, mark every previous vote with the mark "!" (unvoted) in the general votes
                                    markedVoters[z] = "!" + markedVoters[z];
                                }
                            }
                            activeVoters[targets[j]] = remainingVoters;
                            voters[targets[j]] = markedVoters;
                            if (gameData.votes[i].target.toLowerCase() == targets[j].toLowerCase() && gameData.votes[i].target != "") {
                                // push every voter of the current day to the objects as a value of their vote target
                                voters[targets[j]].push(gameData.votes[i].voter);
                                activeVoters[targets[j]].push(gameData.votes[i].voter);
                            }
                        }
                    }
                }

                for (var i = 0; i < targets.length; i++) {
                    var activesList = activeVoters[targets[i]];
                    for (var j = 0; j < activesList.length; j++) {
                        for (var z = nonvoters.length - 1; z >= 0; z--) {
                            if (activesList[j] == nonvoters[z]) {
                                // remove every currently active voter from the "Not Voting" list
                                nonvoters.splice(z, 1);
                            }
                        }
                    }
                }

                gameToHtml += "

Day " + currentDay + " Votecount
";

                for (var i = 0; i < targets.length; i++) {
                    var mainVotersList = voters[targets[i]].slice(0);
                    for (var j = 0; j < mainVotersList.length; j++) {
                            if (mainVotersList[j].indexOf("!") != -1) {
                                var nameWithoutMark = mainVotersList[j].substring(1);
                                mainVotersList[j] = "<s>" + nameWithoutMark + "</s>";
                            }
                    }

                    gameToHtml += "
" + targets[i] + " (" + activeVoters[targets[i]].length + "): " + mainVotersList.join(', ');
            }

                gameToHtml += '

Not Voting (' + nonvoters.length + '): ' + nonvoters.join(', ');
        }

            var firstNewDayVote = false;
            currentDay = 0;

            for (var i = 0; i < gameData.votes.length; i++) {
                var post = gameData.votes[i].index;
                var page = Math.floor(post/20) + 1;
                var address = gameData.game.url + "?page=" + page + "#" + post;

                if (currentDay != gameData.votes[i].day) {
                    firstNewDayVote = true;
                    currentDay = gameData.votes[i].day;
                }
                if (firstNewDayVote === true) {
                    gameToHtml += "

Day " + gameData.votes[i].day + "
";
                    firstNewDayVote = false;
                }
                if (gameData.votes[i].target == "") {
                    gameToHtml += '
' + gameData.votes[i].voter + " unvoted";
                } else if (gameData.votes[i].target == null) {
                    gameToHtml += '
' + gameData.votes[i].voter + " gave an invalid vote!";
                } else {
                    gameToHtml += '
' + gameData.votes[i].voter +
                        " voted for " + gameData.votes[i].target + "";
                }
            }

            if (gameData.votes.length > 0) {
                gameToHtml += '

<textarea id="reply_area" cols=80 rows=25 name="bericht" style="font:10pt Arial; margin-bottom: 4px; width:732px">';
                currentDay = gameData.votes[gameData.votes.length - 1].day;

                gameToHtml += "[blue][b][u][big]Day " + currentDay + " Votecount[/big][/u][/b][/blue]\r\n\r\n";

                for (var i = 0; i < targets.length; i++) {
                    var votersList = voters[targets[i]].slice(0);
                    for (var j = 0; j < votersList.length; j++) {
                            if (votersList[j].indexOf("!") != -1) {
                                var nameWithoutMark = votersList[j].substring(1);
                                votersList[j] = "[s]" + nameWithoutMark + "[/s]";
                            }
                    }

                    gameToHtml += "[b]" + targets[i] + " (" + activeVoters[targets[i]].length + "):[/b] " + votersList.join(', ') + "\r\n";
            }

                gameToHtml += '\r\n' + '[b]Not Voting (' + nonvoters.length + '):[/b] ' + nonvoters.join(', ');
                gameToHtml += '</textarea>';
        }

            if (gameData.votes.length === 0) {
                gameToHtml += "
No votes have been extracted from the given thread yet (this may take more time or there may have been an error while reading the thread)
";
            }

            document.getElementById("gamedata").innerHTML = gameToHtml;
        } else {
            console.log("Error. Status code " + req.status);
        }
    };

    req.send(null);
    $("#index").addClass("hidden");
    $("#gameview").removeClass("hidden");
}

function deletePlayer(id) {
    "use strict";
    $.post("/delete-player", {id: id});
    window.location.reload();
}

function deleteGame(id) {
    "use strict";
    $.post("/delete-game", {id: id});
}

function hideGame() {
    "use strict";
    $("#gameview").addClass("hidden");
    $("#index").removeClass("hidden");
    window.location.hash = "";
}

function addPlayer(gameid) {
	"use strict";
	if (document.getElementById("newPlayer").value) {
		var player = {"game": gameid, "name": document.getElementById("newPlayer").value}
		$.post("/add-player", player);
		window.location.reload();
	}
}
requestData();<|endoftext|>


<commit_before>var when = require('when');
var most = require('most');

var ProviderClient = require('./data/ProviderClient');
var ReadOnlyProviderClient = require('./data/ReadOnlyProviderClient');
var PropertySource = require('./data/PropertySource');
var FunctionSource = require('./data/FunctionSource');
var ObservableSource = require('./data/ObservableSource');

var PatchSyncClient = require('./data/PatchSyncClient');
var PatchClient = require('./data/PatchClient');

var Sync = require('./data/Sync');
var fn = require('./lib/fn');

var defaultSyncSignal = most.periodic(20);

exports.fromSource = fromSource;
exports.fromProperty = fn.curry(fromProperty);
exports.fromFunction = fromFunction;
exports.fromObservable = fromObservable;
exports.fromPatchRemote = fromPatchRemote;
exports.fromPatchRemote = fromPatchSyncRemote;

exports.sync = sync;
exports.syncOn = fn.curry(syncOn);

function fromSource(source) {
	return new ProviderClient(source);
}

function fromProperty(key, objectOrArray) {
	return new ProviderClient(new PropertySource(key, objectOrArray));
}

function fromFunction(f, thisArg) {
	return new ReadOnlyProviderClient(new FunctionSource(f, thisArg));
}

function fromObservable(observable) {
	return new ReadOnlyProviderClient(new ObservableSource(observable));
}

function sync(documents) {
	return syncOn(defaultSyncSignal, documents);
}

function syncOn(signal, documents) {
	var s = typeof signal === 'number' ? most.periodic(signal) : signal;
	return new Sync(documents).run(s);
}

function fromPatchRemote(send, data) {
	return new PatchClient(send, data);
}

function fromPatchSyncRemote(send, data) {
	return new PatchSyncClient(send, data);
}<commit_msg>Fix incorrect export<commit_after>var when = require('when');
var most = require('most');

var ProviderClient = require('./data/ProviderClient');
var ReadOnlyProviderClient = require('./data/ReadOnlyProviderClient');
var PropertySource = require('./data/PropertySource');
var FunctionSource = require('./data/FunctionSource');
var ObservableSource = require('./data/ObservableSource');

var PatchSyncClient = require('./data/PatchSyncClient');
var PatchClient = require('./data/PatchClient');

var Sync = require('./data/Sync');
var fn = require('./lib/fn');

var defaultSyncSignal = most.periodic(20);

exports.fromSource = fromSource;
exports.fromProperty = fn.curry(fromProperty);
exports.fromFunction = fromFunction;
exports.fromObservable = fromObservable;
exports.fromPatchRemote = fromPatchRemote;
exports.fromPatchSyncRemote = fromPatchSyncRemote;

exports.sync = sync;
exports.syncOn = fn.curry(syncOn);

function fromSource(source) {
	return new ProviderClient(source);
}

function fromProperty(key, objectOrArray) {
	return new ProviderClient(new PropertySource(key, objectOrArray));
}

function fromFunction(f, thisArg) {
	return new ReadOnlyProviderClient(new FunctionSource(f, thisArg));
}

function fromObservable(observable) {
	return new ReadOnlyProviderClient(new ObservableSource(observable));
}

function sync(documents) {
	return syncOn(defaultSyncSignal, documents);
}

function syncOn(signal, documents) {
	var s = typeof signal === 'number' ? most.periodic(signal) : signal;
	return new Sync(documents).run(s);
}

function fromPatchRemote(send, data) {
	return new PatchClient(send, data);
}

function fromPatchSyncRemote(send, data) {
	return new PatchSyncClient(send, data);
}<|endoftext|>


<commit_before>'use strict';
import angular from 'angular';
import $ from 'jquery';
import db from '../db';
import uuid from 'node-uuid';
import _ from 'lodash';

const index = {
  'folderList': '.row > .col-md-6:first-child > ul',
  'taskList': '.row > .col-md-6:nth-child(2) > ul',
  'editTaskBody': '#editTaskModal > .modal-dialog > .modal-content > .container-fluid > .row > .modal-body'
}

const TaskCtrl = ($scope) => {

  setTimeout(function() {
    let folder = getActiveFolder();
    $scope.tasks = db('folders').find({folder}).tasks;
    $scope.safeApply();
  }, 0);

  $('#taskSave').on('click', (event) => {

    let folder = getActiveFolder();
    let matchAll = ($('#taskMatch').val() === 'all');

    var values = {
      'id':         uuid(),
      'matchAll':   matchAll
    };

    values.rules = [];

    $.each($('#formTask').serializeArray(), function(i, field) {
      values[field.name] = field.value;
    });

    $('li > select').parent().each((index, value) => {

      let selects = $(value).find('select');
      values.rules.push({
        'type':       $(selects[0]).val(),
        'comparator': $(selects[1]).val(),
        'reference':  $(value).find('input').val()
      });
    });

    db('folders')
    .find({folder})
    .tasks.push(values);
  });

  $('form').submit((event) => {
    event.preventDefault();
  });

  setTimeout(() => {
    $('#taskList > li').each((index, li) => {
      console.log($(li));
      $(li).on('click', () => {
        $('#editTaskModal').modal();
        let folder = getActiveFolder();
        let id = $(li).find('.id').html()
        let task = db('folders').find({folder}).tasks.filter(task => (task.id === id))[0];

        $('#formEditTask input').each((index, val) => {
          if($(val).attr('id') === 'taskName') {
            $(val).val(task.name);
          } else if($(val).attr('id') === 'taskDescription') {
            $(val).val(task.description);
          } else if($(val).attr('id') === 'taskInterval') {
            $(val).val(task.interval);
          }
        });

        $('#editTaskMatch').val(task.matchAll ? 'all' : 'any');

        task.rules.forEach(rule => {
          addRule($('#editRuleList'));
        });

        $('#editRuleList li').each((index, li) => {
          let selects = $(li).children('select');
          let input = $(li).children('input');

          let type = task.rules[index].type;
          let comparator = task.rules[index].comparator;
          let reference = task.rules[index].reference;

          if(type === 'filename') {
            type = 'name';
          }
          $($(selects).get(0)).val(type);
          $($(selects).get(1)).val(comparator);

          $(input).val(reference);

        })

      })
    })
  }, 0);

  $scope.safeApply = function(fn) {
    var phase = this.$root.$$phase;
    if(phase === '$apply' || phase === '$digest') {
      if(fn && (typeof(fn) === 'function')) {
        fn();
      }
    } else {
      this.$apply(fn);
    }
  };
}

function getActiveFolder() {
  console.log($(index.folderList).children().find('.activeFolder'));
  let folderName = $('.activeFolder').find('h3').html();
  let folderPath = $('.activeFolder').find('p').find('i').html();
  setTimeout(() => {console.log($('.activeFolder'))}, 0);

  return {
    'name': folderName,
    'path': folderPath
  }
}

function addRule(ulList) {
  //Make these into inputs instead
  ulList.append('
'+
                  '<button role="button" class="pull-left btn btn-danger"> - </button>'+
                  '<select class="col-md-3" form="formTask">'+
                    '<option value="name"> Filename </option>'+
                    '<option value="extension"> Extension </option>'+
                  '</select>'+
                  '<select class="col-md-3" form="formTask">'+
                    '<option value="equals"> Equals </option>'+
                    '<option value="contains"> Contains </option>'+
                    '<option value="doesNotEquals"> Does not equals </option>'+
                    '<option value="doesNotContain"> Does not contain </option>'+
                  '</select>'+
                  '<input type="text" placeholder="Match">'+
                '
');
}

module.exports = TaskCtrl;<commit_msg>add function for saving edited task<commit_after>'use strict';
import angular from 'angular';
import $ from 'jquery';
import db from '../db';
import uuid from 'node-uuid';
import _ from 'lodash';

const index = {
  'folderList': '.row > .col-md-6:first-child > ul',
  'taskList': '.row > .col-md-6:nth-child(2) > ul',
  'editTaskBody': '#editTaskModal > .modal-dialog > .modal-content > .container-fluid > .row > .modal-body'
}

const TaskCtrl = ($scope) => {

  setTimeout(function() {
    let folder = getActiveFolder();
    $scope.tasks = db('folders').find({folder}).tasks;
    $scope.safeApply();
  }, 0);

  $('#taskSave').on('click', (event) => {

    let folder = getActiveFolder();
    let matchAll = ($('#taskMatch').val() === 'all');

    var values = {
      'id':         uuid(),
      'matchAll':   matchAll
    };

    values.rules = [];

    $.each($('#formTask').serializeArray(), function(i, field) {
      values[field.name] = field.value;
    });

    $('li > select').parent().each((index, value) => {

      let selects = $(value).find('select');
      values.rules.push({
        'type':       $(selects[0]).val(),
        'comparator': $(selects[1]).val(),
        'reference':  $(value).find('input').val()
      });
    });

    db('folders')
    .find({folder})
    .tasks.push(values);
  });

  $('form').submit((event) => {
    event.preventDefault();
  });

  setTimeout(() => {
    $('#taskList > li').each((index, li) => {
      console.log($(li));
      $(li).on('click', () => {
        $('#editTaskModal').modal();
        let folder = getActiveFolder();
        let id = $(li).find('.id').html()
        let task = db('folders').find({folder}).tasks.filter(task => (task.id === id))[0];

        $('#formEditTask input').each((index, val) => {
          if($(val).attr('id') === 'taskName') {
            $(val).val(task.name);
          } else if($(val).attr('id') === 'taskDescription') {
            $(val).val(task.description);
          } else if($(val).attr('id') === 'taskInterval') {
            $(val).val(task.interval);
          }
        });

        $('#editTaskMatch').val(task.matchAll ? 'all' : 'any');

        task.rules.forEach(rule => {
          addRule($('#editRuleList'));
        });

        $('#editRuleList li').each((index, li) => {
          let selects = $(li).children('select');
          let input = $(li).children('input');

          let type = task.rules[index].type;
          let comparator = task.rules[index].comparator;
          let reference = task.rules[index].reference;

          if(type === 'filename') {
            type = 'name';
          }
          $($(selects).get(0)).val(type);
          $($(selects).get(1)).val(comparator);

          $(input).val(reference);
        })

        $('#editTaskSave').on('click', event => {

          let folder = getActiveFolder();
          let matchAll = ($('#editTaskMatch').val() === 'all');

          var values = {
            'id':         task.id,
            'matchAll':   matchAll
          };

          values.rules = [];

          $.each($('#formEditTask').serializeArray(), function(i, field) {
            values[field.name] = field.value;
          });

          $('li > select').parent().each((index, value) => {

            let selects = $(value).find('select');
            values.rules.push({
              'type':       $(selects[0]).val(),
              'comparator': $(selects[1]).val(),
              'reference':  $(value).find('input').val()
            });
          });

          let index = _(db('folders')
          .find({folder}).tasks)
          .findIndex(task => task.id === values.id)

          db('folders')
          .chain()
          .find({folder})
          .set('tasks['+index+']', values)
          .value()
        })
      });
    })
  }, 0);

  $scope.safeApply = function(fn) {
    var phase = this.$root.$$phase;
    if(phase === '$apply' || phase === '$digest') {
      if(fn && (typeof(fn) === 'function')) {
        fn();
      }
    } else {
      this.$apply(fn);
    }
  };
}

function getActiveFolder() {
  console.log($(index.folderList).children().find('.activeFolder'));
  let folderName = $('.activeFolder').find('h3').html();
  let folderPath = $('.activeFolder').find('p').find('i').html();
  setTimeout(() => {console.log($('.activeFolder'))}, 0);

  return {
    'name': folderName,
    'path': folderPath
  }
}

function addRule(ulList) {
  //Make these into inputs instead
  ulList.append('
'+
                  '<button role="button" class="pull-left btn btn-danger"> - </button>'+
                  '<select class="col-md-3" form="formTask">'+
                    '<option value="name"> Filename </option>'+
                    '<option value="extension"> Extension </option>'+
                  '</select>'+
                  '<select class="col-md-3" form="formTask">'+
                    '<option value="equals"> Equals </option>'+
                    '<option value="contains"> Contains </option>'+
                    '<option value="doesNotEquals"> Does not equals </option>'+
                    '<option value="doesNotContain"> Does not contain </option>'+
                  '</select>'+
                  '<input type="text" placeholder="Match">'+
                '
');
}

module.exports = TaskCtrl;<|endoftext|>


<commit_before>var Backbone = require('backbone');
var cdb = require('cartodb.js');
var _ = require('underscore');
var moment = require('moment');
var checkAndBuildOpts = require('../../helpers/required-opts');
var QUERY_TEMPLATE = _.template("SELECT DATE_PART('day', MAX(<%= column %>)::timestamp - MIN(<%= column %>)::timestamp) FROM (<%= table %>) __wrapped");

var STATUS = {
  unavailable: 'unavailable',
  unfetched: 'unfetched',
  fetching: 'fetching',
  fetched: 'fetched'
};

var DEFAULT_MAX_BUCKETS = 366;

var REQUIRED_OPTS = [
  'configModel',
  'querySchemaModel'
];

module.exports = Backbone.Model.extend({
  defaults: {
    status: STATUS.unfetched,
    buckets: []
  },

  initialize: function (attrs, opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);

    if (!_.has(attrs, 'column')) {
      throw new Error('Column is required');
    }

    this._SQL = new cdb.SQL({
      user: this._configModel.get('user_name'),
      sql_api_template: this._configModel.get('sql_api_template'),
      api_key: this._configModel.get('api_key')
    });

    this._initBinds();

    if (this._querySchemaModel.get('status') === STATUS.fetched) {
      this._queryData();
    }
  },

  _initBinds: function () {
    this.on('change:column', this._queryData, this);
    this.listenTo(this._querySchemaModel, 'change:status', this._onQuerySchemaStatusChanged);
  },

  _onQuerySchemaStatusChanged: function () {
    if (this._querySchemaModel.get('status') === STATUS.fetched) {
      this._queryData();
    }
  },

  _queryData: function () {
    if (this._querySchemaModel.getColumnType(this.get('column')) !== 'date') {
      return;
    }

    var query = QUERY_TEMPLATE({
      column: this.get('column'),
      table: this._querySchemaModel.get('query')
    });

    var callback = {
      success: this._onQuerySuccess.bind(this),
      error: this._onQueryError.bind(this)
    };

    this.set('status', STATUS.fetching);
    this._SQL.execute(query, null, callback);
  },

  _onQuerySuccess: function (data) {
    this.set('status', STATUS.fetched);
    var row = data.rows && data.rows[0];
    var days = row && row.date_part ? row.date_part : 0;
    this._calculateBuckets(days);
  },

  _onQueryError: function () {
    this.set('status', STATUS.unavailable);
  },

  _calculateBuckets: function (days) {
    var duration = moment.duration(days, 'days');
    var years = Math.ceil(duration.asYears());

    this.set('buckets', [{
      bins: Math.ceil(duration.asMinutes()),
      val: 'minute',
      label: 'Minutes'
    }, {
      bins: Math.ceil(duration.asHours()),
      val: 'hour',
      label: 'Hours'
    }, {
      bins: days,
      val: 'day',
      label: 'Days'
    }, {
      bins: Math.ceil(duration.asWeeks()),
      val: 'week',
      label: 'Weeks'
    }, {
      bins: Math.ceil(duration.asMonths()),
      val: 'month',
      label: 'Months'
    }, {
      bins: years * 4,
      val: 'quarter',
      label: 'Quarters'
    }, {
      bins: years,
      val: 'year',
      label: 'Years'
    }]);
  },

  getFilteredBuckets: function (max) {
    var limit = max || DEFAULT_MAX_BUCKETS;
    var buckets = _.filter(this.get('buckets'), function (bucket) {
      return bucket.bins <= limit;
    });

    return buckets;
  },

  getPreferredBucket: function (max) {
    var sortedBuckets = _.sortBy(this.getFilteredBuckets(max), 'bins');
    return sortedBuckets.length > 0 ? sortedBuckets[sortedBuckets.length - 1] : {};
  }
});
<commit_msg>Calculate suitable aggregation based on minutes<commit_after>var Backbone = require('backbone');
var cdb = require('cartodb.js');
var _ = require('underscore');
var moment = require('moment');
var checkAndBuildOpts = require('../../helpers/required-opts');
var QUERY_TEMPLATE = _.template("SELECT EXTRACT(EPOCH FROM (MAX(<%= column %>) - MIN(<%= column %>)) / 60) FROM (<%= table %>) __wrapped");

var STATUS = {
  unavailable: 'unavailable',
  unfetched: 'unfetched',
  fetching: 'fetching',
  fetched: 'fetched'
};

var DEFAULT_MAX_BUCKETS = 366;

var REQUIRED_OPTS = [
  'configModel',
  'querySchemaModel'
];

module.exports = Backbone.Model.extend({
  defaults: {
    status: STATUS.unfetched,
    buckets: []
  },

  initialize: function (attrs, opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);

    if (!_.has(attrs, 'column')) {
      throw new Error('Column is required');
    }

    this._SQL = new cdb.SQL({
      user: this._configModel.get('user_name'),
      sql_api_template: this._configModel.get('sql_api_template'),
      api_key: this._configModel.get('api_key')
    });

    this._initBinds();

    if (this._querySchemaModel.get('status') === STATUS.fetched) {
      this._queryData();
    }
  },

  _initBinds: function () {
    this.on('change:column', this._queryData, this);
    this.listenTo(this._querySchemaModel, 'change:status', this._onQuerySchemaStatusChanged);
  },

  _onQuerySchemaStatusChanged: function () {
    if (this._querySchemaModel.get('status') === STATUS.fetched) {
      this._queryData();
    }
  },

  _queryData: function () {
    if (this._querySchemaModel.getColumnType(this.get('column')) !== 'date') {
      return;
    }

    var query = QUERY_TEMPLATE({
      column: this.get('column'),
      table: this._querySchemaModel.get('query')
    });

    var callback = {
      success: this._onQuerySuccess.bind(this),
      error: this._onQueryError.bind(this)
    };

    this.set('status', STATUS.fetching);
    this._SQL.execute(query, null, callback);
  },

  _onQuerySuccess: function (data) {
    this.set('status', STATUS.fetched);
    var row = data.rows && data.rows[0];
    var minutes = row && row.date_part ? row.date_part : 0;
    this._calculateBuckets(minutes);
  },

  _onQueryError: function () {
    this.set('status', STATUS.unavailable);
  },

  _calculateBuckets: function (minutes) {
    var duration = moment.duration(minutes, 'minutes');
    var years = Math.ceil(duration.asYears());

    this.set('buckets', [{
      bins: minutes,
      val: 'minute',
      label: 'Minutes'
    }, {
      bins: Math.ceil(duration.asHours()),
      val: 'hour',
      label: 'Hours'
    }, {
      bins: Math.ceil(duration.asDays()),
      val: 'day',
      label: 'Days'
    }, {
      bins: Math.ceil(duration.asWeeks()),
      val: 'week',
      label: 'Weeks'
    }, {
      bins: Math.ceil(duration.asMonths()),
      val: 'month',
      label: 'Months'
    }, {
      bins: years * 4,
      val: 'quarter',
      label: 'Quarters'
    }, {
      bins: years,
      val: 'year',
      label: 'Years'
    }]);
  },

  getFilteredBuckets: function (max) {
    var limit = max || DEFAULT_MAX_BUCKETS;
    var buckets = _.filter(this.get('buckets'), function (bucket) {
      return bucket.bins <= limit;
    });

    return buckets;
  },

  getPreferredBucket: function (max) {
    var sortedBuckets = _.sortBy(this.getFilteredBuckets(max), 'bins');
    return sortedBuckets.length > 0 ? sortedBuckets[sortedBuckets.length - 1] : {};
  }
});
<|endoftext|>


<commit_before>try
{
	if(require)
		Handlebars = require("handlebars");
}
catch(err)
{

}

Handlebars.registerHelper('equals', function(a, b, opts)
{
	if(a === b) // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('notequals', function(a, b, opts)
{
	if(a !== b) // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('empty', function(a, opts)
{
	if(a === undefined || a === null || a === "" || a === '') // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('notempty', function(a, opts)
{
	if(a === undefined || a === null || a === "" || a === '') // Or === depending on your needs
		return opts.inverse(this);
    else
    	return opts.fn(this);
});

Handlebars.registerHelper("tojson", function(a, b, opts)
{
	try
	{
		return JSON.parse(a)[b];
	}
	catch(err)
	{
		return err;
	}
});

Handlebars.registerHelper("math", function(lvalue, operator, rvalue, options) {
    lvalue = parseFloat(lvalue);
    rvalue = parseFloat(rvalue);

    return {
        "+": lvalue + rvalue,
        "-": lvalue - rvalue,
        "*": lvalue * rvalue,
        "/": lvalue / rvalue,
        "%": lvalue % rvalue
    }[operator];
});

Handlebars.registerHelper("sign", function(lvalue, operator, rvalue, options) {
    lvalue = parseFloat(lvalue);
    rvalue = parseFloat(rvalue);

    return {
        "<": lvalue < rvalue,
        "lt": lvalue < rvalue,
        "<=": lvalue <= rvalue,
        "lt=": lvalue <= rvalue,
        ">": lvalue > rvalue,
        "gt": lvalue > rvalue,
        ">=": lvalue >= rvalue,
        "gt=": lvalue >= rvalue,
        "==": lvalue == rvalue,
        "!=": lvalue != rvalue,
        "===": lvalue === rvalue,
        "!==": lvalue === rvalue,
    }[operator] ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper("shortDate", function(a)
{
	if(a)
	{
		var split = a.split(" ");
		var date = split[0];
		var time = split[1];

		date = date.split("-");

		var today = new Date();
		if(today.getFullYear() == parseInt(date[0]) && today.getMonth()+1 == parseInt(date[1]) && today.getDate() == parseInt(date[2]))
			return time;
		else
			return date.join("-");
	}
	else
	{
		return "";
	}
});

Handlebars.registerHelper('escape', function(variable) {
	  return variable.replace(/(['"])/g, '\\$1');
});<commit_msg>test 핸들바 문법 추가<commit_after>try
{
	if(require)
		Handlebars = require("handlebars");
}
catch(err)
{

}

Handlebars.registerHelper('test', function(test, opts)
{
	var f = new Function("if(" + test + "){return true;}else{return false;}");

	if(f())
		return opts.fn(this);
	else
		return ops.inverse(this);
});

Handlebars.registerHelper('equals', function(a, b, opts)
{
	if(a === b) // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('notequals', function(a, b, opts)
{
	if(a !== b) // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('empty', function(a, opts)
{
	if(a === undefined || a === null || a === "" || a === '') // Or === depending on your needs
        return opts.fn(this);
    else
        return opts.inverse(this);
});

Handlebars.registerHelper('notempty', function(a, opts)
{
	if(a === undefined || a === null || a === "" || a === '') // Or === depending on your needs
		return opts.inverse(this);
    else
    	return opts.fn(this);
});

Handlebars.registerHelper("tojson", function(a, b, opts)
{
	try
	{
		return JSON.parse(a)[b];
	}
	catch(err)
	{
		return err;
	}
});

Handlebars.registerHelper("math", function(lvalue, operator, rvalue, options) {
    lvalue = parseFloat(lvalue);
    rvalue = parseFloat(rvalue);

    return {
        "+": lvalue + rvalue,
        "-": lvalue - rvalue,
        "*": lvalue * rvalue,
        "/": lvalue / rvalue,
        "%": lvalue % rvalue
    }[operator];
});

Handlebars.registerHelper("sign", function(lvalue, operator, rvalue, options) {
    lvalue = parseFloat(lvalue);
    rvalue = parseFloat(rvalue);

    return {
        "<": lvalue < rvalue,
        "lt": lvalue < rvalue,
        "<=": lvalue <= rvalue,
        "lt=": lvalue <= rvalue,
        ">": lvalue > rvalue,
        "gt": lvalue > rvalue,
        ">=": lvalue >= rvalue,
        "gt=": lvalue >= rvalue,
        "==": lvalue == rvalue,
        "!=": lvalue != rvalue,
        "===": lvalue === rvalue,
        "!==": lvalue === rvalue,
    }[operator] ? options.fn(this) : options.inverse(this);
});

Handlebars.registerHelper("shortDate", function(a)
{
	if(a)
	{
		var split = a.split(" ");
		var date = split[0];
		var time = split[1];

		date = date.split("-");

		var today = new Date();
		if(today.getFullYear() == parseInt(date[0]) && today.getMonth()+1 == parseInt(date[1]) && today.getDate() == parseInt(date[2]))
			return time;
		else
			return date.join("-");
	}
	else
	{
		return "";
	}
});

Handlebars.registerHelper('escape', function(variable) {
	  return variable.replace(/(['"])/g, '\\$1');
});<|endoftext|>


<commit_before>import { observable, computed, action } from 'mobx';
import bezierAnimation from '../../helpers/bezier';
import WindowSize from '../../models/window-size';
import WeightsUX from './weights-ux';
import UiSettings from 'shared/model/ui-settings';
import { first, isUndefined, clone, reverse, pick, pickBy, mapValues, concat, flow, map, partial, uniq, some, keys } from 'lodash';
import { asPercent } from '../../helpers/string';

const CELL_AVERAGES_CLOSED_SINGLE_WIDTH = 120;
const CELL_AVERAGES_SINGLE_WIDTH = 90;
const IS_AVERAGES_EXPANDED_KEY = 'is_scores_averages_expanded';
const CLOSED_TO_OPENED = [CELL_AVERAGES_CLOSED_SINGLE_WIDTH, CELL_AVERAGES_SINGLE_WIDTH * 5];
const OPENED_TO_CLOSED = reverse(clone(CLOSED_TO_OPENED));
const PADDING = 80;
const ROW_HEIGHT = 50;
const TABLE_PADDING = 18;
const WINDOW_HEIGHT_PADDING = 260;

const NOT_AVAILABLE_AVERAGE = 'n/a';
const PENDING_AVERAGE = '---';

const scoreKeyToType = (key) => (key.match(/(course_average|homework|reading)/)[0])

export default class ScoresReportUX {

  COLUMN_WIDTH = 170;

  ROW_HEIGHT = ROW_HEIGHT;

  windowSize = new WindowSize();

  @computed get isAveragesExpanded() {
    const isAveragesExpanded = UiSettings.get(IS_AVERAGES_EXPANDED_KEY);
    if (isUndefined(isAveragesExpanded)) {
      return true;
    }
    return isAveragesExpanded;
  }

  @computed get fromTo() {
    if (this.isAveragesExpanded) {
      return OPENED_TO_CLOSED;
    } else {
      return CLOSED_TO_OPENED;
    }
  }

  @observable averagesWidth = first(this.fromTo);
  @observable periodIndex = 0;
  @observable weights = new WeightsUX(this);

  constructor(course) {
    this.course = course;
    course.taskPlans.fetch({});
  }

  @computed get period() {
    return this.course.scores.periods.get(
      this.course.periods.active[this.periodIndex].id
    );
  }

  isAverageUnavailableByType(type) {
    return !this.course.taskPlans[type].any;
  }

  isAverageUnavailableByTypeForPeriod(type) {
    return !this.course.taskPlans[type].withPeriodId(this.period.period_id).any;
  }

  isAveragePendingByType(type) {
    return !this.course.taskPlans[type].pastDue.any;
  }

  isAveragePendingByTypeForPeriod(type) {
    return !this.course.taskPlans[type].pastDueWithPeriodId(this.period.period_id).any;
  }

  nullAverageByType(type) {
    if (this.isAverageUnavailableByTypeForPeriod(type)) {
      return NOT_AVAILABLE_AVERAGE;
    } else if (this.isAveragePendingByTypeForPeriod(type)) {
      return PENDING_AVERAGE;
    }
  }

  // what task types is course score being weighed on?
  // e.g. ['homework'] or ['homework', 'reading']
  @computed get weightTypes() {
    return flow(
      partial(pick, partial.placeholder, this.weights.WEIGHT_KEYS),
      partial(pickBy, partial.placeholder, (weight) => weight > 0),
      keys,
      partial(map, partial.placeholder, scoreKeyToType),
      uniq,
    )(this.course);
  }

  @computed get nullAverageForCourse() {
    if (some(this.weightTypes, this.isAverageUnavailableByTypeForPeriod.bind(this))) {
      return NOT_AVAILABLE_AVERAGE;
    } else if (some(this.weightTypes, this.isAveragePendingByTypeForPeriod.bind(this))) {
      return PENDING_AVERAGE;
    }
  }

  maskAverages(averages) {
    return mapValues(averages, (average, key) => {
      const type = scoreKeyToType(key);
      let nullValue;

      if (average === 0 || average === null) {
        if (type === 'course_average') {
          nullValue = this.nullAverageForCourse;
        } else {
          nullValue = this.nullAverageByType(type);
        }
      }

      return nullValue || `${asPercent(average)}%`;
    });
  }

  @computed get periodStudentsAverages() {
    const scoreKeys = [
      'course_average',
      'homework_score',
      'homework_progress',
      'reading_score',
      'reading_progress',
    ];

    return this.period.students.map((student) => {
      let averages = pick(student, scoreKeys);
      averages = this.maskAverages(averages);
      averages.student_identifier = student.student_identifier;

      return averages;
    });
  }

  @computed get periodAverages() {
    const scoreKeys = [
      'overall_course_average',
      'overall_homework_score',
      'overall_homework_progress',
      'overall_reading_score',
      'overall_reading_progress',
    ];

    const averages = pick(this.period, scoreKeys);
    return this.maskAverages(averages);
  }

  @computed get data() {
    return this.course.scores.periods.get(this.period.period_id);
  }

  @action.bound toggleAverageExpansion() {
    let onComplete = () => {
      UiSettings.set(IS_AVERAGES_EXPANDED_KEY, !this.isAveragesExpanded);
    };

    bezierAnimation({
      range: this.fromTo,
      duration: 500, // ms
      onStep: (width) => { this.averagesWidth = width; },
      onComplete,
    });
  }

  @computed get headerHeight() {
    return (this.course.isTeacher && 180) || 140;
  }

  @computed get windowHeightPadding() {
    return (this.course.isTeacher && (WINDOW_HEIGHT_PADDING + 95)) || WINDOW_HEIGHT_PADDING;
  }

  @computed get tableWidth() {
    const extraCol = this.course.isTeacher ? 1 : 0;
    const desiredWidth = this.averagesWidth +
      (this.COLUMN_WIDTH * (this.period.numAssignments + extraCol));

    return Math.min(
      desiredWidth, (this.windowSize.width - PADDING)
    );
  }

  @computed get desiredHeight() {
    return this.period.students.length * ROW_HEIGHT + this.headerHeight + TABLE_PADDING;
  }

  @computed get expectedHeight() {
    return this.windowSize.height - this.windowHeightPadding;
  }

  @computed get tableHeight() {
    return Math.min(
      this.desiredHeight, this.expectedHeight
    );
  }

}
<commit_msg>look at null values only<commit_after>import { observable, computed, action } from 'mobx';
import bezierAnimation from '../../helpers/bezier';
import WindowSize from '../../models/window-size';
import WeightsUX from './weights-ux';
import UiSettings from 'shared/model/ui-settings';
import { first, isUndefined, clone, reverse, pick, pickBy, mapValues, concat, flow, map, partial, uniq, some, keys } from 'lodash';
import { asPercent } from '../../helpers/string';

const CELL_AVERAGES_CLOSED_SINGLE_WIDTH = 120;
const CELL_AVERAGES_SINGLE_WIDTH = 90;
const IS_AVERAGES_EXPANDED_KEY = 'is_scores_averages_expanded';
const CLOSED_TO_OPENED = [CELL_AVERAGES_CLOSED_SINGLE_WIDTH, CELL_AVERAGES_SINGLE_WIDTH * 5];
const OPENED_TO_CLOSED = reverse(clone(CLOSED_TO_OPENED));
const PADDING = 80;
const ROW_HEIGHT = 50;
const TABLE_PADDING = 18;
const WINDOW_HEIGHT_PADDING = 260;

const NOT_AVAILABLE_AVERAGE = 'n/a';
const PENDING_AVERAGE = '---';

const scoreKeyToType = (key) => (key.match(/(course_average|homework|reading)/)[0])

export default class ScoresReportUX {

  COLUMN_WIDTH = 170;

  ROW_HEIGHT = ROW_HEIGHT;

  windowSize = new WindowSize();

  @computed get isAveragesExpanded() {
    const isAveragesExpanded = UiSettings.get(IS_AVERAGES_EXPANDED_KEY);
    if (isUndefined(isAveragesExpanded)) {
      return true;
    }
    return isAveragesExpanded;
  }

  @computed get fromTo() {
    if (this.isAveragesExpanded) {
      return OPENED_TO_CLOSED;
    } else {
      return CLOSED_TO_OPENED;
    }
  }

  @observable averagesWidth = first(this.fromTo);
  @observable periodIndex = 0;
  @observable weights = new WeightsUX(this);

  constructor(course) {
    this.course = course;
    course.taskPlans.fetch({});
  }

  @computed get period() {
    return this.course.scores.periods.get(
      this.course.periods.active[this.periodIndex].id
    );
  }

  isAverageUnavailableByType(type) {
    return !this.course.taskPlans[type].any;
  }

  isAverageUnavailableByTypeForPeriod(type) {
    return !this.course.taskPlans[type].withPeriodId(this.period.period_id).any;
  }

  isAveragePendingByType(type) {
    return !this.course.taskPlans[type].pastDue.any;
  }

  isAveragePendingByTypeForPeriod(type) {
    return !this.course.taskPlans[type].pastDueWithPeriodId(this.period.period_id).any;
  }

  nullAverageByType(type) {
    if (this.isAverageUnavailableByTypeForPeriod(type)) {
      return NOT_AVAILABLE_AVERAGE;
    } else if (this.isAveragePendingByTypeForPeriod(type)) {
      return PENDING_AVERAGE;
    }
  }

  // what task types is course score being weighed on?
  // e.g. ['homework'] or ['homework', 'reading']
  @computed get weightTypes() {
    return flow(
      partial(pick, partial.placeholder, this.weights.WEIGHT_KEYS),
      partial(pickBy, partial.placeholder, (weight) => weight > 0),
      keys,
      partial(map, partial.placeholder, scoreKeyToType),
      uniq,
    )(this.course);
  }

  @computed get nullAverageForCourse() {
    if (some(this.weightTypes, this.isAverageUnavailableByTypeForPeriod.bind(this))) {
      return NOT_AVAILABLE_AVERAGE;
    } else if (some(this.weightTypes, this.isAveragePendingByTypeForPeriod.bind(this))) {
      return PENDING_AVERAGE;
    }
  }

  maskAverages(averages) {
    return mapValues(averages, (average, key) => {
      const type = scoreKeyToType(key);
      let nullValue;

      if (average === null) {
        if (type === 'course_average') {
          nullValue = this.nullAverageForCourse;
        } else {
          nullValue = this.nullAverageByType(type);
        }
      }

      return nullValue || `${asPercent(average)}%`;
    });
  }

  @computed get periodStudentsAverages() {
    const scoreKeys = [
      'course_average',
      'homework_score',
      'homework_progress',
      'reading_score',
      'reading_progress',
    ];

    return this.period.students.map((student) => {
      let averages = pick(student, scoreKeys);
      averages = this.maskAverages(averages);
      averages.student_identifier = student.student_identifier;

      return averages;
    });
  }

  @computed get periodAverages() {
    const scoreKeys = [
      'overall_course_average',
      'overall_homework_score',
      'overall_homework_progress',
      'overall_reading_score',
      'overall_reading_progress',
    ];

    const averages = pick(this.period, scoreKeys);
    return this.maskAverages(averages);
  }

  @computed get data() {
    return this.course.scores.periods.get(this.period.period_id);
  }

  @action.bound toggleAverageExpansion() {
    let onComplete = () => {
      UiSettings.set(IS_AVERAGES_EXPANDED_KEY, !this.isAveragesExpanded);
    };

    bezierAnimation({
      range: this.fromTo,
      duration: 500, // ms
      onStep: (width) => { this.averagesWidth = width; },
      onComplete,
    });
  }

  @computed get headerHeight() {
    return (this.course.isTeacher && 180) || 140;
  }

  @computed get windowHeightPadding() {
    return (this.course.isTeacher && (WINDOW_HEIGHT_PADDING + 95)) || WINDOW_HEIGHT_PADDING;
  }

  @computed get tableWidth() {
    const extraCol = this.course.isTeacher ? 1 : 0;
    const desiredWidth = this.averagesWidth +
      (this.COLUMN_WIDTH * (this.period.numAssignments + extraCol));

    return Math.min(
      desiredWidth, (this.windowSize.width - PADDING)
    );
  }

  @computed get desiredHeight() {
    return this.period.students.length * ROW_HEIGHT + this.headerHeight + TABLE_PADDING;
  }

  @computed get expectedHeight() {
    return this.windowSize.height - this.windowHeightPadding;
  }

  @computed get tableHeight() {
    return Math.min(
      this.desiredHeight, this.expectedHeight
    );
  }

}
<|endoftext|>