<commit_before>
/*
 *  Guacamole - Clientless Remote Desktop
 *  Copyright (C) 2010  Michael Jumper
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

function GuacamoleClient(display) {

    var STATE_IDLE          = 0;
    var STATE_CONNECTING    = 1;
    var STATE_WAITING       = 2;
    var STATE_CONNECTED     = 3;
    var STATE_DISCONNECTING = 4;
    var STATE_DISCONNECTED  = 5;

    var currentState = STATE_IDLE;
    var stateChangeHandler = null;

    function setState(state) {
        if (state != currentState) {
            currentState = state;
            if (stateChangeHandler)
                stateChangeHandler(currentState);
        }
    }

    this.setOnStateChangeHandler = function(handler) {
        stateChangeHandler = handler;
    }

    function isConnected() {
        return currentState == STATE_CONNECTED
            || currentState == STATE_WAITING;
    }

    // Layers
    var background = null;
    var cursor = null;

    var cursorImage = null;
    var cursorHotspotX = 0;
    var cursorHotspotY = 0;

    // FIXME: Make object. Clean up.
    var cursorRectX = 0;
    var cursorRectY = 0;
    var cursorRectW = 0;
    var cursorRectH = 0;

    var cursorHidden = 0;

    function redrawCursor() {

        // Hide hardware cursor
        if (cursorHidden == 0) {
            display.className += " hideCursor";
            cursorHidden = 1;
        }

        // Erase old cursor
        cursor.clearRect(cursorRectX, cursorRectY, cursorRectW, cursorRectH);

        // Update rect
        cursorRectX = mouse.getX() - cursorHotspotX;
        cursorRectY = mouse.getY() - cursorHotspotY;
        cursorRectW = cursorImage.width;
        cursorRectH = cursorImage.height;

        // Draw new cursor
        cursor.drawImage(cursorRectX, cursorRectY, cursorImage);
    }

	/*****************************************/
	/*** Keyboard                          ***/
	/*****************************************/

    var keyboard = new GuacamoleKeyboard(document);

    this.disableKeyboard = function() {
        keyboard.setKeyPressedHandler(null);
        keyboard.setKeyReleasedHandler(null);
    };

    this.enableKeyboard = function() {
        keyboard.setKeyPressedHandler(
            function (keysym) {
                sendKeyEvent(1, keysym);
            }
        );

        keyboard.setKeyReleasedHandler(
            function (keysym) {
                sendKeyEvent(0, keysym);
            }
        );
    };

    // Enable keyboard by default
    this.enableKeyboard();

    function sendKeyEvent(pressed, keysym) {
        // Do not send requests if not connected
        if (!isConnected())
            return;

        sendMessage("key:" +  keysym + "," + pressed + ";");
    }

    this.pressKey = function(keysym) {
        sendKeyEvent(1, keysym);
    };

    this.releaseKey = function(keysym) {
        sendKeyEvent(0, keysym);
    };

	/*****************************************/
	/*** Mouse                             ***/
	/*****************************************/

    var mouse = new GuacamoleMouse(display);
    mouse.setButtonPressedHandler(
        function(mouseState) {
            sendMouseState(mouseState);
        }
    );

    mouse.setButtonReleasedHandler(
        function(mouseState) {
            sendMouseState(mouseState);
        }
    );

    mouse.setMovementHandler(
        function(mouseState) {

            // Draw client-side cursor
            if (cursorImage != null) {
                redrawCursor();
            }

            sendMouseState(mouseState);
        }
    );

    function sendMouseState(mouseState) {

        // Do not send requests if not connected
        if (!isConnected())
            return;

        // Build mask
        var buttonMask = 0;
        if (mouseState.getLeft())   buttonMask |= 1;
        if (mouseState.getMiddle()) buttonMask |= 2;
        if (mouseState.getRight())  buttonMask |= 4;
        if (mouseState.getUp())     buttonMask |= 8;
        if (mouseState.getDown())   buttonMask |= 16;

        // Send message
        sendMessage("mouse:" + mouseState.getX() + "," + mouseState.getY() + "," + buttonMask + ";");
    }

    var sendingMessages = 0;
    var outputMessageBuffer = "";

    function sendMessage(message) {

        // Add event to queue, restart send loop if finished.
        outputMessageBuffer += message;
        if (sendingMessages == 0)
            sendPendingMessages();

    }

    function sendPendingMessages() {

        if (outputMessageBuffer.length > 0) {

            sendingMessages = 1;

            var message_xmlhttprequest = new XMLHttpRequest();
            message_xmlhttprequest.open("POST", "inbound");
            message_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            message_xmlhttprequest.setRequestHeader("Content-length", outputMessageBuffer.length);

            // Once response received, send next queued event.
            message_xmlhttprequest.onreadystatechange = function() {
                if (message_xmlhttprequest.readyState == 4)
                    sendPendingMessages();
            }

            message_xmlhttprequest.send(outputMessageBuffer);
            outputMessageBuffer = ""; // Clear buffer

        }
        else
            sendingMessages = 0;

    }

	/*****************************************/
	/*** Clipboard                         ***/
	/*****************************************/

    this.setClipboard = function(data) {

        // Do not send requests if not connected
        if (!isConnected())
            return;

        sendMessage("clipboard:" + escapeGuacamoleString(data) + ";");
    }

    function desaturateFilter(data, width, height) {

        for (var i=0; i<data.length; i+=4) {

            // Get RGB values
            var r = data[i];
            var g = data[i+1];
            var b = data[i+2];

            // Desaturate
            var v = Math.max(r, g, b) / 2;
            data[i]   = v;
            data[i+1] = v;
            data[i+2] = v;

        }

    }

    var errorHandler = null;
    this.setErrorHandler = function(handler) {
        errorHandler = handler;
    };

    var errorEncountered = 0;
    function showError(error) {
        // Only display first error (avoid infinite error loops)
        if (errorEncountered == 0) {
            errorEncountered = 1;

            disconnect();

            // In case nothing has been rendered yet, use error style
            display.className += " error";

            // Show error by desaturating display
            if (background)
                background.filter(desaturateFilter);

            if (errorHandler)
                errorHandler(error);
        }
    }

    function handleErrors(message) {
        var errors = message.getErrors();
        for (var errorIndex=0; errorIndex<errors.length; errorIndex++)
            showError(errors[errorIndex].getMessage());
    }

    var clipboardHandler = null;
    var requests = 0;

    this.setClipboardHandler = function(handler) {
        clipboardHandler = handler;
    };

    function handleResponse(xmlhttprequest) {

        var nextRequest = null;

        var instructionStart = 0;
        var startIndex = 0;

        function parseResponse() {

            // Start next request as soon as possible
            if (xmlhttprequest.readyState >= 2 && nextRequest == null)
                nextRequest = makeRequest();

            // Parse stream when data is received and when complete.
            if (xmlhttprequest.readyState == 3 ||
                xmlhttprequest.readyState == 4) {

                // Halt on error during request
                if (xmlhttprequest.status == 0) {
                    showError("Request canceled by browser.");
                    return;
                }
                else if (xmlhttprequest.status != 200) {
                    showError("Error during request (HTTP " + xmlhttprequest.status + "): " + xmlhttprequest.statusText);
                    return;
                }

                var current = xmlhttprequest.responseText;
                var instructionEnd;

                while ((instructionEnd = current.indexOf(";", startIndex)) != -1) {

                    // Start next search at next instruction
                    startIndex = instructionEnd+1;

                    var instruction = current.substr(instructionStart,
                            instructionEnd - instructionStart);

                    instructionStart = startIndex;

                    var opcodeEnd = instruction.indexOf(":");

                    var opcode;
                    var parameters;
                    if (opcodeEnd == -1) {
                        opcode = instruction;
                        parameters = new Array();
                    }
                    else {
                        opcode = instruction.substr(0, opcodeEnd);
                        parameters = instruction.substr(opcodeEnd+1).split(",");
                    }

                    // If we're done parsing, handle the next response.
                    if (opcode.length == 0) {

                        if (isConnected()) {
                            delete xmlhttprequest;
                            if (nextRequest)
                                handleResponse(nextRequest);
                        }

                        break;
                    }

                    // Call instruction handler.
                    doInstruction(opcode, parameters);
                }

                // Start search at end of string.
                startIndex = current.length;

                delete instruction;
                delete parameters;

            }

        }

        xmlhttprequest.onreadystatechange = parseResponse;
        parseResponse();

    }

    function makeRequest() {

        // Download self
        var xmlhttprequest = new XMLHttpRequest();
        xmlhttprequest.open("POST", "outbound");
        xmlhttprequest.send(null);

        return xmlhttprequest;

    }

    function escapeGuacamoleString(str) {

        var escapedString = "";

        for (var i=0; i<str.length; i++) {

            var c = str.charAt(i);
            if (c == ",")
                escapedString += "\\c";
            else if (c == ";")
                escapedString += "\\s";
            else if (c == "\\")
                escapedString += "\\\\";
            else
                escapedString += c;

        }

        return escapedString;

    }

    function unescapeGuacamoleString(str) {

        var unescapedString = "";

        for (var i=0; i<str.length; i++) {

            var c = str.charAt(i);
            if (c == "\\" && i<str.length-1) {

                var escapeChar = str.charAt(++i);
                if (escapeChar == "c")
                    unescapedString += ",";
                else if (escapeChar == "s")
                    unescapedString += ";";
                else if (escapeChar == "\\")
                    unescapedString += "\\";
                else
                    unescapedString += "\\" + escapeChar;

            }
            else
                unescapedString += c;

        }

        return unescapedString;

    }

    var instructionHandlers = {

        "error": function(parameters) {
            showError(unescapeGuacamoleString(parameters[0]));
        },

        "name": function(parameters) {
            document.title = unescapeGuacamoleString(parameters[0]);
        },

        "clipboard": function(parameters) {
            clipboardHandler(unescapeGuacamoleString(parameters[0]));
        },

        "size": function(parameters) {

            var width = parseInt(parameters[0]);
            var height = parseInt(parameters[1]);

            // Update (set) display size
            if (display && (background == null || cursor == null)) {
                display.style.width = width + "px";
                display.style.height = height + "px";

                background = new Layer(width, height);
                cursor = new Layer(width, height);

                display.appendChild(background);
                display.appendChild(cursor);
            }

        },

        "rect": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var w = parseInt(parameters[2]);
            var h = parseInt(parameters[3]);
            var color = parameters[4];

            background.drawRect(
                x,
                y,
                w,
                h,
                color
            );

        },

        "png": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var data = parameters[2];

            background.draw(
                x,
                y,
                "data:image/png;base64," + data
            );

            // If received first update, no longer waiting.
            if (currentState == STATE_WAITING)
                setState(STATE_CONNECTED);

        },

        "copy": function(parameters) {

            var srcX = parseInt(parameters[0]);
            var srcY = parseInt(parameters[1]);
            var srcWidth = parseInt(parameters[2]);
            var srcHeight = parseInt(parameters[3]);
            var dstX = parseInt(parameters[4]);
            var dstY = parseInt(parameters[5]);

            background.copyRect(
                srcX,
                srcY,
                srcWidth,
                srcHeight,
                dstX,
                dstY
            );

        },

        "cursor": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var data = parameters[2];

            // Start cursor image load
            var image = new Image();
            image.onload = function() {
                cursorImage = image;
                cursorHotspotX = x;
                cursorHotspotY = y;
                redrawCursor();
            };
            image.src = "data:image/png;base64," + data

        }

    };

    function doInstruction(opcode, parameters) {

        var handler = instructionHandlers[opcode];
        if (handler)
            handler(parameters);

    }

    this.connect = function() {

        var message = "connect;";

        setState(STATE_CONNECTING);

        // Send connect message (synchronously... as necessary until handoff is implemented)
        var connect_xmlhttprequest = new XMLHttpRequest();
        connect_xmlhttprequest.open("POST", "inbound", false);
        connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        connect_xmlhttprequest.setRequestHeader("Content-length", message.length);
        connect_xmlhttprequest.send(message);

        // Start reading data
        setState(STATE_WAITING);
        handleResponse(makeRequest());

    };

    function disconnect() {

        // Only attempt disconnection not disconnected.
        if (currentState != STATE_DISCONNECTED
                && currentState != STATE_DISCONNECTING) {

            var message = "disconnect;";
            setState(STATE_DISCONNECTING);

            // Send disconnect message (synchronously... as necessary until handoff is implemented)
            var disconnect_xmlhttprequest = new XMLHttpRequest();
            disconnect_xmlhttprequest.open("POST", "inbound", false);
            disconnect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            disconnect_xmlhttprequest.setRequestHeader("Content-length", message.length);
            disconnect_xmlhttprequest.send(message);

            setState(STATE_DISCONNECTED);
        }

    }

    this.disconnect = disconnect;

}
<commit_msg>Use new connect servlet<commit_after>
/*
 *  Guacamole - Clientless Remote Desktop
 *  Copyright (C) 2010  Michael Jumper
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

function GuacamoleClient(display) {

    var STATE_IDLE          = 0;
    var STATE_CONNECTING    = 1;
    var STATE_WAITING       = 2;
    var STATE_CONNECTED     = 3;
    var STATE_DISCONNECTING = 4;
    var STATE_DISCONNECTED  = 5;

    var currentState = STATE_IDLE;
    var stateChangeHandler = null;

    function setState(state) {
        if (state != currentState) {
            currentState = state;
            if (stateChangeHandler)
                stateChangeHandler(currentState);
        }
    }

    this.setOnStateChangeHandler = function(handler) {
        stateChangeHandler = handler;
    }

    function isConnected() {
        return currentState == STATE_CONNECTED
            || currentState == STATE_WAITING;
    }

    // Layers
    var background = null;
    var cursor = null;

    var cursorImage = null;
    var cursorHotspotX = 0;
    var cursorHotspotY = 0;

    // FIXME: Make object. Clean up.
    var cursorRectX = 0;
    var cursorRectY = 0;
    var cursorRectW = 0;
    var cursorRectH = 0;

    var cursorHidden = 0;

    function redrawCursor() {

        // Hide hardware cursor
        if (cursorHidden == 0) {
            display.className += " hideCursor";
            cursorHidden = 1;
        }

        // Erase old cursor
        cursor.clearRect(cursorRectX, cursorRectY, cursorRectW, cursorRectH);

        // Update rect
        cursorRectX = mouse.getX() - cursorHotspotX;
        cursorRectY = mouse.getY() - cursorHotspotY;
        cursorRectW = cursorImage.width;
        cursorRectH = cursorImage.height;

        // Draw new cursor
        cursor.drawImage(cursorRectX, cursorRectY, cursorImage);
    }

	/*****************************************/
	/*** Keyboard                          ***/
	/*****************************************/

    var keyboard = new GuacamoleKeyboard(document);

    this.disableKeyboard = function() {
        keyboard.setKeyPressedHandler(null);
        keyboard.setKeyReleasedHandler(null);
    };

    this.enableKeyboard = function() {
        keyboard.setKeyPressedHandler(
            function (keysym) {
                sendKeyEvent(1, keysym);
            }
        );

        keyboard.setKeyReleasedHandler(
            function (keysym) {
                sendKeyEvent(0, keysym);
            }
        );
    };

    // Enable keyboard by default
    this.enableKeyboard();

    function sendKeyEvent(pressed, keysym) {
        // Do not send requests if not connected
        if (!isConnected())
            return;

        sendMessage("key:" +  keysym + "," + pressed + ";");
    }

    this.pressKey = function(keysym) {
        sendKeyEvent(1, keysym);
    };

    this.releaseKey = function(keysym) {
        sendKeyEvent(0, keysym);
    };

	/*****************************************/
	/*** Mouse                             ***/
	/*****************************************/

    var mouse = new GuacamoleMouse(display);
    mouse.setButtonPressedHandler(
        function(mouseState) {
            sendMouseState(mouseState);
        }
    );

    mouse.setButtonReleasedHandler(
        function(mouseState) {
            sendMouseState(mouseState);
        }
    );

    mouse.setMovementHandler(
        function(mouseState) {

            // Draw client-side cursor
            if (cursorImage != null) {
                redrawCursor();
            }

            sendMouseState(mouseState);
        }
    );

    function sendMouseState(mouseState) {

        // Do not send requests if not connected
        if (!isConnected())
            return;

        // Build mask
        var buttonMask = 0;
        if (mouseState.getLeft())   buttonMask |= 1;
        if (mouseState.getMiddle()) buttonMask |= 2;
        if (mouseState.getRight())  buttonMask |= 4;
        if (mouseState.getUp())     buttonMask |= 8;
        if (mouseState.getDown())   buttonMask |= 16;

        // Send message
        sendMessage("mouse:" + mouseState.getX() + "," + mouseState.getY() + "," + buttonMask + ";");
    }

    var sendingMessages = 0;
    var outputMessageBuffer = "";

    function sendMessage(message) {

        // Add event to queue, restart send loop if finished.
        outputMessageBuffer += message;
        if (sendingMessages == 0)
            sendPendingMessages();

    }

    function sendPendingMessages() {

        if (outputMessageBuffer.length > 0) {

            sendingMessages = 1;

            var message_xmlhttprequest = new XMLHttpRequest();
            message_xmlhttprequest.open("POST", "inbound");
            message_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            message_xmlhttprequest.setRequestHeader("Content-length", outputMessageBuffer.length);

            // Once response received, send next queued event.
            message_xmlhttprequest.onreadystatechange = function() {
                if (message_xmlhttprequest.readyState == 4)
                    sendPendingMessages();
            }

            message_xmlhttprequest.send(outputMessageBuffer);
            outputMessageBuffer = ""; // Clear buffer

        }
        else
            sendingMessages = 0;

    }

	/*****************************************/
	/*** Clipboard                         ***/
	/*****************************************/

    this.setClipboard = function(data) {

        // Do not send requests if not connected
        if (!isConnected())
            return;

        sendMessage("clipboard:" + escapeGuacamoleString(data) + ";");
    }

    function desaturateFilter(data, width, height) {

        for (var i=0; i<data.length; i+=4) {

            // Get RGB values
            var r = data[i];
            var g = data[i+1];
            var b = data[i+2];

            // Desaturate
            var v = Math.max(r, g, b) / 2;
            data[i]   = v;
            data[i+1] = v;
            data[i+2] = v;

        }

    }

    var errorHandler = null;
    this.setErrorHandler = function(handler) {
        errorHandler = handler;
    };

    var errorEncountered = 0;
    function showError(error) {
        // Only display first error (avoid infinite error loops)
        if (errorEncountered == 0) {
            errorEncountered = 1;

            disconnect();

            // In case nothing has been rendered yet, use error style
            display.className += " error";

            // Show error by desaturating display
            if (background)
                background.filter(desaturateFilter);

            if (errorHandler)
                errorHandler(error);
        }
    }

    function handleErrors(message) {
        var errors = message.getErrors();
        for (var errorIndex=0; errorIndex<errors.length; errorIndex++)
            showError(errors[errorIndex].getMessage());
    }

    var clipboardHandler = null;
    var requests = 0;

    this.setClipboardHandler = function(handler) {
        clipboardHandler = handler;
    };

    function handleResponse(xmlhttprequest) {

        var nextRequest = null;

        var instructionStart = 0;
        var startIndex = 0;

        function parseResponse() {

            // Start next request as soon as possible
            if (xmlhttprequest.readyState >= 2 && nextRequest == null)
                nextRequest = makeRequest();

            // Parse stream when data is received and when complete.
            if (xmlhttprequest.readyState == 3 ||
                xmlhttprequest.readyState == 4) {

                // Halt on error during request
                if (xmlhttprequest.status == 0) {
                    showError("Request canceled by browser.");
                    return;
                }
                else if (xmlhttprequest.status != 200) {
                    showError("Error during request (HTTP " + xmlhttprequest.status + "): " + xmlhttprequest.statusText);
                    return;
                }

                var current = xmlhttprequest.responseText;
                var instructionEnd;

                while ((instructionEnd = current.indexOf(";", startIndex)) != -1) {

                    // Start next search at next instruction
                    startIndex = instructionEnd+1;

                    var instruction = current.substr(instructionStart,
                            instructionEnd - instructionStart);

                    instructionStart = startIndex;

                    var opcodeEnd = instruction.indexOf(":");

                    var opcode;
                    var parameters;
                    if (opcodeEnd == -1) {
                        opcode = instruction;
                        parameters = new Array();
                    }
                    else {
                        opcode = instruction.substr(0, opcodeEnd);
                        parameters = instruction.substr(opcodeEnd+1).split(",");
                    }

                    // If we're done parsing, handle the next response.
                    if (opcode.length == 0) {

                        if (isConnected()) {
                            delete xmlhttprequest;
                            if (nextRequest)
                                handleResponse(nextRequest);
                        }

                        break;
                    }

                    // Call instruction handler.
                    doInstruction(opcode, parameters);
                }

                // Start search at end of string.
                startIndex = current.length;

                delete instruction;
                delete parameters;

            }

        }

        xmlhttprequest.onreadystatechange = parseResponse;
        parseResponse();

    }

    function makeRequest() {

        // Download self
        var xmlhttprequest = new XMLHttpRequest();
        xmlhttprequest.open("POST", "outbound");
        xmlhttprequest.send(null);

        return xmlhttprequest;

    }

    function escapeGuacamoleString(str) {

        var escapedString = "";

        for (var i=0; i<str.length; i++) {

            var c = str.charAt(i);
            if (c == ",")
                escapedString += "\\c";
            else if (c == ";")
                escapedString += "\\s";
            else if (c == "\\")
                escapedString += "\\\\";
            else
                escapedString += c;

        }

        return escapedString;

    }

    function unescapeGuacamoleString(str) {

        var unescapedString = "";

        for (var i=0; i<str.length; i++) {

            var c = str.charAt(i);
            if (c == "\\" && i<str.length-1) {

                var escapeChar = str.charAt(++i);
                if (escapeChar == "c")
                    unescapedString += ",";
                else if (escapeChar == "s")
                    unescapedString += ";";
                else if (escapeChar == "\\")
                    unescapedString += "\\";
                else
                    unescapedString += "\\" + escapeChar;

            }
            else
                unescapedString += c;

        }

        return unescapedString;

    }

    var instructionHandlers = {

        "error": function(parameters) {
            showError(unescapeGuacamoleString(parameters[0]));
        },

        "name": function(parameters) {
            document.title = unescapeGuacamoleString(parameters[0]);
        },

        "clipboard": function(parameters) {
            clipboardHandler(unescapeGuacamoleString(parameters[0]));
        },

        "size": function(parameters) {

            var width = parseInt(parameters[0]);
            var height = parseInt(parameters[1]);

            // Update (set) display size
            if (display && (background == null || cursor == null)) {
                display.style.width = width + "px";
                display.style.height = height + "px";

                background = new Layer(width, height);
                cursor = new Layer(width, height);

                display.appendChild(background);
                display.appendChild(cursor);
            }

        },

        "rect": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var w = parseInt(parameters[2]);
            var h = parseInt(parameters[3]);
            var color = parameters[4];

            background.drawRect(
                x,
                y,
                w,
                h,
                color
            );

        },

        "png": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var data = parameters[2];

            background.draw(
                x,
                y,
                "data:image/png;base64," + data
            );

            // If received first update, no longer waiting.
            if (currentState == STATE_WAITING)
                setState(STATE_CONNECTED);

        },

        "copy": function(parameters) {

            var srcX = parseInt(parameters[0]);
            var srcY = parseInt(parameters[1]);
            var srcWidth = parseInt(parameters[2]);
            var srcHeight = parseInt(parameters[3]);
            var dstX = parseInt(parameters[4]);
            var dstY = parseInt(parameters[5]);

            background.copyRect(
                srcX,
                srcY,
                srcWidth,
                srcHeight,
                dstX,
                dstY
            );

        },

        "cursor": function(parameters) {

            var x = parseInt(parameters[0]);
            var y = parseInt(parameters[1]);
            var data = parameters[2];

            // Start cursor image load
            var image = new Image();
            image.onload = function() {
                cursorImage = image;
                cursorHotspotX = x;
                cursorHotspotY = y;
                redrawCursor();
            };
            image.src = "data:image/png;base64," + data

        }

    };

    function doInstruction(opcode, parameters) {

        var handler = instructionHandlers[opcode];
        if (handler)
            handler(parameters);

    }

    this.connect = function() {

        setState(STATE_CONNECTING);

        // Start tunnel and connect synchronously
        var connect_xmlhttprequest = new XMLHttpRequest();
        connect_xmlhttprequest.open("POST", "connect", false);
        connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        connect_xmlhttprequest.setRequestHeader("Content-length", 0);
        connect_xmlhttprequest.send(null);

        // Start reading data
        setState(STATE_WAITING);
        handleResponse(makeRequest());

    };

    function disconnect() {

        // Only attempt disconnection not disconnected.
        if (currentState != STATE_DISCONNECTED
                && currentState != STATE_DISCONNECTING) {

            var message = "disconnect;";
            setState(STATE_DISCONNECTING);

            // Send disconnect message (synchronously... as necessary until handoff is implemented)
            var disconnect_xmlhttprequest = new XMLHttpRequest();
            disconnect_xmlhttprequest.open("POST", "inbound", false);
            disconnect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            disconnect_xmlhttprequest.setRequestHeader("Content-length", message.length);
            disconnect_xmlhttprequest.send(message);

            setState(STATE_DISCONNECTED);
        }

    }

    this.disconnect = disconnect;

}
<|endoftext|>


<commit_before>// Code goes here

var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']);

function MyController($scope, $http) {

  $scope.currentPage = 1;
  $scope.pageSize = 20;
  $scope.cart = [{
            "Name" : "Demo",
            "Price" : 10
        }];

 $http.get("entity.json")
    .then(function(response) {
        $scope.entity = response.data;
    });

  $scope.pageChangeHandler = function(num) {

  };
}

function OtherController($scope) {
  $scope.pageChangeHandler = function(num) {

  };
}

myApp.controller('MyController', MyController);
myApp.controller('OtherController', OtherController);
<commit_msg>Update app.js<commit_after>// Code goes here

var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']);

function MyController($scope, $http) {

  $scope.currentPage = 1;
  $scope.pageSize = 20;
  $scope.cart = [{
            "Name" : "Demo Riga 1",
            "Price" : 10
        },
        {
            "Name" : "Demo Riga 2",
            "Price" : 5
        }];

 $http.get("entity.json")
    .then(function(response) {
        $scope.entity = response.data;
    });

  $scope.pageChangeHandler = function(num) {

  };
}

function OtherController($scope) {
  $scope.pageChangeHandler = function(num) {

  };
}

myApp.controller('MyController', MyController);
myApp.controller('OtherController', OtherController);
<|endoftext|>


<commit_before>// Code goes here

var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']);

function MyController($scope, $http) {

  $scope.currentPage = 1;
  $scope.pageSize = 20;
  $scope.cart = [{
            "Name" : "Demo Riga 1",
            "Price" : 10
        },
        {
            "Name" : "Demo Riga 2",
            "Price" : 5
        }];

 $scope.getTotal = function(){
    var total = 0;
    for(var i = 0; i < $scope.cart.length; i++){
        var product = $scope.cart[i];
        total += (product.Price);
    }
    return total;
}

 $scope.addItem = function (item,pr) {

        $scope.cart.push({
            Name: item.Name,
            Price:pr.Value
        });
    };

$scope.removeItem = function(item) {
  var index = $scope.cart.indexOf(item);
  $scope.cart.splice(index, 1);
}

 $http.get("entity.json")
    .then(function(response) {
        $scope.entity = response.data;
    });

  $scope.pageChangeHandler = function(num) {

  };
}

function OtherController($scope) {
  $scope.pageChangeHandler = function(num) {

  };
}

myApp.controller('MyController', MyController);
myApp.controller('OtherController', OtherController);
<commit_msg>Update app.js<commit_after>// Code goes here

var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']);

function MyController($scope, $http) {

  $scope.currentPage = 1;
  $scope.pageSize = 20;
  $scope.cart = [];

 $scope.getTotal = function(){
    var total = 0;
    for(var i = 0; i < $scope.cart.length; i++){
        var product = $scope.cart[i];
        total += (product.Price);
    }
    return total;
}

 $scope.addItem = function (item,pr) {

        $scope.cart.push({
            Name: item.Name,
            Price:pr.Value
        });
    };

$scope.removeItem = function(item) {
  var index = $scope.cart.indexOf(item);
  $scope.cart.splice(index, 1);
}

 $http.get("entity.json")
    .then(function(response) {
        $scope.entity = response.data;
    });

  $scope.pageChangeHandler = function(num) {

  };
}

function OtherController($scope) {
  $scope.pageChangeHandler = function(num) {

  };
}

myApp.controller('MyController', MyController);
myApp.controller('OtherController', OtherController);
<|endoftext|>


<commit_before>#!/usr/bin/env node

var getopt = require('node-getopt')
var fasta = require('bionode-fasta')
var colors = require('colors')
var fast5 = require('./fast5')

var defaultStrand = 0
var matchScore = +5, mismatchScore = -4, gapScore = -7

var parser = getopt.create([
  ['q' , 'query=PATH'      , 'FASTA query file'],
  ['r' , 'reference=PATH'  , 'FAST5 reference file'],
  ['s' , 'strand=N'        , 'strand (0, 1, 2; default ' + defaultStrand + ')'],
  ['g' , 'group=GROUP'     , 'group name (000, RN_001, ...)'],
  ['h' , 'help'            , 'display this help message']
])              // create Getopt instance
.bindHelp()     // bind option 'help' to default action
var opt = parser.parseSystem(); // parse command line

function inputError(err) {
  parser.showHelp()
  console.warn (err, "\n")
  process.exit(1)
}

var queryFilename = opt.options.query || inputError("Please specify a query file")
var refFilename = opt.options.reference || inputError("Please specify a reference file")
var strand = opt.options.strand || defaultStrand
var group = opt.options.group || ''

var fast5file = new fast5.File (refFilename)
var refSeq = fast5file.get_basecall_seq(strand,group).toLowerCase()

fasta.obj(queryFilename).on ('data', function (data) {
  var querySeq = data.seq.toLowerCase()
  var alignInfo = align (refSeq, querySeq, matchScore, mismatchScore, gapScore)
  prettyPrint (alignInfo.alignment, ['ref', 'query'])
  var matches = alignInfo.alignment.filter (function (col) { return col[0] === col[1] && col[0] !== '-' }).length
  var deletions = alignInfo.alignment.filter (function (col) { return col[1] === '-' }).length
  var insertions = alignInfo.alignment.filter (function (col) { return col[0] === '-' }).length
  var mismatches = alignInfo.alignment.length - matches - insertions - deletions
  console.log ("Score: " + alignInfo.score + " (match " + matchScore + ", mismatch "+ mismatchScore + ", gap " + gapScore + ")")
  console.log (matches + " matches, " + mismatches + " mismatches, " + insertions + " insertions, " + deletions + " deletions")
  console.log ("Reference length " + refSeq.length + " (" + Math.round(100*matches/refSeq.length) + "% identical)")
  console.log ("Query length " + querySeq.length + " (" + Math.round(100*matches/querySeq.length) + "% identical)")
})

function align(xSeq,ySeq,matchScore,mismatchScore,gapScore) {
  var xLen = xSeq.length, yLen = ySeq.length
  var matrix = new Int32Array ((xLen + 1) * (yLen + 1))
  function cellIndex(i,j) { return i*(yLen+1) + j }
  function incoming(i,j) {
    return [matrix[cellIndex(i-1,j-1)] + (xSeq.charAt(i-1) === ySeq.charAt(j-1) ? matchScore : mismatchScore),
	    matrix[cellIndex(i,j-1)] + gapScore,
	    matrix[cellIndex(i-1,j)] + gapScore]
  }
  matrix[0] = 0
  for (var i = 1; i <= xLen; ++i)
    matrix[cellIndex(i,0)] = i*gapScore
  for (var j = 1; j <= yLen; ++j)
    matrix[cellIndex(0,j)] = j*gapScore
  for (var i = 1; i <= xLen; ++i)
    for (var j = 1; j <= yLen; ++j)
      matrix[cellIndex(i,j)] = Math.max.apply (Math, incoming(i,j))
  var iTrace = xLen, jTrace = yLen, alignment = []
  while (iTrace > 0 && jTrace > 0) {
    var sc = incoming (iTrace, jTrace)
    var dir = sc.indexOf (matrix[cellIndex (iTrace, jTrace)])
    switch (dir) {
    case 0: alignment.push ([xSeq.charAt(--iTrace),ySeq.charAt(--jTrace)]); break;
    case 1: alignment.push (['-',ySeq.charAt(--jTrace)]); break;
    case 2: alignment.push ([xSeq.charAt(--iTrace),'-']); break;
    default: throw new Error("traceback error"); break;
    }
  }
  while (iTrace) alignment.push ([xSeq.charAt(--iTrace),'-'])
  while (jTrace) alignment.push (['-',ySeq.charAt(--jTrace)])
  alignment.reverse()
  return { alignment: alignment,
	   score: matrix[cellIndex(xLen,yLen)] }
}

function prettyPrint (alignment, labels, refRowIndex) {
  refRowIndex = refRowIndex || 0
  alignment.forEach (function (colData, colIndex) {
    if (colData.length != labels.length)
      throw new Error ("Column #" + (colIndex+1) + " has " + colData.length + " rows, but there are " + labels.length + " row labels")
  })
  var labelCols = Math.max.apply (Math, labels.map (function (label) { return label.length }))
  var alignCols = process.stdout.columns - labelCols - 1
  for (var colStart = 0; colStart < alignment.length; colStart += alignCols) {
    labels.forEach (function (rowLabel, rowIndex) {
      process.stdout.write (rowLabel + new Array(labelCols+1-rowLabel.length).fill(' ').join(''))
      for (var colIndex = colStart; colIndex < colStart + alignCols && colIndex < alignment.length; ++colIndex) {
	var alignChar = alignment[colIndex][rowIndex], refChar = alignment[colIndex][refRowIndex]
	process.stdout.write (alignChar === refChar ? alignChar : alignChar.inverse)
      }
      process.stdout.write ("\n")
    })
    process.stdout.write ("\n")
  }
}
<commit_msg>More colorization in compare.js<commit_after>#!/usr/bin/env node

var getopt = require('node-getopt')
var fasta = require('bionode-fasta')
var colors = require('colors')
var fast5 = require('./fast5')

var defaultStrand = 0
var matchScore = +5, mismatchScore = -4, gapScore = -7

var parser = getopt.create([
  ['q' , 'query=PATH'      , 'FASTA query file'],
  ['r' , 'reference=PATH'  , 'FAST5 reference file'],
  ['s' , 'strand=N'        , 'strand (0, 1, 2; default ' + defaultStrand + ')'],
  ['g' , 'group=GROUP'     , 'group name (000, RN_001, ...)'],
  ['h' , 'help'            , 'display this help message']
])              // create Getopt instance
.bindHelp()     // bind option 'help' to default action
var opt = parser.parseSystem(); // parse command line

function inputError(err) {
  parser.showHelp()
  console.warn (err, "\n")
  process.exit(1)
}

var queryFilename = opt.options.query || inputError("Please specify a query file")
var refFilename = opt.options.reference || inputError("Please specify a reference file")
var strand = opt.options.strand || defaultStrand
var group = opt.options.group || ''

var fast5file = new fast5.File (refFilename)
var refSeq = fast5file.get_basecall_seq(strand,group).toLowerCase()

fasta.obj(queryFilename).on ('data', function (data) {
  var querySeq = data.seq.toLowerCase()
  var alignInfo = align (refSeq, querySeq, matchScore, mismatchScore, gapScore)
  prettyPrint (alignInfo.alignment, ['ref', 'query'])
  var matches = alignInfo.alignment.filter (function (col) { return col[0] === col[1] && col[0] !== '-' }).length
  var deletions = alignInfo.alignment.filter (function (col) { return col[1] === '-' }).length
  var insertions = alignInfo.alignment.filter (function (col) { return col[0] === '-' }).length
  var mismatches = alignInfo.alignment.length - matches - insertions - deletions
  console.log ("Score: " + alignInfo.score + " (match " + matchScore + ", mismatch "+ mismatchScore + ", gap " + gapScore + ")")
  console.log (matches + " matches, " + (mismatches + " " + "mismatches").bgCyan.blue + ", " + (insertions + " " + "insertions").green + ", " + (deletions + " " + "deletions").bgRed.white)
  console.log ("Reference length " + refSeq.length + " (" + Math.round(100*matches/refSeq.length) + "% identical)")
  console.log ("Query length " + querySeq.length + " (" + Math.round(100*matches/querySeq.length) + "% identical)")
})

function align(xSeq,ySeq,matchScore,mismatchScore,gapScore) {
  var xLen = xSeq.length, yLen = ySeq.length
  var matrix = new Int32Array ((xLen + 1) * (yLen + 1))
  function cellIndex(i,j) { return i*(yLen+1) + j }
  function incoming(i,j) {
    return [matrix[cellIndex(i-1,j-1)] + (xSeq.charAt(i-1) === ySeq.charAt(j-1) ? matchScore : mismatchScore),
	    matrix[cellIndex(i,j-1)] + gapScore,
	    matrix[cellIndex(i-1,j)] + gapScore]
  }
  matrix[0] = 0
  for (var i = 1; i <= xLen; ++i)
    matrix[cellIndex(i,0)] = i*gapScore
  for (var j = 1; j <= yLen; ++j)
    matrix[cellIndex(0,j)] = j*gapScore
  for (var i = 1; i <= xLen; ++i)
    for (var j = 1; j <= yLen; ++j)
      matrix[cellIndex(i,j)] = Math.max.apply (Math, incoming(i,j))
  var iTrace = xLen, jTrace = yLen, alignment = []
  while (iTrace > 0 && jTrace > 0) {
    var sc = incoming (iTrace, jTrace)
    var dir = sc.indexOf (matrix[cellIndex (iTrace, jTrace)])
    switch (dir) {
    case 0: alignment.push ([xSeq.charAt(--iTrace),ySeq.charAt(--jTrace)]); break;
    case 1: alignment.push (['-',ySeq.charAt(--jTrace)]); break;
    case 2: alignment.push ([xSeq.charAt(--iTrace),'-']); break;
    default: throw new Error("traceback error"); break;
    }
  }
  while (iTrace) alignment.push ([xSeq.charAt(--iTrace),'-'])
  while (jTrace) alignment.push (['-',ySeq.charAt(--jTrace)])
  alignment.reverse()
  return { alignment: alignment,
	   score: matrix[cellIndex(xLen,yLen)] }
}

function prettyPrint (alignment, labels, refRowIndex) {
  refRowIndex = refRowIndex || 0
  alignment.forEach (function (colData, colIndex) {
    if (colData.length != labels.length)
      throw new Error ("Column #" + (colIndex+1) + " has " + colData.length + " rows, but there are " + labels.length + " row labels")
  })
  var labelCols = Math.max.apply (Math, labels.map (function (label) { return label.length }))
  var alignCols = process.stdout.columns - labelCols - 1
  for (var colStart = 0; colStart < alignment.length; colStart += alignCols) {
    labels.forEach (function (rowLabel, rowIndex) {
      process.stdout.write (rowLabel + new Array(labelCols+1-rowLabel.length).fill(' ').join(''))
      for (var colIndex = colStart; colIndex < colStart + alignCols && colIndex < alignment.length; ++colIndex) {
	var alignChar = alignment[colIndex][rowIndex], refChar = alignment[colIndex][refRowIndex]
	process.stdout.write (alignChar === refChar ? alignChar : (alignChar === '-' ? alignChar.bgRed.white : (refChar === '-' ? alignChar.green : alignChar.bgCyan.blue)))
      }
      process.stdout.write ("\n")
    })
    process.stdout.write ("\n")
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * This Module manages all /minified/* requests. It controls the
 * minification && compression of Javascript and CSS.
 */

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

const ERR = require('async-stacktrace');
const settings = require('./Settings');
const fs = require('fs');
const path = require('path');
const plugins = require('../../static/js/pluginfw/plugin_defs');
const RequireKernel = require('etherpad-require-kernel');
const urlutil = require('url');
const mime = require('mime-types');
const Threads = require('threads');
const log4js = require('log4js');
const util = require('util');

const logger = log4js.getLogger('Minify');

const ROOT_DIR = path.normalize(`${__dirname}/../../static/`);

const threadsPool = new Threads.Pool(() => Threads.spawn(new Threads.Worker('./MinifyWorker')), 2);

const LIBRARY_WHITELIST = [
  'async',
  'js-cookie',
  'security',
  'tinycon',
  'underscore',
  'unorm',
];

// What follows is a terrible hack to avoid loop-back within the server.
// TODO: Serve files from another service, or directly from the file system.
const requestURI = async (url, method, headers) => {
  var headers = {};

  return await new Promise((resolve) => {
    const parsedURL = urlutil.parse(url);
    let status = 500;
    const content = [];
    const mockRequest = {
      url,
      method,
      params: {filename: parsedURL.path.replace(/^\/static\//, '')},
      headers,
    };
    const mockResponse = {
      writeHead: (_status, _headers) => {
        status = _status;
        for (const header in _headers) {
          if (Object.prototype.hasOwnProperty.call(_headers, header)) {
            headers[header] = _headers[header];
          }
        }
      },
      setHeader: (header, value) => {
        headers[header.toLowerCase()] = value.toString();
      },
      header: (header, value) => {
        headers[header.toLowerCase()] = value.toString();
      },
      write: (_content) => {
        _content && content.push(_content);
      },
      end: (_content) => {
        _content && content.push(_content);
        resolve([status, headers, content.join('')]);
      },
    };
    minify(mockRequest, mockResponse);
  });
};

const requestURIs = (locations, method, headers, callback) => {
  Promise.all(locations.map((loc) => requestURI(loc, method, headers))).then((responses) => {
    const statuss = responses.map((x) => x[0]);
    const headerss = responses.map((x) => x[1]);
    const contentss = responses.map((x) => x[2]);
    callback(statuss, headerss, contentss);
  });
};

/**
 * creates the minifed javascript for the given minified name
 * @param req the Express request
 * @param res the Express response
 */
const minify = (req, res) => {
  let filename = req.params.filename;

  // No relative paths, especially if they may go up the file hierarchy.
  filename = path.normalize(path.join(ROOT_DIR, filename));
  filename = filename.replace(/\.\./g, '');

  if (filename.indexOf(ROOT_DIR) === 0) {
    filename = filename.slice(ROOT_DIR.length);
    filename = filename.replace(/\\/g, '/');
  } else {
    res.writeHead(404, {});
    res.end();
    return;
  }

  /* Handle static files for plugins/libraries:
     paths like "plugins/ep_myplugin/static/js/test.js"
     are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js,
     commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js
  */
  const match = filename.match(/^plugins\/([^/]+)(\/(?:(static\/.*)|.*))?$/);
  if (match) {
    const library = match[1];
    const libraryPath = match[2] || '';

    if (plugins.plugins[library] && match[3]) {
      const plugin = plugins.plugins[library];
      const pluginPath = plugin.package.realPath;
      filename = path.relative(ROOT_DIR, pluginPath + libraryPath);
      filename = filename.replace(/\\/g, '/'); // windows path fix
    } else if (LIBRARY_WHITELIST.indexOf(library) !== -1) {
      // Go straight into node_modules
      // Avoid `require.resolve()`, since 'mustache' and 'mustache/index.js'
      // would end up resolving to logically distinct resources.
      filename = `../node_modules/${library}${libraryPath}`;
    }
  }

  const contentType = mime.lookup(filename);

  util.callbackify(statFile)(filename, 3, (error, [date, exists]) => {
    if (error) {
      res.writeHead(500, {});
      res.end();
      return;
    }
    if (date) {
      date = new Date(date);
      date.setMilliseconds(0);
      res.setHeader('last-modified', date.toUTCString());
      res.setHeader('date', (new Date()).toUTCString());
      if (settings.maxAge !== undefined) {
        const expiresDate = new Date(Date.now() + settings.maxAge * 1000);
        res.setHeader('expires', expiresDate.toUTCString());
        res.setHeader('cache-control', `max-age=${settings.maxAge}`);
      }
    }

    if (!exists) {
      res.writeHead(404, {});
      res.end();
    } else if (new Date(req.headers['if-modified-since']) >= date) {
      res.writeHead(304, {});
      res.end();
    } else if (req.method === 'HEAD') {
      res.header('Content-Type', contentType);
      res.writeHead(200, {});
      res.end();
    } else if (req.method === 'GET') {
      util.callbackify(getFileCompressed)(filename, contentType, (error, content) => {
        if (ERR(error, () => {
          res.writeHead(500, {});
          res.end();
        })) return;
        res.header('Content-Type', contentType);
        res.writeHead(200, {});
        res.write(content);
        res.end();
      });
    } else {
      res.writeHead(405, {allow: 'HEAD, GET'});
      res.end();
    }
  });
};

// find all includes in ace.js and embed them.
const getAceFile = async () => {
  let data = await util.promisify(fs.readFile)(`${ROOT_DIR}js/ace.js`, 'utf8');

  // Find all includes in ace.js and embed them
  const filenames = [];
  if (settings.minify) {
    const regex = /\$\$INCLUDE_[a-zA-Z_]+\((['"])([^'"]*)\1\)/gi;
    // This logic can be simplified via String.prototype.matchAll() once support for Node.js
    // v11.x and older is dropped.
    let matches;
    while ((matches = regex.exec(data)) != null) {
      filenames.push(matches[2]);
    }
  }
  // Always include the require kernel.
  filenames.push('../static/js/require-kernel.js');

  data += ';\n';
  data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n';

  // Request the contents of the included file on the server-side and write
  // them into the file.
  await Promise.all(filenames.map(async (filename) => {
    // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI.
    const baseURI = 'http://invalid.invalid';
    let resourceURI = baseURI + path.normalize(path.join('/static/', filename));
    resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?)

    const [status, , body] = await requestURI(resourceURI, 'GET', {});
    const error = !(status === 200 || status === 404);
    if (!error) {
      data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${
        JSON.stringify(status === 200 ? body || '' : null)};\n`;
    } else {
      console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`);
    }
  }));
  return data;
};

// Check for the existance of the file and get the last modification date.
const statFile = async (filename, dirStatLimit) => {
  /*
   * The only external call to this function provides an explicit value for
   * dirStatLimit: this check could be removed.
   */
  if (typeof dirStatLimit === 'undefined') {
    dirStatLimit = 3;
  }

  if (dirStatLimit < 1 || filename === '' || filename === '/') {
    return [null, false];
  } else if (filename === 'js/ace.js') {
    // Sometimes static assets are inlined into this file, so we have to stat
    // everything.
    return [await lastModifiedDateOfEverything(), true];
  } else if (filename === 'js/require-kernel.js') {
    return [requireLastModified(), true];
  } else {
    let stats;
    try {
      stats = await util.promisify(fs.stat)(ROOT_DIR + filename);
    } catch (err) {
      if (err.code === 'ENOENT') {
        // Stat the directory instead.
        const [date] = await statFile(path.dirname(filename), dirStatLimit - 1);
        return [date, false];
      }
      throw err;
    }
    return [stats.mtime.getTime(), stats.isFile()];
  }
};

const lastModifiedDateOfEverything = async () => {
  const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`];
  let latestModification = 0;
  // go through this two folders
  await Promise.all(folders2check.map(async (path) => {
    // read the files in the folder
    const files = await util.promisify(fs.readdir)(path);

    // we wanna check the directory itself for changes too
    files.push('.');

    // go through all files in this folder
    await Promise.all(files.map(async (filename) => {
      // get the stat data of this file
      const stats = await util.promisify(fs.stat)(`${path}/${filename}`);

      // get the modification time
      const modificationTime = stats.mtime.getTime();

      // compare the modification time to the highest found
      if (modificationTime > latestModification) {
        latestModification = modificationTime;
      }
    }));
  }));
  return latestModification;
};

// This should be provided by the module, but until then, just use startup
// time.
const _requireLastModified = new Date();
const requireLastModified = () => _requireLastModified.toUTCString();
const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`;

const getFileCompressed = async (filename, contentType) => {
  let content = await getFile(filename);
  if (!content || !settings.minify) {
    return content;
  } else if (contentType === 'application/javascript') {
    return await new Promise((resolve) => {
      threadsPool.queue(async ({compressJS}) => {
        try {
          logger.info('Compress JS file %s.', filename);

          content = content.toString();
          const compressResult = await compressJS(content);

          if (compressResult.error) {
            console.error(`Error compressing JS (${filename}) using terser`, compressResult.error);
          } else {
            content = compressResult.code.toString(); // Convert content obj code to string
          }
        } catch (error) {
          console.error('getFile() returned an error in ' +
                        `getFileCompressed(${filename}, ${contentType}): ${error}`);
        }
        resolve(content);
      });
    });
  } else if (contentType === 'text/css') {
    return await new Promise((resolve) => {
      threadsPool.queue(async ({compressCSS}) => {
        try {
          logger.info('Compress CSS file %s.', filename);

          content = await compressCSS(filename, ROOT_DIR);
        } catch (error) {
          console.error(`CleanCSS.minify() returned an error on ${filename}: ${error}`);
        }
        resolve(content);
      });
    });
  } else {
    return content;
  }
};

const getFile = async (filename) => {
  if (filename === 'js/ace.js') return await getAceFile();
  if (filename === 'js/require-kernel.js') return requireDefinition();
  return await util.promisify(fs.readFile)(ROOT_DIR + filename);
};

exports.minify = minify;

exports.requestURIs = requestURIs;

exports.shutdown = async (hookName, context) => {
  await threadsPool.terminate();
};
<commit_msg>Minify: Asyncify `minify()`<commit_after>'use strict';

/**
 * This Module manages all /minified/* requests. It controls the
 * minification && compression of Javascript and CSS.
 */

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

const settings = require('./Settings');
const fs = require('fs');
const path = require('path');
const plugins = require('../../static/js/pluginfw/plugin_defs');
const RequireKernel = require('etherpad-require-kernel');
const urlutil = require('url');
const mime = require('mime-types');
const Threads = require('threads');
const log4js = require('log4js');
const util = require('util');

const logger = log4js.getLogger('Minify');

const ROOT_DIR = path.normalize(`${__dirname}/../../static/`);

const threadsPool = new Threads.Pool(() => Threads.spawn(new Threads.Worker('./MinifyWorker')), 2);

const LIBRARY_WHITELIST = [
  'async',
  'js-cookie',
  'security',
  'tinycon',
  'underscore',
  'unorm',
];

// What follows is a terrible hack to avoid loop-back within the server.
// TODO: Serve files from another service, or directly from the file system.
const requestURI = async (url, method, headers) => {
  var headers = {};

  return await new Promise((resolve, reject) => {
    const parsedURL = urlutil.parse(url);
    let status = 500;
    const content = [];
    const mockRequest = {
      url,
      method,
      params: {filename: parsedURL.path.replace(/^\/static\//, '')},
      headers,
    };
    const mockResponse = {
      writeHead: (_status, _headers) => {
        status = _status;
        for (const header in _headers) {
          if (Object.prototype.hasOwnProperty.call(_headers, header)) {
            headers[header] = _headers[header];
          }
        }
      },
      setHeader: (header, value) => {
        headers[header.toLowerCase()] = value.toString();
      },
      header: (header, value) => {
        headers[header.toLowerCase()] = value.toString();
      },
      write: (_content) => {
        _content && content.push(_content);
      },
      end: (_content) => {
        _content && content.push(_content);
        resolve([status, headers, content.join('')]);
      },
    };
    minify(mockRequest, mockResponse).catch(reject);
  });
};

const requestURIs = (locations, method, headers, callback) => {
  Promise.all(locations.map((loc) => requestURI(loc, method, headers))).then((responses) => {
    const statuss = responses.map((x) => x[0]);
    const headerss = responses.map((x) => x[1]);
    const contentss = responses.map((x) => x[2]);
    callback(statuss, headerss, contentss);
  });
};

/**
 * creates the minifed javascript for the given minified name
 * @param req the Express request
 * @param res the Express response
 */
const minify = async (req, res) => {
  let filename = req.params.filename;

  // No relative paths, especially if they may go up the file hierarchy.
  filename = path.normalize(path.join(ROOT_DIR, filename));
  filename = filename.replace(/\.\./g, '');

  if (filename.indexOf(ROOT_DIR) === 0) {
    filename = filename.slice(ROOT_DIR.length);
    filename = filename.replace(/\\/g, '/');
  } else {
    res.writeHead(404, {});
    res.end();
    return;
  }

  /* Handle static files for plugins/libraries:
     paths like "plugins/ep_myplugin/static/js/test.js"
     are rewritten into ROOT_PATH_OF_MYPLUGIN/static/js/test.js,
     commonly ETHERPAD_ROOT/node_modules/ep_myplugin/static/js/test.js
  */
  const match = filename.match(/^plugins\/([^/]+)(\/(?:(static\/.*)|.*))?$/);
  if (match) {
    const library = match[1];
    const libraryPath = match[2] || '';

    if (plugins.plugins[library] && match[3]) {
      const plugin = plugins.plugins[library];
      const pluginPath = plugin.package.realPath;
      filename = path.relative(ROOT_DIR, pluginPath + libraryPath);
      filename = filename.replace(/\\/g, '/'); // windows path fix
    } else if (LIBRARY_WHITELIST.indexOf(library) !== -1) {
      // Go straight into node_modules
      // Avoid `require.resolve()`, since 'mustache' and 'mustache/index.js'
      // would end up resolving to logically distinct resources.
      filename = `../node_modules/${library}${libraryPath}`;
    }
  }

  const contentType = mime.lookup(filename);

  let date, exists;
  try {
    [date, exists] = await statFile(filename, 3);
  } catch (err) {
    res.writeHead(500, {});
    res.end();
    return;
  }
  if (date) {
    date = new Date(date);
    date.setMilliseconds(0);
    res.setHeader('last-modified', date.toUTCString());
    res.setHeader('date', (new Date()).toUTCString());
    if (settings.maxAge !== undefined) {
      const expiresDate = new Date(Date.now() + settings.maxAge * 1000);
      res.setHeader('expires', expiresDate.toUTCString());
      res.setHeader('cache-control', `max-age=${settings.maxAge}`);
    }
  }

  if (!exists) {
    res.writeHead(404, {});
    res.end();
  } else if (new Date(req.headers['if-modified-since']) >= date) {
    res.writeHead(304, {});
    res.end();
  } else if (req.method === 'HEAD') {
    res.header('Content-Type', contentType);
    res.writeHead(200, {});
    res.end();
  } else if (req.method === 'GET') {
    let content;
    try {
      content = await getFileCompressed(filename, contentType);
    } catch (err) {
      res.writeHead(500, {});
      res.end();
      return;
    }
    res.header('Content-Type', contentType);
    res.writeHead(200, {});
    res.write(content);
    res.end();
  } else {
    res.writeHead(405, {allow: 'HEAD, GET'});
    res.end();
  }
};

// find all includes in ace.js and embed them.
const getAceFile = async () => {
  let data = await util.promisify(fs.readFile)(`${ROOT_DIR}js/ace.js`, 'utf8');

  // Find all includes in ace.js and embed them
  const filenames = [];
  if (settings.minify) {
    const regex = /\$\$INCLUDE_[a-zA-Z_]+\((['"])([^'"]*)\1\)/gi;
    // This logic can be simplified via String.prototype.matchAll() once support for Node.js
    // v11.x and older is dropped.
    let matches;
    while ((matches = regex.exec(data)) != null) {
      filenames.push(matches[2]);
    }
  }
  // Always include the require kernel.
  filenames.push('../static/js/require-kernel.js');

  data += ';\n';
  data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n';

  // Request the contents of the included file on the server-side and write
  // them into the file.
  await Promise.all(filenames.map(async (filename) => {
    // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI.
    const baseURI = 'http://invalid.invalid';
    let resourceURI = baseURI + path.normalize(path.join('/static/', filename));
    resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?)

    const [status, , body] = await requestURI(resourceURI, 'GET', {});
    const error = !(status === 200 || status === 404);
    if (!error) {
      data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${
        JSON.stringify(status === 200 ? body || '' : null)};\n`;
    } else {
      console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`);
    }
  }));
  return data;
};

// Check for the existance of the file and get the last modification date.
const statFile = async (filename, dirStatLimit) => {
  /*
   * The only external call to this function provides an explicit value for
   * dirStatLimit: this check could be removed.
   */
  if (typeof dirStatLimit === 'undefined') {
    dirStatLimit = 3;
  }

  if (dirStatLimit < 1 || filename === '' || filename === '/') {
    return [null, false];
  } else if (filename === 'js/ace.js') {
    // Sometimes static assets are inlined into this file, so we have to stat
    // everything.
    return [await lastModifiedDateOfEverything(), true];
  } else if (filename === 'js/require-kernel.js') {
    return [requireLastModified(), true];
  } else {
    let stats;
    try {
      stats = await util.promisify(fs.stat)(ROOT_DIR + filename);
    } catch (err) {
      if (err.code === 'ENOENT') {
        // Stat the directory instead.
        const [date] = await statFile(path.dirname(filename), dirStatLimit - 1);
        return [date, false];
      }
      throw err;
    }
    return [stats.mtime.getTime(), stats.isFile()];
  }
};

const lastModifiedDateOfEverything = async () => {
  const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`];
  let latestModification = 0;
  // go through this two folders
  await Promise.all(folders2check.map(async (path) => {
    // read the files in the folder
    const files = await util.promisify(fs.readdir)(path);

    // we wanna check the directory itself for changes too
    files.push('.');

    // go through all files in this folder
    await Promise.all(files.map(async (filename) => {
      // get the stat data of this file
      const stats = await util.promisify(fs.stat)(`${path}/${filename}`);

      // get the modification time
      const modificationTime = stats.mtime.getTime();

      // compare the modification time to the highest found
      if (modificationTime > latestModification) {
        latestModification = modificationTime;
      }
    }));
  }));
  return latestModification;
};

// This should be provided by the module, but until then, just use startup
// time.
const _requireLastModified = new Date();
const requireLastModified = () => _requireLastModified.toUTCString();
const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`;

const getFileCompressed = async (filename, contentType) => {
  let content = await getFile(filename);
  if (!content || !settings.minify) {
    return content;
  } else if (contentType === 'application/javascript') {
    return await new Promise((resolve) => {
      threadsPool.queue(async ({compressJS}) => {
        try {
          logger.info('Compress JS file %s.', filename);

          content = content.toString();
          const compressResult = await compressJS(content);

          if (compressResult.error) {
            console.error(`Error compressing JS (${filename}) using terser`, compressResult.error);
          } else {
            content = compressResult.code.toString(); // Convert content obj code to string
          }
        } catch (error) {
          console.error('getFile() returned an error in ' +
                        `getFileCompressed(${filename}, ${contentType}): ${error}`);
        }
        resolve(content);
      });
    });
  } else if (contentType === 'text/css') {
    return await new Promise((resolve) => {
      threadsPool.queue(async ({compressCSS}) => {
        try {
          logger.info('Compress CSS file %s.', filename);

          content = await compressCSS(filename, ROOT_DIR);
        } catch (error) {
          console.error(`CleanCSS.minify() returned an error on ${filename}: ${error}`);
        }
        resolve(content);
      });
    });
  } else {
    return content;
  }
};

const getFile = async (filename) => {
  if (filename === 'js/ace.js') return await getAceFile();
  if (filename === 'js/require-kernel.js') return requireDefinition();
  return await util.promisify(fs.readFile)(ROOT_DIR + filename);
};

exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err)));

exports.requestURIs = requestURIs;

exports.shutdown = async (hookName, context) => {
  await threadsPool.terminate();
};
<|endoftext|>


<commit_before>import React from 'react';
import Octokit from '@octokit/rest';
import styled, { injectGlobal } from 'styled-components';
import Assistant from '../../styles/fonts/Assistant-ExtraLight.ttf';

injectGlobal`
  * {
    box-sizing: border-box;
  }

  @font-face {
    font-family: 'Assistant';
    src: url(${Assistant});
  }
`;

const Header = styled.h1`
  height: 5vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: 'Assistant', sans-serif;
  font-size: 55px;
  font-weight: 300;
`;

const LeaderboardContainer = styled.div`
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 80vh;
  width: 100vw;
  margin-bottom: 100px;
  @media (max-width: 824px) {
    flex-direction: column;
  }
`;

const LeaderboardContainerLeft = styled.div`
  width: 40vw;
  height: 80vh;
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-direction: column;
  position: relative;
  @media (max-width: 824px) {
    width: 100vw;
    min-height: 60vh;
  }
`;

const LeftSpan = styled.span`
  position: absolute;
  left: 0;
  top: 45%;
  border: px solid red;
  transform: rotate(-90deg);
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 600;
  color: #607d8b;
   @media (max-width: 1220px) {
    display: none;
  }
`;

const RightBorder = styled.div`
  position: absolute;
  right: 0;
  height: 30vh;
  top: 30%;
  width: 1px;
  background-color: #e0e0e0;
  @media (max-width: 850px) {
    display: none;
  }
`;

const TopCards = styled.li`
  height: 150px;
  width: 300px;
  box-shadow: 0 4px 22px 0 rgba(0,0,0,0.3);
  border-radius: 4px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin-bottom: 30px;

  &:hover {
    box-shadow: 0 4px 25px 0 rgba(0,0,0,0.5);
  }

`;

const TopCardsTop = styled.div`
  height: 70%;
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
`;

const TopCardsTopLeft = styled.div`
  height: 100%;
  width: 35%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const TopCardsTopRight = styled.div`
  height: 100%;
  width: 65%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 400;
  padding-left: 10px;
  padding-top: 17px;
  overflow: hidden !important;
`;

const TopCardsBottom = styled.div`
  height: 30%;
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding-right: 15px;
  // padding-bottom: 10px;
  font-family: 'Assistant', sans-serif;
  font-size: 20px;
  font-weight: 600;
`;

const LeaderboardContainerRight = styled.div`
  width: 60vw;
  max-height: 80vh;
  overflow-y: scroll;
  @media (max-width: 824px) {
    width: 100vw;
    margin-top: 60px;
  }
`;

const Leaderboard = styled.ol`
  padding: 0;
`;

const TopLeaders = styled.ol`
  padding: 0;
`;

const ListItem = styled.li`
  height: 90px;
  width: 350px;
  margin: 10px auto;
  font-size: 1.8rem;
  display: flex;
  // box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
  transition: all 0.2s ease-in-out;
  // border-radius: 3px;
  border: 1px solid #f5f5f5;
  background-color: #fafafa;
  &:hover {
    // box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
  }
`;

const Avatar = styled.img`
  height: 60px;
  width: 60px;
  border-radius: 50%;
  margin: 5px;
  margin-top: 15px;
  margin-left: 10px;
  border: 2px solid #eeeeee;
`;

const TopAvatar = styled.img`
  height: 90px;
  width: 90px;
  border-radius: 50%;
  margin-top: 20px;
  border: 2px solid #eeeeee;
`;

const Username = styled.span`
  flex-grow: 1;
  padding: 8px;
  height: 90px;
  display: flex;
  justify-content: flex-start;
  padding-left: 40px;
  align-items: center;
  font-family: 'Assistant', sans-serif;
  font-size: 20px;
  font-weight: 400;
`;

const Commits = styled.span`
  padding: 8px;
  width: 52px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  color: #607d8b;
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 600;
`;

function addCommit(prevState, user) {
  if (!user) {
    return prevState;
  } else {
    const username = user.login;
    const prevLeaderboard = prevState.leaderboard;
    let index;
    const result = prevLeaderboard.find((user, i) => {
      if (user.username === username) {
        index = i;
        return true;
      }
    });
    if (!result) {
      return {
        leaderboard: [
          ...prevLeaderboard,
          { username, commits: 1, avatar: user.avatar_url, url: user.html_url }
        ]
      };
    } else {
      let newLeaderboard = prevLeaderboard;
      newLeaderboard[index].commits += 1;
      return { leaderboard: newLeaderboard };
    }
  }
}

export default class LeaderboardComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { leaderboard: [] , topCards: [{ background : '#ffc400' , status : 'Gold' }, { background : '#cfd8dc' , status : 'Silver' }, { background : '#8d6e63' , status : 'Bronze' }] };
    this.octokit = new Octokit();
  }

  getLeaderboard() {
    this.octokit.repos.getForOrg({ org: 'osdc' }).then(repos => {
      repos.data.forEach(({ name }) => {
        this.octokit.repos.getCommits({ owner: 'osdc', repo: name }).then(commits => {
          commits.data.forEach(commit => {
            const { committer, author } = commit;
            this.setState(prevState => addCommit(prevState, committer));
            this.setState(prevState => addCommit(prevState, author));
          });
        });
      });
    });
  }

  componentDidMount() {
    this.getLeaderboard();
  }

  render() {
    const sorted = this.state.leaderboard.sort((a, b) => {
      return b.commits - a.commits;
    });

    let listLength = sorted.length;

    let standardCommits = sorted.slice(0);

    let topCommits = standardCommits.slice(0,3);

    standardCommits = standardCommits.splice(3,listLength);

    const commitList = standardCommits.map((user, i) => (
      <ListItem key={i}>
        <Avatar src={user.avatar} />
        <Username>@{user.username}</Username>
        <Commits>{user.commits}</Commits>
      </ListItem>
    ));

    const topCommitList = topCommits.map((users, i) => (
      <TopCards key={i} style={{'backgroundColor' : this.state.topCards[i].background}}>
        <TopCardsTop>
          <TopCardsTopLeft><TopAvatar src={users.avatar} /></TopCardsTopLeft>
          <TopCardsTopRight>
            @{users.username}
            {users.commits} Commits
          </TopCardsTopRight>
        </TopCardsTop>
        <TopCardsBottom>{this.state.topCards[i].status}</TopCardsBottom>
      </TopCards>
    ));

    return (

        Leaderboard

        <LeaderboardContainer>
          <LeaderboardContainerLeft>
            <LeftSpan>Top Players</LeftSpan>
              <TopLeaders>
                {topCommitList}
              </TopLeaders>
            <RightBorder></RightBorder>
          </LeaderboardContainerLeft>

          <LeaderboardContainerRight>
            <Leaderboard>{commitList}</Leaderboard>
          </LeaderboardContainerRight>
        </LeaderboardContainer>

    );
  }
}
<commit_msg>add temporary back to main link<commit_after>import React from 'react';
import Link from 'gatsby-link';
import Octokit from '@octokit/rest';
import styled, { injectGlobal } from 'styled-components';
import Assistant from '../../styles/fonts/Assistant-ExtraLight.ttf';

injectGlobal`
  * {
    box-sizing: border-box;
  }

  @font-face {
    font-family: 'Assistant';
    src: url(${Assistant});
  }
`;

const BackButton = styled.span`
  position: fixed;
  top: 20px;
  left: 25px;
  font-family: 'Assistant', sans-serif;
  font-size: 20px;
  font-weight: 800;

  &:hover {
    cursor: pointer;
  }

`;

const Header = styled.h1`
  height: 5vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: 'Assistant', sans-serif;
  font-size: 55px;
  font-weight: 300;
`;

const LeaderboardContainer = styled.div`
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 80vh;
  width: 100vw;
  margin-bottom: 100px;
  @media (max-width: 824px) {
    flex-direction: column;
  }
`;

const LeaderboardContainerLeft = styled.div`
  width: 40vw;
  height: 80vh;
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-direction: column;
  position: relative;
  @media (max-width: 824px) {
    width: 100vw;
    min-height: 60vh;
  }
`;

const LeftSpan = styled.span`
  position: absolute;
  left: 0;
  top: 45%;
  border: px solid red;
  transform: rotate(-90deg);
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 600;
  color: #607d8b;
   @media (max-width: 1220px) {
    display: none;
  }
`;

const RightBorder = styled.div`
  position: absolute;
  right: 0;
  height: 30vh;
  top: 30%;
  width: 1px;
  background-color: #e0e0e0;
  @media (max-width: 850px) {
    display: none;
  }
`;

const TopCards = styled.li`
  height: 150px;
  width: 300px;
  box-shadow: 0 4px 22px 0 rgba(0,0,0,0.3);
  border-radius: 4px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin-bottom: 30px;

  &:hover {
    box-shadow: 0 4px 25px 0 rgba(0,0,0,0.5);
  }

`;

const TopCardsTop = styled.div`
  height: 70%;
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
`;

const TopCardsTopLeft = styled.div`
  height: 100%;
  width: 35%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const TopCardsTopRight = styled.div`
  height: 100%;
  width: 65%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 400;
  padding-left: 10px;
  padding-top: 17px;
  overflow: hidden !important;
`;

const TopCardsBottom = styled.div`
  height: 30%;
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding-right: 15px;
  // padding-bottom: 10px;
  font-family: 'Assistant', sans-serif;
  font-size: 20px;
  font-weight: 600;
`;

const LeaderboardContainerRight = styled.div`
  width: 60vw;
  max-height: 80vh;
  overflow-y: scroll;
  @media (max-width: 824px) {
    width: 100vw;
    margin-top: 60px;
  }
`;

const Leaderboard = styled.ol`
  padding: 0;
`;

const TopLeaders = styled.ol`
  padding: 0;
`;

const ListItem = styled.li`
  height: 90px;
  width: 350px;
  margin: 10px auto;
  font-size: 1.8rem;
  display: flex;
  // box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
  transition: all 0.2s ease-in-out;
  // border-radius: 3px;
  border: 1px solid #f5f5f5;
  background-color: #fafafa;
  &:hover {
    // box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
  }
`;

const Avatar = styled.img`
  height: 60px;
  width: 60px;
  border-radius: 50%;
  margin: 5px;
  margin-top: 15px;
  margin-left: 10px;
  border: 2px solid #eeeeee;
`;

const TopAvatar = styled.img`
  height: 90px;
  width: 90px;
  border-radius: 50%;
  margin-top: 20px;
  border: 2px solid #eeeeee;
`;

const Username = styled.span`
  flex-grow: 1;
  padding: 8px;
  height: 90px;
  display: flex;
  justify-content: flex-start;
  padding-left: 40px;
  align-items: center;
  font-family: 'Assistant', sans-serif;
  font-size: 20px;
  font-weight: 400;
`;

const Commits = styled.span`
  padding: 8px;
  width: 52px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  color: #607d8b;
  font-family: 'Assistant', sans-serif;
  font-size: 25px;
  font-weight: 600;
`;

function addCommit(prevState, user) {
  if (!user) {
    return prevState;
  } else {
    const username = user.login;
    const prevLeaderboard = prevState.leaderboard;
    let index;
    const result = prevLeaderboard.find((user, i) => {
      if (user.username === username) {
        index = i;
        return true;
      }
    });
    if (!result) {
      return {
        leaderboard: [
          ...prevLeaderboard,
          { username, commits: 1, avatar: user.avatar_url, url: user.html_url }
        ]
      };
    } else {
      let newLeaderboard = prevLeaderboard;
      newLeaderboard[index].commits += 1;
      return { leaderboard: newLeaderboard };
    }
  }
}

export default class LeaderboardComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { leaderboard: [] , topCards: [{ background : '#ffc400' , status : 'Gold' }, { background : '#cfd8dc' , status : 'Silver' }, { background : '#8d6e63' , status : 'Bronze' }] };
    this.octokit = new Octokit();
  }

  getLeaderboard() {
    this.octokit.repos.getForOrg({ org: 'osdc' }).then(repos => {
      repos.data.forEach(({ name }) => {
        this.octokit.repos.getCommits({ owner: 'osdc', repo: name }).then(commits => {
          commits.data.forEach(commit => {
            const { committer, author } = commit;
            this.setState(prevState => addCommit(prevState, committer));
            this.setState(prevState => addCommit(prevState, author));
          });
        });
      });
    });
  }

  componentDidMount() {
    this.getLeaderboard();
  }

  render() {
    const sorted = this.state.leaderboard.sort((a, b) => {
      return b.commits - a.commits;
    });

    let listLength = sorted.length;

    let standardCommits = sorted.slice(0);

    let topCommits = standardCommits.slice(0,3);

    standardCommits = standardCommits.splice(3,listLength);

    const commitList = standardCommits.map((user, i) => (
      <ListItem key={i}>
        <Avatar src={user.avatar} />
        <Username>@{user.username}</Username>
        <Commits>{user.commits}</Commits>
      </ListItem>
    ));

    const topCommitList = topCommits.map((users, i) => (
      <TopCards key={i} style={{'backgroundColor' : this.state.topCards[i].background}}>
        <TopCardsTop>
          <TopCardsTopLeft><TopAvatar src={users.avatar} /></TopCardsTopLeft>
          <TopCardsTopRight>
            @{users.username}
            {users.commits} Commits
          </TopCardsTopRight>
        </TopCardsTop>
        <TopCardsBottom>{this.state.topCards[i].status}</TopCardsBottom>
      </TopCards>
    ));

    return (

        <BackButton><Link to="/" style={{'outline' : 'none' ,'text-decoration' : 'none', 'color' : 'black'}} >Back to main</Link></BackButton>
        Leaderboard

        <LeaderboardContainer>
          <LeaderboardContainerLeft>
            <LeftSpan>Top Players</LeftSpan>
              <TopLeaders>
                {topCommitList}
              </TopLeaders>
            <RightBorder></RightBorder>
          </LeaderboardContainerLeft>

          <LeaderboardContainerRight>
            <Leaderboard>{commitList}</Leaderboard>
          </LeaderboardContainerRight>
        </LeaderboardContainer>

    );
  }
}
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var CompoundPath = this.CompoundPath = PathItem.extend({
	initialize: function(items) {
		this.base();
		this.children = [];
		if (items) {
			for (var i = 0, l = items.length; i < l; i++)
				this.appendTop(items[i]);
		}
	},

	/**
	 * If this is a compound path with only one path inside,
	 * the path is moved outside and the compound path is erased.
	 * Otherwise, the compound path is returned unmodified.
	 *
	 * @return the simplified compound path.
	 */
	simplify: function() {
		if (this.children.length == 1) {
			var child = this.children[0];
			child.moveAbove(this);
			this.remove();
			return child;
		}
		return this;
	},

	smooth: function() {
		for (var i = 0, l = this.children.length; i < l; i++)
			this.children[i].smooth();
	},

	draw: function(ctx, param) {
		var firstChild = this.children[0];
		ctx.beginPath();
		param.compound = true;
		for (var i = 0, l = this.children.length; i < l; i++)
			Item.draw(this.children[i], ctx, param);
		firstChild._setStyles(ctx);
		var fillColor = firstChild.getFillColor(),
			strokeColor = firstChild.getStrokeColor();
		if (fillColor) {
			ctx.fillStyle = fillColor.getCanvasStyle(ctx);
			ctx.fill();
		}
		if (strokeColor) {
			ctx.strokeStyle = strokeColor.getCanvasStyle(ctx);
			ctx.stroke();
		}
	}
}, new function() { // Injection scope for PostScript-like drawing functions
	function getCurrentPath(that) {
		if (that.children.length) {
			return that.children[that.children.length - 1];
		} else {
			throw new Error('Use a moveTo() command first');
		}
	}

	var fields = {
		moveTo: function() {
			var path = new Path();
			this.appendTop(path);
			path.moveTo.apply(path, arguments);
		},

		moveBy: function() {
			var point = arguments.length ? Point.read(arguments) : new Point(),
				path = getCurrentPath(this),
				current = path.segments[path.segments.length - 1]._point;
			this.moveTo(current.add(point));
		},

		closePath: function() {
			getCurrentPath(this).setClosed(true);
		}
	};

	// Redirect all other drawing commands to the current path
	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
			'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) {
		fields[key] = function() {
			var path = getCurrentPath(this);
			path[key].apply(path, arguments);
		};
	});

	return fields;
});
<commit_msg>Have the CompoundPath constructor reverse the segments of all paths passed to it except for the first one. Introduce 'keepDirection' option to allow importing of already existing CompoundPath items.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var CompoundPath = this.CompoundPath = PathItem.extend({
	// PORT: port the reversing of segments and keepDirection flag
	initialize: function(items, keepDirection) {
		this.base();
		this.children = [];
		if (items) {
			for (var i = 0, l = items.length; i < l; i++) {
				var item = items[i];
				// All paths except for the first one are reversed when
				// creating a compound path, so that they draw holes.
				// When keepDirection is set to true, child paths aren't reversed.
				if (!keepDirection && i != l - 1)
					item.reverse();
				this.appendTop(items[i]);
			}
		}
	},

	/**
	 * If this is a compound path with only one path inside,
	 * the path is moved outside and the compound path is erased.
	 * Otherwise, the compound path is returned unmodified.
	 *
	 * @return the simplified compound path.
	 */
	simplify: function() {
		if (this.children.length == 1) {
			var child = this.children[0];
			child.moveAbove(this);
			this.remove();
			return child;
		}
		return this;
	},

	smooth: function() {
		for (var i = 0, l = this.children.length; i < l; i++)
			this.children[i].smooth();
	},

	draw: function(ctx, param) {
		var firstChild = this.children[0];
		ctx.beginPath();
		param.compound = true;
		for (var i = 0, l = this.children.length; i < l; i++)
			Item.draw(this.children[i], ctx, param);
		firstChild._setStyles(ctx);
		var fillColor = firstChild.getFillColor(),
			strokeColor = firstChild.getStrokeColor();
		if (fillColor) {
			ctx.fillStyle = fillColor.getCanvasStyle(ctx);
			ctx.fill();
		}
		if (strokeColor) {
			ctx.strokeStyle = strokeColor.getCanvasStyle(ctx);
			ctx.stroke();
		}
	}
}, new function() { // Injection scope for PostScript-like drawing functions
	function getCurrentPath(that) {
		if (that.children.length) {
			return that.children[that.children.length - 1];
		} else {
			throw new Error('Use a moveTo() command first');
		}
	}

	var fields = {
		moveTo: function() {
			var path = new Path();
			this.appendTop(path);
			path.moveTo.apply(path, arguments);
		},

		moveBy: function() {
			var point = arguments.length ? Point.read(arguments) : new Point(),
				path = getCurrentPath(this),
				current = path.segments[path.segments.length - 1]._point;
			this.moveTo(current.add(point));
		},

		closePath: function() {
			getCurrentPath(this).setClosed(true);
		}
	};

	// Redirect all other drawing commands to the current path
	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
			'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) {
		fields[key] = function() {
			var path = getCurrentPath(this);
			path[key].apply(path, arguments);
		};
	});

	return fields;
});
<|endoftext|>


<commit_before>CompoundPath = PathItem.extend(new function() {

	function getCurrentPath(compoundPath) {
		if (compoundPath.children.length) {
			return compoundPath.children[compoundPath.children.length - 1];
		} else {
			throw Error('Use a moveTo() command first');
		}
	}

	var fields = {
		initialize: function(items) {
			this.base();
			this.children = [];
			if (items) {
				for (var i = 0, l = items.length; i < l; i++) {
					this.appendTop(items[i]);
				}
			}
		},

		draw: function(ctx) {
			if(!this.visible)
				return;
			if (this.children.length) {
				var firstChild = this.children[0];
				ctx.beginPath();
				for (var i = 0, l = this.children.length; i < l; i++) {
					var child = this.children[i];
					child.draw(ctx, true);
				}
				firstChild.setCtxStyles(ctx);
				if (firstChild.fillColor) {
					ctx.fillStyle = firstChild.fillColor.getCssString();
					ctx.fill();
				}
				if (firstChild.strokeColor) {
					ctx.strokeStyle = firstChild.strokeColor.getCssString();
					ctx.stroke();
				}
			}
		},

		// TODO: add getBounds

		/**
		 * If this is a compound path with only one path inside,
		 * the path is moved outside and the compound path is erased.
		 * Otherwise, the compound path is returned unmodified.
		 *
		 * @return the simplified compound path.
		 */
		simplify: function() {
			if (this.children.length == 1) {
				var child = this.children[0];
				child.moveAbove(this);
				this.remove();
				return child;
			}
			return this;
		},

		smooth: function() {
			for (var i = 0, l = this.children.length; i < l; i++) {
				this.children[i].smooth();
			}
		},

		moveTo: function() {
			var path = new Path();
			this.appendTop(path);
			path.moveTo.apply(path, arguments);
		},

		moveBy: function() {
			if (!arguments.length) {
				// TODO: Shouldn't this be relative to the previous position
				// in lack of an argument? This should then be corrected in
				// Scriptographer too.
				this.moveTo(0, 0);
			} else {
				var point = Point.read(arguments);
				var path = getCurrentPath(this);
				var current = path.segments[path.segments.length - 1].point;
				this.moveTo(current.add(point));
			}
		},

		closePath: function() {
			var path = getCurrentPath();
			path.setClosed(true);
		}
	};

	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
			'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) {
		fields[key] = function() {
			var path = getCurrentPath(this);
			path[key].apply(path, arguments);
		};
	});

	return fields;
});<commit_msg>Add CompoundPath#bounds<commit_after>CompoundPath = PathItem.extend(new function() {

	function getCurrentPath(compoundPath) {
		if (compoundPath.children.length) {
			return compoundPath.children[compoundPath.children.length - 1];
		} else {
			throw Error('Use a moveTo() command first');
		}
	}

	var fields = {
		initialize: function(items) {
			this.base();
			this.children = [];
			if (items) {
				for (var i = 0, l = items.length; i < l; i++) {
					this.appendTop(items[i]);
				}
			}
		},

		draw: function(ctx) {
			if(!this.visible)
				return;
			if (this.children.length) {
				var firstChild = this.children[0];
				ctx.beginPath();
				for (var i = 0, l = this.children.length; i < l; i++) {
					var child = this.children[i];
					child.draw(ctx, true);
				}
				firstChild.setCtxStyles(ctx);
				if (firstChild.fillColor) {
					ctx.fillStyle = firstChild.fillColor.getCssString();
					ctx.fill();
				}
				if (firstChild.strokeColor) {
					ctx.strokeStyle = firstChild.strokeColor.getCssString();
					ctx.stroke();
				}
			}
		},

		// TODO: have getBounds of Group / Layer / CompoundPath use the same
		// code (from a utility script?)
		getBounds: function() {
			if (this.children.length) {
				var rect = this.children[0].bounds;
				var x1 = rect.x;
				var y1 = rect.y;
				var x2 = rect.x + rect.width;
				var y2 = rect.y + rect.height;
				for (var i = 1, l = this.children.length; i < l; i++) {
					var rect2 = this.children[i].bounds;
					x1 = Math.min(rect2.x, x1);
					y1 = Math.min(rect2.y, y1);
					x2 = Math.max(rect2.x + rect2.width, x1 + x2 - x1);
					y2 = Math.max(rect2.y + rect2.height, y1 + y2 - y1);
				}
			}
			return new Rectangle(x1, y1, x2 - x1, y2 - y1);
		},

		/**
		 * If this is a compound path with only one path inside,
		 * the path is moved outside and the compound path is erased.
		 * Otherwise, the compound path is returned unmodified.
		 *
		 * @return the simplified compound path.
		 */
		simplify: function() {
			if (this.children.length == 1) {
				var child = this.children[0];
				child.moveAbove(this);
				this.remove();
				return child;
			}
			return this;
		},

		smooth: function() {
			for (var i = 0, l = this.children.length; i < l; i++) {
				this.children[i].smooth();
			}
		},

		moveTo: function() {
			var path = new Path();
			this.appendTop(path);
			path.moveTo.apply(path, arguments);
		},

		moveBy: function() {
			if (!arguments.length) {
				// TODO: Shouldn't this be relative to the previous position
				// in lack of an argument? This should then be corrected in
				// Scriptographer too.
				this.moveTo(0, 0);
			} else {
				var point = Point.read(arguments);
				var path = getCurrentPath(this);
				var current = path.segments[path.segments.length - 1].point;
				this.moveTo(current.add(point));
			}
		},

		closePath: function() {
			var path = getCurrentPath();
			path.setClosed(true);
		}
	};

	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
			'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) {
		fields[key] = function() {
			var path = getCurrentPath(this);
			path[key].apply(path, arguments);
		};
	});

	return fields;
});<|endoftext|>


<commit_before>/**
 * @name  patternfly
 *
 * @description
 *   Base module for patternfly.
 */
angular.module('patternfly', [
  'patternfly.autofocus',
  'patternfly.card',
  'patternfly.filters',
  'patternfly.form',
  'patternfly.notification',
  'patternfly.select',
  'patternfly.sort',
  'patternfly.utils',
  'patternfly.validation',
  'patternfly.views'
]);

<commit_msg>Add toolbars module to the default modules<commit_after>/**
 * @name  patternfly
 *
 * @description
 *   Base module for patternfly.
 */
angular.module('patternfly', [
  'patternfly.autofocus',
  'patternfly.card',
  'patternfly.filters',
  'patternfly.form',
  'patternfly.notification',
  'patternfly.select',
  'patternfly.sort',
  'patternfly.toolbars',
  'patternfly.utils',
  'patternfly.validation',
  'patternfly.views'
]);

<|endoftext|>