<commit_before>"use strict";

var Drawing = {
    rectangle: function(ctx, centerPt, halfWidth, halfHeight, fillMode) {
        ctx.beginPath();
        ctx.rect(centerPt.x - halfWidth, centerPt.y - halfHeight, 2 * halfWidth, 2 * halfHeight);
        if (fillMode == "filled") ctx.fill();
        ctx.stroke();
    },

    square: function(ctx, centerPt, r, fillMode) {
        Drawing.rectangle(ctx, centerPt, r, r, fillMode);
    },

    ellipse: function(ctx, centerPt, halfWidth, halfHeight, fillMode) {
        ctx.beginPath();
        ctx.moveTo(centerPt.x - halfWidth, centerPt.y);
        ctx.bezierCurveTo(centerPt.x - halfWidth, centerPt.y - halfHeight, centerPt.x + halfWidth, centerPt.y - halfHeight, centerPt.x + halfWidth, centerPt.y);
        ctx.bezierCurveTo(centerPt.x + halfWidth, centerPt.y + halfHeight, centerPt.x - halfWidth, centerPt.y + halfHeight, centerPt.x - halfWidth, centerPt.y);
        if (fillMode == "filled") ctx.fill();
        ctx.stroke();
    },

    circle: function(ctx, centerPt, r, fillMode) {
        ctx.beginPath();
        ctx.arc(centerPt.x, centerPt.y, r, 2 * Math.PI, false);
        if (fillMode == "filled") ctx.fill();
        ctx.stroke();
    },

    triangle: function(ctx, centerPt, r, fillMode) {
        ctx.beginPath();
        ctx.moveTo(centerPt.x, centerPt.y - r);
        ctx.lineTo(centerPt.x + r, centerPt.y + r);
        ctx.lineTo(centerPt.x - r, centerPt.y + r);
        ctx.lineTo(centerPt.x, centerPt.y - r);
        if (fillMode == "filled") ctx.fill();
        ctx.stroke();
    },

    line: function(ctx, centerPt, r) {
        ctx.beginPath();
        ctx.moveTo(centerPt.x - r, centerPt.y);
        ctx.lineTo(centerPt.x + r, centerPt.y);
        ctx.stroke();
    },

    fillText: function(ctx, centerPt, r, text) {
        ctx.fillText(text, centerPt.x, centerPt.y, 2 * r);
    }
};

;(function() {
    var canvas = document.getElementById("drawing")
    var drawing = canvas.getContext('2d');

    function mapLength(obj) {
        var size = 0;
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) size++;
        }
        return size;
    }

    function clone(object) {
        return JSON.parse(JSON.stringify(object));
    }

    var Default = {
        BACKGROUND: "white",
        COLOR: "black",
        LINE_STYLE: "solid",
        LINE_WIDTH: "thin",
        FILL_MODE: "empty"
    };

    /*
    Parse multiple rows of text.
    */
    function parse(input)  {
        var rows = input.split(/\n/);

        var layers = new Array();
        var instructions = new Array();
        var metaInstructions = {
            background: { shape: "background", color: Default.BACKGROUND },
            shapes: { shape: "shapes", color: Default.COLOR, lineStyle: Default.LINE_STYLE, lineWidth: Default.LINE_WIDTH, fillMode: Default.FILL_MODE }
        };

        rows.forEach(function(row) {
            var trimmedRow = row.trim();

            if (trimmedRow.substr(0, 1) == '-') return; // ignore comments

            if (trimmedRow.substr(0, 1) == '=') {
                layers.push(instructions);
                instructions = new Array();
                return;
            }

            var rowInstructions = parseRow(trimmedRow);

            if (rowInstructions.metaInstructions) {
                for (var key in rowInstructions.metaInstructions) {
                    metaInstructions[key] = rowInstructions.metaInstructions[key];
                }
            }

            if (rowInstructions.drawingInstructions) {
                instructions.push({ drawingInstructions: rowInstructions.drawingInstructions, metaInstructions: clone(metaInstructions) });
            }
        });

        layers.push(instructions);

        return layers;
    }

    function newProperties() {
        return {
            color: null,
            textColor:  null,
            size: null,
            lineStyle: null,
            lineWidth: null,
            fillMode: null,
            cardinality: 1,
            texts: new Array()
        };
    }

    /*
    Parse a single row of text.
    Returns an array of meta instructions and an array of drawing instructions.
    */
    function parseRow(row) {
        var metaInstructions = new Object();
        var drawingInstructions = new Array();
        var properties = newProperties();

        var tokens = row.split(/\s+/);

        tokens.forEach(function(token_) {
            var token = translateSymbols(token_).toLowerCase();

            switch (token) {
                case "background":
                    metaInstructions[token] = { color: properties.color, shape: token };
                    properties = newProperties();
                    return;

                case "shapes":
                    metaInstructions[token] = {
                        shape: token,
                        color: properties.color || Default.COLOR,
                        lineStyle: properties.lineStyle || Default.LINE_STYLE,
                        lineWidth: properties.lineWidth || Default.LINE_WIDTH,
                        fillMode: properties.fillMode || Default.FILL_MODE };
                    properties = newProperties();
                    return;

                case "blank":
                case "blanks":
                    for (var i = 0; i < properties.cardinality; i++) {
                        drawingInstructions.push({ shape: "blank" });
                    }
                    properties = newProperties();
                    return;

                case "square": case "rectangle": case "circle": case "ellipse": case "triangle": case "line":
                case "squares": case "rectangles": case "circles": case "ellipses": case "triangles": case "lines":
                    for (var i = 0; i < properties.cardinality; i++) {
                        drawingInstructions.push({
                            text: properties.texts.length > i ? properties.texts[i] : null,
                            color: properties.color,
                            textColor: properties.textColor,
                            size: properties.size,
                            lineStyle: properties.lineStyle,
                            lineWidth: properties.lineWidth,
                            fillMode: properties.fillMode,
                            shape: token
                        });
                    };
                    properties = newProperties();
                    return;
            }

            switch (token) {
                case "aqua": case "black": case "blue": case "fuchsia": case "gray": case "green": case "lime":  case "maroon": case "navy":
                case "olive": case "orange": case "purple": case "red": case "silver": case "teal": case "white": case "yellow":
                    properties.color = token;
                    return;

                case "tiny": case "small": case "big":
                    properties.size = token;
                    return;

                case "dashed": case "dotted": case "solid":
                    properties.lineStyle = token;
                    return;

                case "fat": case "thick": case "thin":
                    properties.lineWidth = token;
                    return;

                case "filled": case "empty":
                    properties.fillMode = token;
                    return;
            }

            if (token.search(/^".*"$/) == 0 || token.search(/^'.*'$/) == 0) {
                properties.texts.push(token_.substr(1, token_.length - 2));
                if (properties.color != null) {
                    properties.textColor = properties.color;
                    properties.color = null;
                }
            }

            if (!isNaN(token)) {
                properties.cardinality = parseInt(token);
                return;
            }
        });

        return {
            metaInstructions : mapLength(metaInstructions) > 0 ? metaInstructions : null,
            drawingInstructions: drawingInstructions.length > 0 ? drawingInstructions : null
        };
    }

    function translateSymbols(token) {
        switch (token) {
            case "#": return "square";
            case "o": return "circle";
            case "[]": return "rectangle";
            case "()": return "ellipse";
            case "/\\": return "triangle";
            case "-": return "line";
            default: return token;
        }
    }

    function renderLayers(layers) {
        drawing.clearRect(0, 0, canvas.width, canvas.height);

        drawing.textAlign = "center";
        drawing.textBaseline = "middle";

        var firstLayer = true;
        layers.forEach(function(instructions) {
            render(instructions, firstLayer);
            firstLayer = false;
        });
    }

    /*
    Render multiple rows of instructions.
    Accepts an array of arrays of instructions.
    */
    function render(instructions, renderBackground) {
        var rowCount = instructions.length;

        var currentRow = 0;
        if (renderBackground) {
            instructions.forEach(function(rowInstructions) {
                renderRowBackground(rowInstructions, currentRow, rowCount);
                currentRow++;
            });
        }

        currentRow = 0;
        instructions.forEach(function(rowInstructions) {
            renderRow(rowInstructions, currentRow, rowCount);
            currentRow++;
        });
    }

    function renderRowBackground(rowInstructions, currentRow, rowCount) {
        var columnCount = rowInstructions.drawingInstructions.length;

        for (var key in rowInstructions.metaInstructions) {
            renderBackgroundInstruction(rowInstructions.metaInstructions[key], currentRow, 0, rowCount, columnCount);
        }
    }

    /*
    Render a single row of instructions.
    Accepts an array of instructions.
    */
    function renderRow(rowInstructions, currentRow, rowCount) {
        var columnCount = rowInstructions.drawingInstructions.length;

        for (var key in rowInstructions.metaInstructions) {
            renderInstruction(rowInstructions.metaInstructions[key], currentRow, 0, rowCount, columnCount);
        }

        var currentColumn = 0;
        rowInstructions.drawingInstructions.forEach(function(instruction) {
            renderInstruction(instruction, currentRow, currentColumn, rowCount, columnCount);
            currentColumn++;
        });
    }

    /*
    Calculate the center of a shape
    */
    function calcCenter(currentRow, currentColumn, rowCount, columnCount) {
        var cellWidth = canvas.width / columnCount;
        var cellHeight = canvas.height / rowCount;

        return {
            x: currentColumn * cellWidth + cellWidth / 2,
            y: currentRow * cellHeight + cellHeight / 2
        };
    }

    /*
    Calculate a shapes radius
    */
    function calcRadius(canvasSize, count, size) {
        var factor = 0.8;

        switch (size) {
            case "big":
                factor = 0.99;
                break;

            case "small":
                factor = 0.5;
                break;

            case "tiny":
                factor = 0.25;
                break;
        }

        return canvasSize / count / 2 * factor;
    }

    function calcFontSize(rowCount, columnCount, size) {
        var factor = 0.75;

        switch (size) {
            case "big":
                factor = 1.0;
                break;

            case "small":
                factor = 0.5;
                break;

            case "tiny":
                factor = 0.25;
                break;
        }

        return Math.min(canvas.width / columnCount / 8, canvas.height / rowCount / 8) * factor;
    }

    function calcLineWidth(lineWidth) {
        switch (lineWidth) {
            case "thin":
                return 1;
            case "thick":
                return 4;
            case "fat":
                return 8;
        }
    }

    function setLineStyle(lineStyle, lineWidth) {
        var w = calcLineWidth(lineWidth);

        switch (lineStyle) {
            case "dashed":
                drawing.setLineDash([3 * w]);
                break;
            case "dotted":
                drawing.setLineDash([w, w]);
                break;
            case "solid":
                drawing.setLineDash([]);
                break;
        }
    }

    function setLineWidth(lineWidth) {
        drawing.lineWidth = calcLineWidth(lineWidth);
    }

    function renderBackgroundInstruction(instruction, currentRow, currentColumn, rowCount, columnCount) {
        switch (instruction.shape) {
            case "background":
                drawing.fillStyle = instruction.color;
                drawing.fillRect(0, canvas.height * currentRow / rowCount, canvas.width, canvas.height / rowCount);
                break;
        }
    }

    /*
    Render a single instruction.
    */
    function renderInstruction(instruction, currentRow, currentColumn, rowCount, columnCount) {
        if (instruction.shape == "shapes") {
            canvas.defaultColor = instruction.color;
            canvas.defaultLineStyle = instruction.lineStyle;
            canvas.defaultLineWidth = instruction.lineWidth;
            canvas.defaultFillMode = instruction.fillMode;
            return;
        }

        drawing.fillStyle = drawing.strokeStyle = instruction.color || canvas.defaultColor;
        setLineStyle(instruction.lineStyle || canvas.defaultLineStyle, instruction.lineWidth || canvas.defaultLineWidth);
        setLineWidth(instruction.lineWidth || canvas.defaultLineWidth);

        switch (instruction.shape) {
            case "square":
            case "squares":
                Drawing.square(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "rectangle":
            case "rectangles":
                Drawing.rectangle(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size) * 1.1, calcRadius(canvas.height, rowCount, instruction.size) * 0.9,
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "circle":
            case "circles":
                Drawing.circle(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "ellipse":
            case "ellipses":
                Drawing.ellipse(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size) * 1.1, calcRadius(canvas.height, rowCount, instruction.size) * 0.9,
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "triangle":
            case "triangles":
                Drawing.triangle(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "line":
            case "lines":
                Drawing.line(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size));
                break;
        }

        if (instruction.text) {
            drawing.fillStyle = instruction.textColor || canvas.defaultColor;
            drawing.font = calcFontSize(rowCount, columnCount, instruction.size) + "pt Arial";
            Drawing.fillText(drawing, calcCenter(currentRow, currentColumn, rowCount, columnCount),
                Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                instruction.text);
        }
    }

    window.onload = function() {
        var input = document.getElementById("input");

        var draw = function() {
            var layers = parse(input.value);
            renderLayers(layers);

            window.requestAnimationFrame(draw);
        };

        window.requestAnimationFrame(draw);
    };
})();
<commit_msg>Implement proper drawing abstraction<commit_after>"use strict";

function Drawing(canvas)  {
    var ctx = canvas.getContext('2d');

    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    return {
        clear: function() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        },

        setLineDash: function(value) {
            ctx.setLineDash(value);
        },

        setLineWidth: function(value) {
            ctx.lineWidth = value;
        },

        setFillStyle: function(value) {
            ctx.fillStyle = value;
        },

        setStrokeStyle: function(value) {
            ctx.strokeStyle = value;
        },

        setFont: function(value) {
            ctx.font = value;
        },

        rectangle: function(centerPt, halfWidth, halfHeight, fillMode) {
            ctx.beginPath();
            ctx.rect(centerPt.x - halfWidth, centerPt.y - halfHeight, 2 * halfWidth, 2 * halfHeight);
            if (fillMode == "filled") ctx.fill();
            ctx.stroke();
        },

        fillRectangle: function(centerPt, halfWidth, halfHeight) {
            ctx.fillRect(centerPt.x - halfWidth, centerPt.y - halfHeight, 2 * halfWidth, 2 * halfHeight);
        },

        square: function(centerPt, r, fillMode) {
            this.rectangle(centerPt, r, r, fillMode);
        },

        ellipse: function(centerPt, halfWidth, halfHeight, fillMode) {
            ctx.beginPath();
            ctx.moveTo(centerPt.x - halfWidth, centerPt.y);
            ctx.bezierCurveTo(centerPt.x - halfWidth, centerPt.y - halfHeight, centerPt.x + halfWidth, centerPt.y - halfHeight, centerPt.x + halfWidth, centerPt.y);
            ctx.bezierCurveTo(centerPt.x + halfWidth, centerPt.y + halfHeight, centerPt.x - halfWidth, centerPt.y + halfHeight, centerPt.x - halfWidth, centerPt.y);
            if (fillMode == "filled") ctx.fill();
            ctx.stroke();
        },

        circle: function(centerPt, r, fillMode) {
            ctx.beginPath();
            ctx.arc(centerPt.x, centerPt.y, r, 2 * Math.PI, false);
            if (fillMode == "filled") ctx.fill();
            ctx.stroke();
        },

        triangle: function(centerPt, r, fillMode) {
            ctx.beginPath();
            ctx.moveTo(centerPt.x, centerPt.y - r);
            ctx.lineTo(centerPt.x + r, centerPt.y + r);
            ctx.lineTo(centerPt.x - r, centerPt.y + r);
            ctx.lineTo(centerPt.x, centerPt.y - r);
            if (fillMode == "filled") ctx.fill();
            ctx.stroke();
        },

        line: function(centerPt, r) {
            ctx.beginPath();
            ctx.moveTo(centerPt.x - r, centerPt.y);
            ctx.lineTo(centerPt.x + r, centerPt.y);
            ctx.stroke();
        },

        fillText: function(centerPt, r, text) {
            ctx.fillText(text, centerPt.x, centerPt.y, 2 * r);
        }
    }
};

;(function() {
    var canvas = document.getElementById("drawing")
    var drawing = Drawing(canvas);

    function mapLength(obj) {
        var size = 0;
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) size++;
        }
        return size;
    }

    function clone(object) {
        return JSON.parse(JSON.stringify(object));
    }

    var Default = {
        BACKGROUND: "white",
        COLOR: "black",
        LINE_STYLE: "solid",
        LINE_WIDTH: "thin",
        FILL_MODE: "empty"
    };

    /*
    Parse multiple rows of text.
    */
    function parse(input)  {
        var rows = input.split(/\n/);

        var layers = new Array();
        var instructions = new Array();
        var metaInstructions = {
            background: { shape: "background", color: Default.BACKGROUND },
            shapes: { shape: "shapes", color: Default.COLOR, lineStyle: Default.LINE_STYLE, lineWidth: Default.LINE_WIDTH, fillMode: Default.FILL_MODE }
        };

        rows.forEach(function(row) {
            var trimmedRow = row.trim();

            if (trimmedRow.substr(0, 1) == '-') return; // ignore comments

            if (trimmedRow.substr(0, 1) == '=') {
                layers.push(instructions);
                instructions = new Array();
                return;
            }

            var rowInstructions = parseRow(trimmedRow);

            if (rowInstructions.metaInstructions) {
                for (var key in rowInstructions.metaInstructions) {
                    metaInstructions[key] = rowInstructions.metaInstructions[key];
                }
            }

            if (rowInstructions.drawingInstructions) {
                instructions.push({ drawingInstructions: rowInstructions.drawingInstructions, metaInstructions: clone(metaInstructions) });
            }
        });

        layers.push(instructions);

        return layers;
    }

    function newProperties() {
        return {
            color: null,
            textColor:  null,
            size: null,
            lineStyle: null,
            lineWidth: null,
            fillMode: null,
            cardinality: 1,
            texts: new Array()
        };
    }

    /*
    Parse a single row of text.
    Returns an array of meta instructions and an array of drawing instructions.
    */
    function parseRow(row) {
        var metaInstructions = new Object();
        var drawingInstructions = new Array();
        var properties = newProperties();

        var tokens = row.split(/\s+/);

        tokens.forEach(function(token_) {
            var token = translateSymbols(token_).toLowerCase();

            switch (token) {
                case "background":
                    metaInstructions[token] = { color: properties.color, shape: token };
                    properties = newProperties();
                    return;

                case "shapes":
                    metaInstructions[token] = {
                        shape: token,
                        color: properties.color || Default.COLOR,
                        lineStyle: properties.lineStyle || Default.LINE_STYLE,
                        lineWidth: properties.lineWidth || Default.LINE_WIDTH,
                        fillMode: properties.fillMode || Default.FILL_MODE };
                    properties = newProperties();
                    return;

                case "blank":
                case "blanks":
                    for (var i = 0; i < properties.cardinality; i++) {
                        drawingInstructions.push({ shape: "blank" });
                    }
                    properties = newProperties();
                    return;

                case "square": case "rectangle": case "circle": case "ellipse": case "triangle": case "line":
                case "squares": case "rectangles": case "circles": case "ellipses": case "triangles": case "lines":
                    for (var i = 0; i < properties.cardinality; i++) {
                        drawingInstructions.push({
                            text: properties.texts.length > i ? properties.texts[i] : null,
                            color: properties.color,
                            textColor: properties.textColor,
                            size: properties.size,
                            lineStyle: properties.lineStyle,
                            lineWidth: properties.lineWidth,
                            fillMode: properties.fillMode,
                            shape: token
                        });
                    };
                    properties = newProperties();
                    return;
            }

            switch (token) {
                case "aqua": case "black": case "blue": case "fuchsia": case "gray": case "green": case "lime":  case "maroon": case "navy":
                case "olive": case "orange": case "purple": case "red": case "silver": case "teal": case "white": case "yellow":
                    properties.color = token;
                    return;

                case "tiny": case "small": case "big":
                    properties.size = token;
                    return;

                case "dashed": case "dotted": case "solid":
                    properties.lineStyle = token;
                    return;

                case "fat": case "thick": case "thin":
                    properties.lineWidth = token;
                    return;

                case "filled": case "empty":
                    properties.fillMode = token;
                    return;
            }

            if (token.search(/^".*"$/) == 0 || token.search(/^'.*'$/) == 0) {
                properties.texts.push(token_.substr(1, token_.length - 2));
                if (properties.color != null) {
                    properties.textColor = properties.color;
                    properties.color = null;
                }
            }

            if (!isNaN(token)) {
                properties.cardinality = parseInt(token);
                return;
            }
        });

        return {
            metaInstructions : mapLength(metaInstructions) > 0 ? metaInstructions : null,
            drawingInstructions: drawingInstructions.length > 0 ? drawingInstructions : null
        };
    }

    function translateSymbols(token) {
        switch (token) {
            case "#": return "square";
            case "o": return "circle";
            case "[]": return "rectangle";
            case "()": return "ellipse";
            case "/\\": return "triangle";
            case "-": return "line";
            default: return token;
        }
    }

    function renderLayers(layers) {
        drawing.clear();

        var firstLayer = true;
        layers.forEach(function(instructions) {
            render(instructions, firstLayer);
            firstLayer = false;
        });
    }

    /*
    Render multiple rows of instructions.
    Accepts an array of arrays of instructions.
    */
    function render(instructions, renderBackground) {
        var rowCount = instructions.length;

        var currentRow = 0;
        if (renderBackground) {
            instructions.forEach(function(rowInstructions) {
                renderRowBackground(rowInstructions, currentRow, rowCount);
                currentRow++;
            });
        }

        currentRow = 0;
        instructions.forEach(function(rowInstructions) {
            renderRow(rowInstructions, currentRow, rowCount);
            currentRow++;
        });
    }

    function renderRowBackground(rowInstructions, currentRow, rowCount) {
        var columnCount = rowInstructions.drawingInstructions.length;

        for (var key in rowInstructions.metaInstructions) {
            renderBackgroundInstruction(rowInstructions.metaInstructions[key], currentRow, 0, rowCount, columnCount);
        }
    }

    /*
    Render a single row of instructions.
    Accepts an array of instructions.
    */
    function renderRow(rowInstructions, currentRow, rowCount) {
        var columnCount = rowInstructions.drawingInstructions.length;

        for (var key in rowInstructions.metaInstructions) {
            renderInstruction(rowInstructions.metaInstructions[key], currentRow, 0, rowCount, columnCount);
        }

        var currentColumn = 0;
        rowInstructions.drawingInstructions.forEach(function(instruction) {
            renderInstruction(instruction, currentRow, currentColumn, rowCount, columnCount);
            currentColumn++;
        });
    }

    /*
    Calculate the center of a shape
    */
    function calcCenter(currentRow, currentColumn, rowCount, columnCount) {
        var cellWidth = canvas.width / columnCount;
        var cellHeight = canvas.height / rowCount;

        return {
            x: currentColumn * cellWidth + cellWidth / 2,
            y: currentRow * cellHeight + cellHeight / 2
        };
    }

    /*
    Calculate a shapes radius
    */
    function calcRadius(canvasSize, count, size) {
        var factor = 0.8;

        switch (size) {
            case "big":
                factor = 0.99;
                break;

            case "small":
                factor = 0.5;
                break;

            case "tiny":
                factor = 0.25;
                break;
        }

        return canvasSize / count / 2 * factor;
    }

    function calcFontSize(rowCount, columnCount, size) {
        var factor = 0.75;

        switch (size) {
            case "big":
                factor = 1.0;
                break;

            case "small":
                factor = 0.5;
                break;

            case "tiny":
                factor = 0.25;
                break;
        }

        return Math.min(canvas.width / columnCount / 8, canvas.height / rowCount / 8) * factor;
    }

    function calcLineWidth(lineWidth) {
        switch (lineWidth) {
            case "thin":
                return 1;
            case "thick":
                return 4;
            case "fat":
                return 8;
        }
    }

    function setLineStyle(lineStyle, lineWidth) {
        var w = calcLineWidth(lineWidth);

        switch (lineStyle) {
            case "dashed":
                drawing.setLineDash([3 * w]);
                break;
            case "dotted":
                drawing.setLineDash([w, w]);
                break;
            case "solid":
                drawing.setLineDash([]);
                break;
        }
    }

    function setLineWidth(lineWidth) {
        drawing.setLineWidth(calcLineWidth(lineWidth));
    }

    function renderBackgroundInstruction(instruction, currentRow, currentColumn, rowCount, columnCount) {
        switch (instruction.shape) {
            case "background":
                drawing.setFillStyle(instruction.color);
                drawing.fillRectangle({
                    x: canvas.width / 2,
                    y: (canvas.height * currentRow / rowCount) + (canvas.height / rowCount / 2)
                },
                canvas.width / 2,
                (canvas.height / rowCount / 2));
                break;
        }
    }

    /*
    Render a single instruction.
    */
    function renderInstruction(instruction, currentRow, currentColumn, rowCount, columnCount) {
        if (instruction.shape == "shapes") {
            canvas.defaultColor = instruction.color;
            canvas.defaultLineStyle = instruction.lineStyle;
            canvas.defaultLineWidth = instruction.lineWidth;
            canvas.defaultFillMode = instruction.fillMode;
            return;
        }

        drawing.setFillStyle(instruction.color || canvas.defaultColor);
        drawing.setStrokeStyle(instruction.color || canvas.defaultColor);
        setLineStyle(instruction.lineStyle || canvas.defaultLineStyle, instruction.lineWidth || canvas.defaultLineWidth);
        setLineWidth(instruction.lineWidth || canvas.defaultLineWidth);

        switch (instruction.shape) {
            case "square":
            case "squares":
                drawing.square(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "rectangle":
            case "rectangles":
                drawing.rectangle(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size) * 1.1, calcRadius(canvas.height, rowCount, instruction.size) * 0.9,
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "circle":
            case "circles":
                drawing.circle(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "ellipse":
            case "ellipses":
                drawing.ellipse(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size) * 1.1, calcRadius(canvas.height, rowCount, instruction.size) * 0.9,
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "triangle":
            case "triangles":
                drawing.triangle(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                    instruction.fillMode || canvas.defaultFillMode);
                break;

            case "line":
            case "lines":
                drawing.line(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                    calcRadius(canvas.width, columnCount, instruction.size));
                break;
        }

        if (instruction.text) {
            drawing.setFillStyle(instruction.textColor || canvas.defaultColor);
            drawing.setFont(calcFontSize(rowCount, columnCount, instruction.size) + "pt Arial");
            drawing.fillText(calcCenter(currentRow, currentColumn, rowCount, columnCount),
                Math.min(calcRadius(canvas.width, columnCount, instruction.size), calcRadius(canvas.height, rowCount, instruction.size)),
                instruction.text);
        }
    }

    window.onload = function() {
        var input = document.getElementById("input");

        var draw = function() {
            var layers = parse(input.value);
            renderLayers(layers);

            window.requestAnimationFrame(draw);
        };

        window.requestAnimationFrame(draw);
    };
})();
<|endoftext|>


<commit_before>// r119

const assets = [
	'./',

	'./manifest.json',
	'./images/icon.png',

	'../files/favicon.ico',

	'../build/three.module.js',

	'../examples/jsm/controls/TransformControls.js',

	'../examples/jsm/libs/chevrotain.module.min.js',
	'../examples/jsm/libs/inflate.module.min.js',
	'../examples/jsm/libs/jszip.module.min.js',

	'../examples/js/libs/draco/draco_decoder.js',
	'../examples/js/libs/draco/draco_decoder.wasm',
	'../examples/js/libs/draco/draco_encoder.js',
	'../examples/js/libs/draco/draco_wasm_wrapper.js',

	'../examples/js/libs/draco/gltf/draco_decoder.js',
	'../examples/js/libs/draco/gltf/draco_decoder.wasm',
	'../examples/js/libs/draco/gltf/draco_wasm_wrapper.js',

	'../examples/jsm/libs/rhino3dm/rhino3dm.wasm',
	'../examples/jsm/libs/rhino3dm/rhino3dm.js',

	'../examples/jsm/loaders/3DMLoader.js',
	'../examples/jsm/loaders/3MFLoader.js',
	'../examples/jsm/loaders/AMFLoader.js',
	'../examples/jsm/loaders/ColladaLoader.js',
	'../examples/jsm/loaders/DRACOLoader.js',
	'../examples/jsm/loaders/FBXLoader.js',
	'../examples/jsm/loaders/GLTFLoader.js',
	'../examples/jsm/loaders/KMZLoader.js',
	'../examples/jsm/loaders/MD2Loader.js',
	'../examples/jsm/loaders/OBJLoader.js',
	'../examples/jsm/loaders/MTLLoader.js',
	'../examples/jsm/loaders/PLYLoader.js',
	'../examples/jsm/loaders/RGBELoader.js',
	'../examples/jsm/loaders/STLLoader.js',
	'../examples/jsm/loaders/SVGLoader.js',
	'../examples/jsm/loaders/TGALoader.js',
	'../examples/jsm/loaders/TDSLoader.js',
	'../examples/jsm/loaders/VRMLLoader.js',
	'../examples/jsm/loaders/VTKLoader.js',

	'../examples/jsm/curves/NURBSCurve.js',
	'../examples/jsm/curves/NURBSUtils.js',

	'../examples/jsm/exporters/ColladaExporter.js',
	'../examples/jsm/exporters/DRACOExporter.js',
	'../examples/jsm/exporters/GLTFExporter.js',
	'../examples/jsm/exporters/OBJExporter.js',
	'../examples/jsm/exporters/PLYExporter.js',
	'../examples/jsm/exporters/STLExporter.js',

	'../examples/jsm/helpers/VertexNormalsHelper.js',

	'../examples/jsm/geometries/TeapotBufferGeometry.js',

	'../examples/jsm/renderers/CSS2DRenderer.js', // 3DMLoader dependency

	'../examples/jsm/webxr/VRButton.js',

	'./images/rotate.svg',
	'./images/scale.svg',
	'./images/translate.svg',

	'./js/libs/codemirror/codemirror.css',
	'./js/libs/codemirror/theme/monokai.css',

	'./js/libs/codemirror/codemirror.js',
	'./js/libs/codemirror/mode/javascript.js',
	'./js/libs/codemirror/mode/glsl.js',

	'./js/libs/esprima.js',
	'./js/libs/jsonlint.js',

	'./js/libs/codemirror/addon/dialog.css',
	'./js/libs/codemirror/addon/show-hint.css',
	'./js/libs/codemirror/addon/tern.css',

	'./js/libs/codemirror/addon/dialog.js',
	'./js/libs/codemirror/addon/show-hint.js',
	'./js/libs/codemirror/addon/tern.js',
	'./js/libs/acorn/acorn.js',
	'./js/libs/acorn/acorn_loose.js',
	'./js/libs/acorn/walk.js',
	'./js/libs/ternjs/polyfill.js',
	'./js/libs/ternjs/signal.js',
	'./js/libs/ternjs/tern.js',
	'./js/libs/ternjs/def.js',
	'./js/libs/ternjs/comment.js',
	'./js/libs/ternjs/infer.js',
	'./js/libs/ternjs/doc_comment.js',
	'./js/libs/tern-threejs/threejs.js',

	'./js/libs/signals.min.js',
	'./js/libs/ui.js',
	'./js/libs/ui.three.js',

	'./js/libs/app.js',
	'./js/Player.js',
	'./js/Script.js',

	//

	'./css/main.css',

	'./js/EditorControls.js',
	'./js/Storage.js',

	'./js/Editor.js',
	'./js/Config.js',
	'./js/History.js',
	'./js/Loader.js',
	'./js/LoaderUtils.js',
	'./js/Menubar.js',
	'./js/Menubar.File.js',
	'./js/Menubar.Edit.js',
	'./js/Menubar.Add.js',
	'./js/Menubar.Play.js',
	'./js/Menubar.Examples.js',
	'./js/Menubar.Help.js',
	'./js/Menubar.Status.js',
	'./js/Resizer.js',
	'./js/Sidebar.js',
	'./js/Sidebar.Scene.js',
	'./js/Sidebar.Project.js',
	'./js/Sidebar.Settings.js',
	'./js/Sidebar.Settings.Shortcuts.js',
	'./js/Sidebar.Settings.Viewport.js',
	'./js/Sidebar.Properties.js',
	'./js/Sidebar.Object.js',
	'./js/Sidebar.Geometry.js',
	'./js/Sidebar.Geometry.Geometry.js',
	'./js/Sidebar.Geometry.BufferGeometry.js',
	'./js/Sidebar.Geometry.Modifiers.js',
	'./js/Sidebar.Geometry.BoxGeometry.js',
	'./js/Sidebar.Geometry.CircleGeometry.js',
	'./js/Sidebar.Geometry.CylinderGeometry.js',
	'./js/Sidebar.Geometry.DodecahedronGeometry.js',
	'./js/Sidebar.Geometry.ExtrudeGeometry.js',
	'./js/Sidebar.Geometry.IcosahedronGeometry.js',
	'./js/Sidebar.Geometry.OctahedronGeometry.js',
	'./js/Sidebar.Geometry.PlaneGeometry.js',
	'./js/Sidebar.Geometry.RingGeometry.js',
	'./js/Sidebar.Geometry.SphereGeometry.js',
	'./js/Sidebar.Geometry.ShapeGeometry.js',
	'./js/Sidebar.Geometry.TetrahedronGeometry.js',
	'./js/Sidebar.Geometry.TorusGeometry.js',
	'./js/Sidebar.Geometry.TorusKnotGeometry.js',
	'./js/Sidebar.Geometry.TubeGeometry.js',
	'./js/Sidebar.Geometry.TeapotBufferGeometry.js',
	'./js/Sidebar.Geometry.LatheGeometry.js',
	'./js/Sidebar.Material.js',
	'./js/Sidebar.Animation.js',
	'./js/Sidebar.Script.js',
	'./js/Sidebar.History.js',
	'./js/Strings.js',
	'./js/Toolbar.js',
	'./js/Viewport.js',
	'./js/Viewport.Camera.js',
	'./js/Viewport.Info.js',
	'./js/Viewport.ViewHelper.js',

	'./js/Command.js',
	'./js/commands/AddObjectCommand.js',
	'./js/commands/RemoveObjectCommand.js',
	'./js/commands/MoveObjectCommand.js',
	'./js/commands/SetPositionCommand.js',
	'./js/commands/SetRotationCommand.js',
	'./js/commands/SetScaleCommand.js',
	'./js/commands/SetValueCommand.js',
	'./js/commands/SetUuidCommand.js',
	'./js/commands/SetColorCommand.js',
	'./js/commands/SetGeometryCommand.js',
	'./js/commands/SetGeometryValueCommand.js',
	'./js/commands/MultiCmdsCommand.js',
	'./js/commands/AddScriptCommand.js',
	'./js/commands/RemoveScriptCommand.js',
	'./js/commands/SetScriptValueCommand.js',
	'./js/commands/SetMaterialCommand.js',
	'./js/commands/SetMaterialColorCommand.js',
	'./js/commands/SetMaterialMapCommand.js',
	'./js/commands/SetMaterialValueCommand.js',
	'./js/commands/SetMaterialVectorCommand.js',
	'./js/commands/SetSceneCommand.js',
	'./js/commands/Commands.js',

	//

	'./examples/arkanoid.app.json',
	'./examples/camera.app.json',
	'./examples/particles.app.json',
	'./examples/pong.app.json',
	'./examples/shaders.app.json'

];

self.addEventListener( 'install', async function () {

	const cache = await caches.open( 'threejs-editor' );

	assets.forEach( function ( asset ) {

		cache.add( asset ).catch( function () {

			console.error( '[SW] Cound\'t cache:', asset );

		} );

	} );

} );

self.addEventListener( 'fetch', async function ( event ) {

	const request = event.request;
	event.respondWith( cacheFirst( request ) );

} );

async function cacheFirst( request ) {

	const cachedResponse = await caches.match( request );

	if ( cachedResponse === undefined ) {

		console.error( '[SW] Not cached:', request.url );
		return fetch( request );

	}

	return cachedResponse;

}
<commit_msg>Editor: Removed CSS2Renderer dependency.<commit_after>// r119

const assets = [
	'./',

	'./manifest.json',
	'./images/icon.png',

	'../files/favicon.ico',

	'../build/three.module.js',

	'../examples/jsm/controls/TransformControls.js',

	'../examples/jsm/libs/chevrotain.module.min.js',
	'../examples/jsm/libs/inflate.module.min.js',
	'../examples/jsm/libs/jszip.module.min.js',

	'../examples/js/libs/draco/draco_decoder.js',
	'../examples/js/libs/draco/draco_decoder.wasm',
	'../examples/js/libs/draco/draco_encoder.js',
	'../examples/js/libs/draco/draco_wasm_wrapper.js',

	'../examples/js/libs/draco/gltf/draco_decoder.js',
	'../examples/js/libs/draco/gltf/draco_decoder.wasm',
	'../examples/js/libs/draco/gltf/draco_wasm_wrapper.js',

	'../examples/jsm/libs/rhino3dm/rhino3dm.wasm',
	'../examples/jsm/libs/rhino3dm/rhino3dm.js',

	'../examples/jsm/loaders/3DMLoader.js',
	'../examples/jsm/loaders/3MFLoader.js',
	'../examples/jsm/loaders/AMFLoader.js',
	'../examples/jsm/loaders/ColladaLoader.js',
	'../examples/jsm/loaders/DRACOLoader.js',
	'../examples/jsm/loaders/FBXLoader.js',
	'../examples/jsm/loaders/GLTFLoader.js',
	'../examples/jsm/loaders/KMZLoader.js',
	'../examples/jsm/loaders/MD2Loader.js',
	'../examples/jsm/loaders/OBJLoader.js',
	'../examples/jsm/loaders/MTLLoader.js',
	'../examples/jsm/loaders/PLYLoader.js',
	'../examples/jsm/loaders/RGBELoader.js',
	'../examples/jsm/loaders/STLLoader.js',
	'../examples/jsm/loaders/SVGLoader.js',
	'../examples/jsm/loaders/TGALoader.js',
	'../examples/jsm/loaders/TDSLoader.js',
	'../examples/jsm/loaders/VRMLLoader.js',
	'../examples/jsm/loaders/VTKLoader.js',

	'../examples/jsm/curves/NURBSCurve.js',
	'../examples/jsm/curves/NURBSUtils.js',

	'../examples/jsm/exporters/ColladaExporter.js',
	'../examples/jsm/exporters/DRACOExporter.js',
	'../examples/jsm/exporters/GLTFExporter.js',
	'../examples/jsm/exporters/OBJExporter.js',
	'../examples/jsm/exporters/PLYExporter.js',
	'../examples/jsm/exporters/STLExporter.js',

	'../examples/jsm/helpers/VertexNormalsHelper.js',

	'../examples/jsm/geometries/TeapotBufferGeometry.js',

	'../examples/jsm/webxr/VRButton.js',

	'./images/rotate.svg',
	'./images/scale.svg',
	'./images/translate.svg',

	'./js/libs/codemirror/codemirror.css',
	'./js/libs/codemirror/theme/monokai.css',

	'./js/libs/codemirror/codemirror.js',
	'./js/libs/codemirror/mode/javascript.js',
	'./js/libs/codemirror/mode/glsl.js',

	'./js/libs/esprima.js',
	'./js/libs/jsonlint.js',

	'./js/libs/codemirror/addon/dialog.css',
	'./js/libs/codemirror/addon/show-hint.css',
	'./js/libs/codemirror/addon/tern.css',

	'./js/libs/codemirror/addon/dialog.js',
	'./js/libs/codemirror/addon/show-hint.js',
	'./js/libs/codemirror/addon/tern.js',
	'./js/libs/acorn/acorn.js',
	'./js/libs/acorn/acorn_loose.js',
	'./js/libs/acorn/walk.js',
	'./js/libs/ternjs/polyfill.js',
	'./js/libs/ternjs/signal.js',
	'./js/libs/ternjs/tern.js',
	'./js/libs/ternjs/def.js',
	'./js/libs/ternjs/comment.js',
	'./js/libs/ternjs/infer.js',
	'./js/libs/ternjs/doc_comment.js',
	'./js/libs/tern-threejs/threejs.js',

	'./js/libs/signals.min.js',
	'./js/libs/ui.js',
	'./js/libs/ui.three.js',

	'./js/libs/app.js',
	'./js/Player.js',
	'./js/Script.js',

	//

	'./css/main.css',

	'./js/EditorControls.js',
	'./js/Storage.js',

	'./js/Editor.js',
	'./js/Config.js',
	'./js/History.js',
	'./js/Loader.js',
	'./js/LoaderUtils.js',
	'./js/Menubar.js',
	'./js/Menubar.File.js',
	'./js/Menubar.Edit.js',
	'./js/Menubar.Add.js',
	'./js/Menubar.Play.js',
	'./js/Menubar.Examples.js',
	'./js/Menubar.Help.js',
	'./js/Menubar.Status.js',
	'./js/Resizer.js',
	'./js/Sidebar.js',
	'./js/Sidebar.Scene.js',
	'./js/Sidebar.Project.js',
	'./js/Sidebar.Settings.js',
	'./js/Sidebar.Settings.Shortcuts.js',
	'./js/Sidebar.Settings.Viewport.js',
	'./js/Sidebar.Properties.js',
	'./js/Sidebar.Object.js',
	'./js/Sidebar.Geometry.js',
	'./js/Sidebar.Geometry.Geometry.js',
	'./js/Sidebar.Geometry.BufferGeometry.js',
	'./js/Sidebar.Geometry.Modifiers.js',
	'./js/Sidebar.Geometry.BoxGeometry.js',
	'./js/Sidebar.Geometry.CircleGeometry.js',
	'./js/Sidebar.Geometry.CylinderGeometry.js',
	'./js/Sidebar.Geometry.DodecahedronGeometry.js',
	'./js/Sidebar.Geometry.ExtrudeGeometry.js',
	'./js/Sidebar.Geometry.IcosahedronGeometry.js',
	'./js/Sidebar.Geometry.OctahedronGeometry.js',
	'./js/Sidebar.Geometry.PlaneGeometry.js',
	'./js/Sidebar.Geometry.RingGeometry.js',
	'./js/Sidebar.Geometry.SphereGeometry.js',
	'./js/Sidebar.Geometry.ShapeGeometry.js',
	'./js/Sidebar.Geometry.TetrahedronGeometry.js',
	'./js/Sidebar.Geometry.TorusGeometry.js',
	'./js/Sidebar.Geometry.TorusKnotGeometry.js',
	'./js/Sidebar.Geometry.TubeGeometry.js',
	'./js/Sidebar.Geometry.TeapotBufferGeometry.js',
	'./js/Sidebar.Geometry.LatheGeometry.js',
	'./js/Sidebar.Material.js',
	'./js/Sidebar.Animation.js',
	'./js/Sidebar.Script.js',
	'./js/Sidebar.History.js',
	'./js/Strings.js',
	'./js/Toolbar.js',
	'./js/Viewport.js',
	'./js/Viewport.Camera.js',
	'./js/Viewport.Info.js',
	'./js/Viewport.ViewHelper.js',

	'./js/Command.js',
	'./js/commands/AddObjectCommand.js',
	'./js/commands/RemoveObjectCommand.js',
	'./js/commands/MoveObjectCommand.js',
	'./js/commands/SetPositionCommand.js',
	'./js/commands/SetRotationCommand.js',
	'./js/commands/SetScaleCommand.js',
	'./js/commands/SetValueCommand.js',
	'./js/commands/SetUuidCommand.js',
	'./js/commands/SetColorCommand.js',
	'./js/commands/SetGeometryCommand.js',
	'./js/commands/SetGeometryValueCommand.js',
	'./js/commands/MultiCmdsCommand.js',
	'./js/commands/AddScriptCommand.js',
	'./js/commands/RemoveScriptCommand.js',
	'./js/commands/SetScriptValueCommand.js',
	'./js/commands/SetMaterialCommand.js',
	'./js/commands/SetMaterialColorCommand.js',
	'./js/commands/SetMaterialMapCommand.js',
	'./js/commands/SetMaterialValueCommand.js',
	'./js/commands/SetMaterialVectorCommand.js',
	'./js/commands/SetSceneCommand.js',
	'./js/commands/Commands.js',

	//

	'./examples/arkanoid.app.json',
	'./examples/camera.app.json',
	'./examples/particles.app.json',
	'./examples/pong.app.json',
	'./examples/shaders.app.json'

];

self.addEventListener( 'install', async function () {

	const cache = await caches.open( 'threejs-editor' );

	assets.forEach( function ( asset ) {

		cache.add( asset ).catch( function () {

			console.error( '[SW] Cound\'t cache:', asset );

		} );

	} );

} );

self.addEventListener( 'fetch', async function ( event ) {

	const request = event.request;
	event.respondWith( cacheFirst( request ) );

} );

async function cacheFirst( request ) {

	const cachedResponse = await caches.match( request );

	if ( cachedResponse === undefined ) {

		console.error( '[SW] Not cached:', request.url );
		return fetch( request );

	}

	return cachedResponse;

}
<|endoftext|>


<commit_before>//	Embed code into pages to receive data
//	Inspired by Google Analytics tracking code
//
//	Variables:
//		pip - where your tracker script is hosted, no http/https required
//		squeek - Unique ID for site/page/etc...
//

var pip = "domain.com/path/to/tracker.js";
var squeek = "xxxx-xxxx-xxxx-xxxx";

  (function() {
	var tc = document.createElement('script');
		tc.type = 'text/javascript';
		tc.async = true;
		tc.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + pip;
	var fs = document.getElementsByTagName('script')[0];
	fs.parentNode.insertBefore(tc, fs);
  })();
<commit_msg>Switch variable names<commit_after>//	Embed code into pages to receive data
//	Inspired by Google Analytics tracking code
//
//	Variables:
//		squeek - where your tracker script is hosted, no http/https required
//		pip - Unique ID for site/page/etc...
//

var squeek = "domain.com/path/to/tracker.js";
var pip = "xxxx-xxxx-xxxx-xxxx";

  (function() {
	var tc = document.createElement('script');
		tc.type = 'text/javascript';
		tc.async = true;
		tc.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + pip;
	var fs = document.getElementsByTagName('script')[0];
	fs.parentNode.insertBefore(tc, fs);
  })();
<|endoftext|>


<commit_before>var request = require('request');
var querystring = require('querystring');
/*var gen = require('random-seed');*/

var clients = 0;

class queue {
	constructor() {
		this.reqqueue = {};
		this.queue_handler();
	}
	add(req, callback) {
		var id = Math.max.apply(Math, Object.keys(this.reqqueue))+1 | 0;
		this.reqqueue[id] = [req, callback];
		return id;
	}
	remove(id) {
		delete this.reqqueue[id];
	}
	queue_handler() {
		if (Object.keys(this.reqqueue).length <= 0) {
			setTimeout(function (t){t.queue_handler()}, 100, this);
			return;
		}
		var id = Math.min.apply(Math, Object.keys(this.reqqueue));
		var url = this.reqqueue[id][0];
		var that = this;
		request(url, function (error, response, body) {
			if (!error && response.statusCode == 200) {
				setTimeout(function(t) {
					that.remove(id);
					that.queue_handler();
				}, 10);
				that.reqqueue[id][1](body);
			} else if (error) {
				throw (error);
			}
		});
	}
}

class true_random {
	constructor(cache_size = 100, min_cache = 50, debug = false, callback = function() {}) {
		this.cache_size = cache_size;
		this.client = clients++;
		this.queue = new queue();
		this.cache = [];
		this._cache(callback);
		this.debug = debug;
		this.min_cache = min_cache;
	}
	_debug(s) {
		if (this.debug == true) {
			console.debug(s);
		}
	}
	parser(callback) {
		return function(data) {
			callback(JSON.parse(data).data.map(function(a) {
				return parseInt(a, 16);
			}));
		};
	}
	integer(min = 0, max = 1) {
		if (this.cache.length-1<this.min_cache) {
			this._cache();
		} if (this.cache.length < 1) {
			return 'Nen';
		}
		var n = (this.cache[0]/281474976710655)*(max-min)+min;
		this.cache.shift();
		return n;
	}
	integers(min = 0, max = 1, num = 1) {
		if (this.cache.length-num-1<=this.min_cache) {
			this._cache();
		} if (this.cache.length < num) {
			return 'NeN';
		}
		var n = this.cache.slice(0, num).map(function(f) {
			return (f/281474976710655)*(max-min)+min
		});
		this.cache = this.cache.slice(num, this.cache.length);
		return n;
	}
	_cache(callback = function() {}) {
		var that = this;
		this._integers(function(ints) {
			that.cache = that.cache.concat(ints.map(function(n) {return parseInt(n)}));
			that._debug('Done cacheing');
			callback();
		}, this.cache_size)
	}
	_integer(callback) {
		this._integers(callback, 1);
	}
	_integers(callback, num = 1) {
		if (num > 1024 || num < 1) {
			this._debug('Error num='+num);
			throw (new RangeError('Num argument is not in range {1, 1024}'));
		}
		var url = "http://qrng.anu.edu.au/API/jsonI.php?"+querystring.stringify({length: num, type: 'hex16', size: 6});
		this.queue.add(url, this.parser(callback));
	}
}

/*var random = function(callback) {
	var r = new true_random(5, false, function() {
		callback(new gen(r.integer));
	});
}*/

module.exports = {rand: true_random};
<commit_msg>Improved callback handling<commit_after>var request = require('request');
var querystring = require('querystring');
/*var gen = require('random-seed');*/

var clients = 0;

class queue {
	constructor() {
		this.reqqueue = {};
		this.queue_handler();
	}
	add(req, callback) {
		var id = Math.max.apply(Math, Object.keys(this.reqqueue))+1 | 0;
		this.reqqueue[id] = [req, callback];
		return id;
	}
	remove(id) {
		delete this.reqqueue[id];
	}
	queue_handler() {
		if (Object.keys(this.reqqueue).length <= 0) {
			setTimeout(function (t){t.queue_handler()}, 100, this);
			return;
		}
		var id = Math.min.apply(Math, Object.keys(this.reqqueue));
		var url = this.reqqueue[id][0];
		var that = this;
		request(url, function (error, response, body) {
			if (!error && response.statusCode == 200) {
				setTimeout(function(t) {
					that.remove(id);
					that.queue_handler();
				}, 10);
				that.reqqueue[id][1](body);
			} else if (error) {
				throw (error);
			}
		});
	}
}

class true_random {
	constructor(cache_size = 100, min_cache = 50, callback = function() {}, debug = false) {
		this.cache_size = cache_size;
		this.client = clients++;
		this.queue = new queue();
		this.cache = [];
		this._cache(callback);
		this.debug = debug;
		this.min_cache = min_cache;
	}
	_debug(s) {
		if (this.debug == true) {
			console.debug(s);
		}
	}
	parser(callback) {
		return function(data) {
			callback(JSON.parse(data).data.map(function(a) {
				return parseInt(a, 16);
			}));
		};
	}
	integer(min = 0, max = 1) {
		if (this.cache.length-1<this.min_cache) {
			this._cache();
		} if (this.cache.length < 1) {
			return 'Nen';
		}
		var n = (this.cache[0]/281474976710655)*(max-min)+min;
		this.cache.shift();
		return n;
	}
	integers(min = 0, max = 1, num = 1) {
		if (this.cache.length-num-1<=this.min_cache) {
			this._cache();
		} if (this.cache.length < num) {
			return 'NeN';
		}
		var n = this.cache.slice(0, num).map(function(f) {
			return (f/281474976710655)*(max-min)+min
		});
		this.cache = this.cache.slice(num, this.cache.length);
		return n;
	}
	_cache(callback = function() {}) {
		var that = this;
		this._integers(function(ints) {
			that.cache = that.cache.concat(ints.map(function(n) {return parseInt(n)}));
			that._debug('Done cacheing');
			callback(that);
		}, this.cache_size)
	}
	_integer(callback) {
		this._integers(callback, 1);
	}
	_integers(callback, num = 1) {
		if (num > 1024 || num < 1) {
			this._debug('Error num='+num);
			throw (new RangeError('Num argument is not in range {1, 1024}'));
		}
		var url = "http://qrng.anu.edu.au/API/jsonI.php?"+querystring.stringify({length: num, type: 'hex16', size: 6});
		this.queue.add(url, this.parser(callback));
	}
}

/*var random = function(callback) {
	var r = new true_random(5, false, function() {
		callback(new gen(r.integer));
	});
}*/

module.exports = {rand: true_random};
<|endoftext|>


<commit_before>const {normalize, resolve} = require('path');
const commonjs = require('rollup-plugin-commonjs');
const nodeResolve = require('rollup-plugin-node-resolve');

/** Resolves browser imports. */
function browserResolve() {
  return {
    name: 'browserResolve',
    resolveId: source => source == '../lib/index.js' ? resolve(__dirname, '../lib/browser.js') : null
  };
}

module.exports = config => config.set({
  basePath: resolve(__dirname, '..'),
  browsers: ['FirefoxHeadless'],
  files: [
    {pattern: 'test/**/*.js', type: 'module'}
  ],
  frameworks: ['mocha'],
  preprocessors: {
    'test/**/*.js': ['rollup']
  },
  reporters: ['progress'],
  rollupPreprocessor: {
    onwarn: (warning, warn) => {
      if (warning.code == 'CIRCULAR_DEPENDENCY' && warning.importer.includes(normalize('node_modules/chai'))) return;
      warn(warning);
    },
    output: {format: 'iife', name: 'akismet'},
    plugins: [browserResolve(), nodeResolve(), commonjs()]
  },
  singleRun: true
});
<commit_msg>Updated the documentation<commit_after>const {normalize, resolve} = require('path');
const commonjs = require('rollup-plugin-commonjs');
const nodeResolve = require('rollup-plugin-node-resolve');

/**
 * Resolves browser imports.
 * @return {object} A Rollup plugin resolving browser imports.
 */
function browserResolve() {
  return {
    name: 'browserResolve',
    resolveId: source => source == '../lib/index.js' ? resolve(__dirname, '../lib/browser.js') : null
  };
}

module.exports = config => config.set({
  basePath: resolve(__dirname, '..'),
  browsers: ['FirefoxHeadless'],
  files: [
    {pattern: 'test/**/*.js', type: 'module'}
  ],
  frameworks: ['mocha'],
  preprocessors: {
    'test/**/*.js': ['rollup']
  },
  reporters: ['progress'],
  rollupPreprocessor: {
    onwarn: (warning, warn) => {
      if (warning.code == 'CIRCULAR_DEPENDENCY' && warning.importer.includes(normalize('node_modules/chai'))) return;
      warn(warning);
    },
    output: {format: 'iife', name: 'akismet'},
    plugins: [browserResolve(), nodeResolve(), commonjs()]
  },
  singleRun: true
});
<|endoftext|>


<commit_before>'use strict';

angular.module('treasuremapApp')
  .controller('NewCtrl', function($scope, $http, $timeout, $q, uiGmapGoogleMapApi, Auth) {
    uiGmapGoogleMapApi.then(function(maps) {
      $timeout(function() {
        //maps.event.trigger($scope.mapNew, 'resize');
        $scope.showMap = true;
      }, 100);
    });

    $scope.newLocation = {
      details: {
        imports: '',
        pictures: [],
        links: []
      }
    };
    $scope.alerts = [];

    $scope.closeAlert = function(index) {
      $scope.alerts.splice(index, 1);
    };

    $scope.mapNew = {
      control: {},
      center: {
        latitude: 52.5075419,
        longitude: 13.4251364
      },
      zoom: 16,
      options: {
        scrollwheel: false,
        disableDefaultUI: true,
        zoomControl: true,
        draggable: false
      }
    };
    $scope.searchbox = {
      template: 'searchbox.tpl.html',
      events: {
        places_changed: function(searchBox) {
          $scope.place = searchBox.getPlaces()[0];

          var zipcode = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'postal_code');
          });
          var city = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'administrative_area_level_1') || _.contains(i.types, 'locality');
          });
          var street = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'route');
          });
          var number = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'street_number');
          });
          $scope.newLocation.address.zipcode = zipcode ? zipcode.short_name : '';
          $scope.newLocation.address.city = city ? city.long_name : '';
          $scope.newLocation.address.street = street ? street.long_name : '';
          $scope.newLocation.address.street += number ? ' ' + number.short_name : '';

          $scope.newLocation.coordinates = {
            lat: $scope.place.geometry.location.lat(),
            latitude: $scope.place.geometry.location.lat(),
            lng: $scope.place.geometry.location.lng(),
            longitude: $scope.place.geometry.location.lng()
          };
          $scope.mapNew.center = $scope.newLocation.coordinates;
        }
      }
    };

    $scope.sources = [{
      name: 'Wikipedia',
      url: '//de.wikipedia.org/w/',
      enabled: true
    }];

    function wikiImport(source) {
      var deferred = $q.defer();
      var baseURL = source.url;
      var name = encodeURI($scope.newLocation.details.name);
      if (source.name === 'Wikipedia') {
        $http.jsonp(baseURL + 'api.php?action=query&titles=' + name + '&format=json&redirects&callback=JSON_CALLBACK')
          .success(function(obj) {
            var pageID = Object.keys(obj.query.pages)[0];

            $http.jsonp(baseURL + 'api.php?action=query&pageids=' + pageID + '&prop=info&inprop=url&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.query.pages;
                for (var key in objects) {
                  var value = objects[key];
                  $scope.newLocation.details.links.push({
                    name: 'Wikipedia',
                    url: value.fullurl
                  });

                  $http.jsonp('http://wikitravel.org/wiki/de/api.php?action=query&list=search&srsearch=' + name + '&srwhat=text&continue&format=json&callback=JSON_CALLBACK')
                    .success(function(data) {
                      if (data.search.length >= 1) {
                        $scope.newLocation.details.links.push({
                          name: 'Wikitravel',
                          url: 'http://wikitravel.org/wiki/de/index.php?search=' + name
                        });
                      }
                    })
                    .error(function(data, status) {
                      console.log('Error! ' + status);
                    });

                  $scope.newLocation.details.links.push({
                    name: 'Google',
                    url: 'https://www.google.de/#q=' + name
                  });
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

            $http.jsonp(baseURL + 'api.php?action=parse&pageid=' + pageID + '&prop=text&section=0&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.parse.text;
                for (var key in objects) {
                  var value = objects[key];
                  $scope.newLocation.details.imports += value.replace(/href="\//g, 'target="_blank" href="http://de.wikipedia.org/');
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

            $http.jsonp(baseURL + 'api.php?action=parse&pageid=' + pageID + '&prop=images&section=0&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.parse.images;
                for (var key in objects) {
                  var title = encodeURI(objects[key]);
                  $http.jsonp(baseURL + 'api.php?action=query&titles=Image:' + title + '&prop=imageinfo&format=json&iiprop=url&callback=JSON_CALLBACK')
                    .success(function(data) {
                      var objects = data.query.pages;
                      for (var key in objects) {
                        var value = objects[key].imageinfo[0].url;
                        $scope.newLocation.details.pictures.push(value);
                        deferred.resolve();
                      }
                    })
                    .error(function(data, status) {
                      console.log('Error! ' + status);
                    });
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

          })
          .error(function(data, status) {
            console.log('Error! ' + status);
          });
      }
      return deferred.promise;
    }

    $http.get('/api/categories')
      .success(function(categories) {
        $scope.categories = categories;
      })
      .error(function(data, status) {
        console.log('Error!' + status);
        console.log(data);
      });

    $scope.addLocation = function(form) {
      $scope.submitted = true;

      if ($scope.newLocation === {}) {
        return;
      }

      if ($scope.sources[0].enabled) {
        wikiImport($scope.sources[0]).then(function() {
          saveLocation(form);
        });
      }

    };

    $scope.setCategory = function(category) {
      $scope.newLocation.details.category = category;
      $scope.newLocation.details.category.url = category.imgUrl;
    }

    function saveLocation(form) {
      if (form.$valid && $scope.newLocation.coordinates) {
        $scope.newLocation.details.category = $scope.newLocation.details.category._id;
        $http.post('/api/locations', $scope.newLocation)
          .success(function(data, status) {
            console.log('Success! ' + status);
            console.log(data);
            $scope.alerts.push({
              type: 'success',
              msg: 'New Location successfully added!'
            });

            $scope.newLocation = {};

            $http.get('/api/categories/' + data.details.category)
              .success(function(category) {
                data.details.category = category;
                $scope.$close(data);
              });
          })
          .error(function(data, status) {
            console.log('Error! ' + status);
            $scope.alerts.push({
              type: 'danger',
              msg: 'Couln\'t add new location!'
            });
          });
      } else {
        form.$valid = false;
      }
    }

    var S3_BUCKET = ***REMOVED***;
    var creds = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: ***REMOVED***,
    });

    AWS.config.update({
      region: ***REMOVED***,
      credentials: creds
    });

    // TODO: User Auth for Cognito
    /*
    if(Auth.isLoggedIn){
      if(Auth.getCurrentUser().provider == 'local'){
         console.log(Auth.getToken());
         //userLoggedIn('treasuremapApp', Auth.getToken());
         userLoggedIn('treasuremap-stage.herokuapp.com', Auth.getToken());
      } else {
         userLoggedIn(Auth.getCurrentUser().provider, Auth.getToken());
      }
   }

    function userLoggedIn(providerName, token) {
      creds.params.Logins = {};
      creds.params.Logins[providerName] = token;
      creds.expired = true;
      creds.refresh(function(err, data) {
        if (err) console.log(err, err.stack);
        else console.log(data);
      });
   }*/

    var S3 = new AWS.S3({
      region: ***REMOVED***
    });

    $scope.$watch('files', function() {
      $scope.upload($scope.files);
    });

    $scope.upload = function(files) {
      if (files && files.length) {
        for (var i = 0; i < files.length; i++) {
          var file = files[i];
          var filename = CryptoJS.MD5(file) + '.' + file.name.split('.').pop();
          var params = {
            Bucket: S3_BUCKET,
            Key: 'images/' + filename,
            ContentType: file.type,
            Body: file
          };
          S3.upload(params, function(err, data) {
            if (err) {
              console.log(err, err.stack);
            } else {
              $scope.newLocation.details.pictures.push(data.Location);
            }
          });
        }
      }
    };
  });
<commit_msg>quick fix<commit_after>'use strict';

angular.module('treasuremapApp')
  .controller('NewCtrl', function($scope, $http, $timeout, $q, uiGmapGoogleMapApi, Auth) {
    uiGmapGoogleMapApi.then(function(maps) {
      $timeout(function() {
        //maps.event.trigger($scope.mapNew, 'resize');
        $scope.showMap = true;
      }, 100);
    });

    $scope.newLocation = {
      details: {
        imports: '',
        pictures: [],
        links: []
      }
    };
    $scope.alerts = [];

    $scope.closeAlert = function(index) {
      $scope.alerts.splice(index, 1);
    };

    $scope.mapNew = {
      control: {},
      center: {
        latitude: 52.5075419,
        longitude: 13.4251364
      },
      zoom: 16,
      options: {
        scrollwheel: false,
        disableDefaultUI: true,
        zoomControl: true,
        draggable: false
      }
    };
    $scope.searchbox = {
      template: 'searchbox.tpl.html',
      events: {
        places_changed: function(searchBox) {
          $scope.place = searchBox.getPlaces()[0];

          var zipcode = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'postal_code');
          });
          var city = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'administrative_area_level_1') || _.contains(i.types, 'locality');
          });
          var street = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'route');
          });
          var number = _.find($scope.place.address_components, function(i) {
            return _.contains(i.types, 'street_number');
          });
          $scope.newLocation.address.zipcode = zipcode ? zipcode.short_name : '';
          $scope.newLocation.address.city = city ? city.long_name : '';
          $scope.newLocation.address.street = street ? street.long_name : '';
          $scope.newLocation.address.street += number ? ' ' + number.short_name : '';

          $scope.newLocation.coordinates = {
            lat: $scope.place.geometry.location.lat(),
            latitude: $scope.place.geometry.location.lat(),
            lng: $scope.place.geometry.location.lng(),
            longitude: $scope.place.geometry.location.lng()
          };
          $scope.mapNew.center = $scope.newLocation.coordinates;
        }
      }
    };

    $scope.sources = [{
      name: 'Wikipedia',
      url: '//de.wikipedia.org/w/',
      enabled: true
    }];

    function wikiImport(source) {
      var deferred = $q.defer();
      var baseURL = source.url;
      var name = encodeURI($scope.newLocation.details.name);
      if (source.name === 'Wikipedia') {
        $http.jsonp(baseURL + 'api.php?action=query&titles=' + name + '&format=json&redirects&callback=JSON_CALLBACK')
          .success(function(obj) {
            var pageID = Object.keys(obj.query.pages)[0];

            $http.jsonp(baseURL + 'api.php?action=query&pageids=' + pageID + '&prop=info&inprop=url&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.query.pages;
                for (var key in objects) {
                  var value = objects[key];
                  $scope.newLocation.details.links.push({
                    name: 'Wikipedia',
                    url: value.fullurl
                  });

                  $http.jsonp('http://wikitravel.org/wiki/de/api.php?action=query&list=search&srsearch=' + name + '&srwhat=text&continue&format=json&callback=JSON_CALLBACK')
                    .success(function(data) {
                      if (data.query.search.length >= 1) {
                        $scope.newLocation.details.links.push({
                          name: 'Wikitravel',
                          url: 'http://wikitravel.org/wiki/de/index.php?search=' + name
                        });
                      }
                    })
                    .error(function(data, status) {
                      console.log('Error! ' + status);
                    });

                  $scope.newLocation.details.links.push({
                    name: 'Google',
                    url: 'https://www.google.de/#q=' + name
                  });
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

            $http.jsonp(baseURL + 'api.php?action=parse&pageid=' + pageID + '&prop=text&section=0&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.parse.text;
                for (var key in objects) {
                  var value = objects[key];
                  $scope.newLocation.details.imports += value.replace(/href="\//g, 'target="_blank" href="http://de.wikipedia.org/');
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

            $http.jsonp(baseURL + 'api.php?action=parse&pageid=' + pageID + '&prop=images&section=0&format=json&callback=JSON_CALLBACK')
              .success(function(data) {
                var objects = data.parse.images;
                for (var key in objects) {
                  var title = encodeURI(objects[key]);
                  $http.jsonp(baseURL + 'api.php?action=query&titles=Image:' + title + '&prop=imageinfo&format=json&iiprop=url&callback=JSON_CALLBACK')
                    .success(function(data) {
                      var objects = data.query.pages;
                      for (var key in objects) {
                        var value = objects[key].imageinfo[0].url;
                        $scope.newLocation.details.pictures.push(value);
                        deferred.resolve();
                      }
                    })
                    .error(function(data, status) {
                      console.log('Error! ' + status);
                    });
                }
              })
              .error(function(data, status) {
                console.log('Error! ' + status);
              });

          })
          .error(function(data, status) {
            console.log('Error! ' + status);
          });
      }
      return deferred.promise;
    }

    $http.get('/api/categories')
      .success(function(categories) {
        $scope.categories = categories;
      })
      .error(function(data, status) {
        console.log('Error!' + status);
        console.log(data);
      });

    $scope.addLocation = function(form) {
      $scope.submitted = true;

      if ($scope.newLocation === {}) {
        return;
      }

      if ($scope.sources[0].enabled) {
        wikiImport($scope.sources[0]).then(function() {
          saveLocation(form);
        });
      }

    };

    $scope.setCategory = function(category) {
      $scope.newLocation.details.category = category;
      $scope.newLocation.details.category.url = category.imgUrl;
    }

    function saveLocation(form) {
      if (form.$valid && $scope.newLocation.coordinates) {
        $scope.newLocation.details.category = $scope.newLocation.details.category._id;
        $http.post('/api/locations', $scope.newLocation)
          .success(function(data, status) {
            console.log('Success! ' + status);
            console.log(data);
            $scope.alerts.push({
              type: 'success',
              msg: 'New Location successfully added!'
            });

            $scope.newLocation = {};

            $http.get('/api/categories/' + data.details.category)
              .success(function(category) {
                data.details.category = category;
                $scope.$close(data);
              });
          })
          .error(function(data, status) {
            console.log('Error! ' + status);
            $scope.alerts.push({
              type: 'danger',
              msg: 'Couln\'t add new location!'
            });
          });
      } else {
        form.$valid = false;
      }
    }

    var S3_BUCKET = ***REMOVED***;
    var creds = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: ***REMOVED***,
    });

    AWS.config.update({
      region: ***REMOVED***,
      credentials: creds
    });

    // TODO: User Auth for Cognito
    /*
    if(Auth.isLoggedIn){
      if(Auth.getCurrentUser().provider == 'local'){
         console.log(Auth.getToken());
         //userLoggedIn('treasuremapApp', Auth.getToken());
         userLoggedIn('treasuremap-stage.herokuapp.com', Auth.getToken());
      } else {
         userLoggedIn(Auth.getCurrentUser().provider, Auth.getToken());
      }
   }

    function userLoggedIn(providerName, token) {
      creds.params.Logins = {};
      creds.params.Logins[providerName] = token;
      creds.expired = true;
      creds.refresh(function(err, data) {
        if (err) console.log(err, err.stack);
        else console.log(data);
      });
   }*/

    var S3 = new AWS.S3({
      region: ***REMOVED***
    });

    $scope.$watch('files', function() {
      $scope.upload($scope.files);
    });

    $scope.upload = function(files) {
      if (files && files.length) {
        for (var i = 0; i < files.length; i++) {
          var file = files[i];
          var filename = CryptoJS.MD5(file) + '.' + file.name.split('.').pop();
          var params = {
            Bucket: S3_BUCKET,
            Key: 'images/' + filename,
            ContentLength: file.length,
            ContentType: file.type,
            Body: file
          };
          S3.upload(params, function(err, data) {
            if (err) {
              console.log(err, err.stack);
            } else {
              $scope.newLocation.details.pictures.push(data.Location);
            }
          });
        }
      }
    };
  });
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import ProjectActionCreator from '../../actions/project'
import PropTypes from 'prop-types'
import {Link} from 'react-router-dom'
import Card, {CardContent} from 'material-ui/Card'
import Typography from 'material-ui/Typography'
import styles from './ProjectDetail.scss'

class ProjectDetail extends Component {
  constructor (args) {
    super(args)
    this.state = {projectId: this.props.match.params.id}
  }

  componentDidMount () {
    this.props.fetchProjectById(this.state.projectId)
  }

  renderApplication (application) {
    return (
      <Card>
        <CardContent>
          <Typography>
            Applicant: {application.volunteer.email}
          </Typography>
          <Typography expandable={true}>
            Message: {application.message}
          </Typography>
          <Typography>
            Submitted at: {application.createdAt}
          </Typography>
        </CardContent>
      </Card>
    )
  }

  render () {
    const project = this.props.project || {}
    const applications = project.applications || []
    return applications.length === 0 ? (

No one had applied this project

          <Link to={`/profile`}>Go Back</Link>

) : (

The following applicants applied this project:

          {
            applications.map((app) =>
 {this.renderApplication(app)}
)
          }

          <Link to={`/profile`} >Go Back</Link>

)
  }
}

const mapDispatchToProps = {
  fetchProjectById: ProjectActionCreator.fetchProjectById
}

const mapStateToProperties = (state) => {
  return {project: state.project.project || {}}
}

ProjectDetail.propTypes = {
  project: PropTypes.object,
  fetchProjectById: PropTypes.func,
  match: PropTypes.object
}

export default connect(mapStateToProperties, mapDispatchToProps)(ProjectDetail)
<commit_msg>Go to https://test.calltocode.org/ Try to hover and click the nav links Links work as expected<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import ProjectActionCreator from '../../actions/project'
import PropTypes from 'prop-types'
import {Link} from 'react-router-dom'
import Card, {CardContent} from 'material-ui/Card'
import Typography from 'material-ui/Typography'
import styles from './ProjectDetail.scss'

class ProjectDetail extends Component {
  componentDidMount () {
    this.props.fetchProjectById(this.props.match.params.id)
  }

  renderApplication (application) {
    return (
      <Card>
        <CardContent>
          <Typography>
            Applicant: {application.volunteer.email}
          </Typography>
          <Typography expandable={true}>
            Message: {application.message}
          </Typography>
          <Typography>
            Submitted at: {application.createdAt}
          </Typography>
        </CardContent>
      </Card>
    )
  }

  render () {
    const project = this.props.project || {};
    const applications = project.applications || [];
    return applications.length === 0 ? (

No one had applied this project

          <Link to={`/profile`}>Go Back</Link>

) : (

The following applicants applied this project:

          {
            applications.map((app) =>
 {this.renderApplication(app)}
)
          }

          <Link to={`/profile`} >Go Back</Link>

)
  }
}

const mapDispatchToProps = {
  fetchProjectById: ProjectActionCreator.fetchProjectById
};

const mapStateToProperties = (state) => {
  return {project: state.project.project || {}}
};

ProjectDetail.propTypes = {
  project: PropTypes.object,
  fetchProjectById: PropTypes.func,
  match: PropTypes.object
};

export default connect(mapStateToProperties, mapDispatchToProps)(ProjectDetail)
<|endoftext|>


<commit_before>/**
 * Mailvelope - secure email with OpenPGP encryption for Webmail
 * Copyright (C) 2014  Thomas Oberndörfer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * 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/>.
 */

'use strict';

define(function(require, exports, module) {

  var sub = require('./sub.controller');

  function EditorController(port) {
    sub.SubController.call(this, port);
    if (!port) {
      this.mainType = 'editor';
      this.id = this.mvelo.util.getHash();
    }
    this.initText = '';
    this.encryptCallback = null;
    this.keyringId = null;
    this.pwdCache = require('../pwdCache');
    this.editorPopup = null;
    this.getRecipients = null;
    this.keyidBuffer = null;
    this.signBuffer = null;
    this.pwdControl = null;
    this.keyring = require('../keyring');
    this.mailbuild = require('../../mailbuild');
    this.pgpMIME = false;
  }

  EditorController.prototype = Object.create(sub.SubController.prototype);

  EditorController.prototype.handlePortMessage = function(msg) {
    var that = this;
    //console.log('EditorController.handlePortMessage', msg);
    switch (msg.event) {
      case 'editor-init':
        this.ports.editor.postMessage({event: 'set-text', text: this.initText});
        break;
      case 'editor-cancel':
        this.editorPopup.close();
        this.editorPopup = null;
        break;
      case 'editor-transfer-output':
        this.editorPopup.close();
        this.editorPopup = null;
        this.encryptCallback(null, msg.data);
        break;
      case 'encrypt-dialog-init':
        // send content
        this.mvelo.data.load('common/ui/inline/dialogs/templates/encrypt.html', function(content) {
          //console.log('content rendered', content);
          that.ports.eDialog.postMessage({event: 'encrypt-dialog-content', data: content});
          // get potential recipients
          that.getRecipients(function(result) {
            that.ports.eDialog.postMessage({event: 'public-key-userids', keys: result.keys, primary: result.primary});
          });
        });
        break;
      case 'sign-dialog-init':
        var localKeyring = this.keyring.getById(this.mvelo.LOCAL_KEYRING_ID);
        var keys = localKeyring.getPrivateKeys();
        var primary = localKeyring.getAttributes().primary_key;
        this.mvelo.data.load('common/ui/inline/dialogs/templates/sign.html', function(content) {
          var port = that.ports.sDialog;
          port.postMessage({event: 'sign-dialog-content', data: content});
          port.postMessage({event: 'signing-key-userids', keys: keys, primary: primary});
        });
        break;
      case 'encrypt-dialog-cancel':
      case 'sign-dialog-cancel':
        // forward event to encrypt frame
        this.ports.editor.postMessage(msg);
        break;
      case 'encrypt-dialog-ok':
        // add recipients to buffer
        this.keyidBuffer = msg.recipient;
        // get email text from eFrame
        this.ports.editor.postMessage({event: 'get-plaintext', action: 'encrypt'});
        break;
      case 'editor-container-encrypt':
        this.pgpMIME = true;
        this.keyringId = msg.keyringId;
        var keyIdMap = this.keyring.getById(this.keyringId).getKeyIdByAddress(msg.recipients, {validity: true});
        if (Object.keys(keyIdMap).some(function(keyId) {
          return keyIdMap[keyId] === false;
        })) {
          var error = new Error('No valid encryption key for recipient address');
          error.code = 'NO_KEY_FOR_RECIPIENT';
          this.ports.editorCont.postMessage({event: 'error-message', error: error});
          return;
        }
        var keyIds = [];
        msg.recipients.forEach(function(recipient) {
          keyIds = keyIds.concat(keyIdMap[recipient]);
        });
        this.keyidBuffer = this.mvelo.util.sortAndDeDup(keyIds);
        this.ports.editor.postMessage({event: 'get-plaintext', action: 'encrypt'});
        break;
      case 'sign-dialog-ok':
        this.signBuffer = {};
        var cacheEntry = this.pwdCache.get(msg.signKeyId, msg.signKeyId);
        if (cacheEntry && cacheEntry.key) {
          this.signBuffer.key = cacheEntry.key;
          this.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
        } else {
          var key = this.keyring.getById(this.mvelo.LOCAL_KEYRING_ID).getKeyForSigning(msg.signKeyId);
          // add key in buffer
          this.signBuffer.key = key.signKey;
          this.signBuffer.keyid = msg.signKeyId;
          this.signBuffer.userid = key.userId;
          if (cacheEntry) {
            this.pwdCache.unlock(cacheEntry, this.signBuffer, function() {
              that.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
            });
          } else {
            // open password dialog
            this.pwdControl = sub.factory.get('pwdDialog');
            this.pwdControl.unlockKey({
              message: this.signBuffer,
              openPopup: false
            }, function(err) {
              if (err === 'pwd-dialog-cancel') {
                that.ports.editor.postMessage({event: 'hide-pwd-dialog'});
                return;
              }
              if (err) {
                // TODO: propagate error to sign dialog
              }
              // success
              that.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
            });
            this.ports.editor.postMessage({event: 'show-pwd-dialog', id: this.pwdControl.id});
          }
        }
        break;
      case 'editor-plaintext':
        if (msg.action === 'encrypt') {
          var data = this.buildMail(msg.message, msg.attachments);
          this.model.encryptMessage(data, this.keyringId, this.keyidBuffer, function(err, msg) {
            var port = that.ports.editorCont || that.ports.editor;
            port.postMessage({event: 'encrypted-message', message: msg});
          });
        } else if (msg.action === 'sign') {
          this.model.signMessage(msg.message, this.signBuffer.key, function(err, msg) {
            that.ports.editor.postMessage({event: 'signed-message', message: msg});
          });
        } else {
          throw new Error('Unknown eframe action:', msg.action);
        }
        break;
      default:
        console.log('unknown event', msg);
    }
  };

  EditorController.prototype.encrypt = function(options, callback) {
    var that = this;
    this.initText = options.initText;
    this.getRecipients = options.getRecipients;
    this.keyringId = options.keyringId || this.mvelo.LOCAL_KEYRING_ID;
    this.encryptCallback = callback;
    this.mvelo.windows.openPopup('common/ui/editor/editor.html?id=' + this.id + '&editor_type=' + this.prefs.data().general.editor_type, {width: 820, height: 450, modal: false}, function(window) {
      that.editorPopup = window;
    });
  };

  EditorController.prototype.buildMail = function(message, attachments) {
    //var t0 = Date.now();
    var mainMessage = new this.mailbuild("multipart/mixed");
    var composedMessage;
    var hasAttachment;
    if (message) {
      var textMime = new this.mailbuild("text/plain")
        .setHeader("Content-Type", "text/plain; charset=utf-8")
        .addHeader("Content-Transfer-Encoding", "quoted-printable")
        .setContent(message);
      mainMessage.appendChild(textMime);
    }
    if (attachments && Object.keys(attachments).length > 0) {
      hasAttachment = true;
      for (var attachment in attachments) {
        var attachmentMime = new this.mailbuild("text/plain")
          .createChild(false, {filename: attachments[attachment].filename})
          //.setHeader("Content-Type", msg.attachments[attachment].type+"; charset=utf-8")
          .addHeader("Content-Transfer-Encoding", "base64")
          .addHeader("Content-Disposition", "attachment") // ; filename="+msg.attachments[attachment].filename
          .setContent(attachments[attachment].content);
        mainMessage.appendChild(attachmentMime);
      }
    }
    if (hasAttachment || this.pgpMIME) {
      composedMessage = mainMessage.build();
    } else {
      composedMessage = message;
    }
    //var t1 = Date.now();
    //console.log("Building mime message took " + (t1 - t0) + " milliseconds. Current time: " + t1);
    return composedMessage;
  };

  exports.EditorController = EditorController;

});
<commit_msg>editor.encrypt uses auto_add_primary flag<commit_after>/**
 * Mailvelope - secure email with OpenPGP encryption for Webmail
 * Copyright (C) 2014  Thomas Oberndörfer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * 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/>.
 */

'use strict';

define(function(require, exports, module) {

  var sub = require('./sub.controller');

  function EditorController(port) {
    sub.SubController.call(this, port);
    if (!port) {
      this.mainType = 'editor';
      this.id = this.mvelo.util.getHash();
    }
    this.initText = '';
    this.encryptCallback = null;
    this.keyringId = null;
    this.pwdCache = require('../pwdCache');
    this.editorPopup = null;
    this.getRecipients = null;
    this.keyidBuffer = null;
    this.signBuffer = null;
    this.pwdControl = null;
    this.keyring = require('../keyring');
    this.mailbuild = require('../../mailbuild');
    this.pgpMIME = false;
  }

  EditorController.prototype = Object.create(sub.SubController.prototype);

  EditorController.prototype.handlePortMessage = function(msg) {
    var that = this;
    //console.log('EditorController.handlePortMessage', msg);
    switch (msg.event) {
      case 'editor-init':
        this.ports.editor.postMessage({event: 'set-text', text: this.initText});
        break;
      case 'editor-cancel':
        this.editorPopup.close();
        this.editorPopup = null;
        break;
      case 'editor-transfer-output':
        this.editorPopup.close();
        this.editorPopup = null;
        this.encryptCallback(null, msg.data);
        break;
      case 'encrypt-dialog-init':
        // send content
        this.mvelo.data.load('common/ui/inline/dialogs/templates/encrypt.html', function(content) {
          //console.log('content rendered', content);
          that.ports.eDialog.postMessage({event: 'encrypt-dialog-content', data: content});
          // get potential recipients
          that.getRecipients(function(result) {
            that.ports.eDialog.postMessage({event: 'public-key-userids', keys: result.keys, primary: result.primary});
          });
        });
        break;
      case 'sign-dialog-init':
        var localKeyring = this.keyring.getById(this.mvelo.LOCAL_KEYRING_ID);
        var keys = localKeyring.getPrivateKeys();
        var primary = localKeyring.getAttributes().primary_key;
        this.mvelo.data.load('common/ui/inline/dialogs/templates/sign.html', function(content) {
          var port = that.ports.sDialog;
          port.postMessage({event: 'sign-dialog-content', data: content});
          port.postMessage({event: 'signing-key-userids', keys: keys, primary: primary});
        });
        break;
      case 'encrypt-dialog-cancel':
      case 'sign-dialog-cancel':
        // forward event to encrypt frame
        this.ports.editor.postMessage(msg);
        break;
      case 'encrypt-dialog-ok':
        // add recipients to buffer
        this.keyidBuffer = msg.recipient;
        // get email text from eFrame
        this.ports.editor.postMessage({event: 'get-plaintext', action: 'encrypt'});
        break;
      case 'editor-container-encrypt':
        this.pgpMIME = true;
        this.keyringId = msg.keyringId;
        var keyIdMap = this.keyring.getById(this.keyringId).getKeyIdByAddress(msg.recipients, {validity: true});
        if (Object.keys(keyIdMap).some(function(keyId) {
          return keyIdMap[keyId] === false;
        })) {
          var error = new Error('No valid encryption key for recipient address');
          error.code = 'NO_KEY_FOR_RECIPIENT';
          this.ports.editorCont.postMessage({event: 'error-message', error: error});
          return;
        }
        var keyIds = [];
        msg.recipients.forEach(function(recipient) {
          keyIds = keyIds.concat(keyIdMap[recipient]);
        });
        var primary = this.prefs.data().general.auto_add_primary &&
                      this.keyring.getById(this.keyringId).getAttributes().primary_key.toLowerCase();
        if (primary) {
          keyIds.push(primary);
        }
        this.keyidBuffer = this.mvelo.util.sortAndDeDup(keyIds);
        this.ports.editor.postMessage({event: 'get-plaintext', action: 'encrypt'});
        break;
      case 'sign-dialog-ok':
        this.signBuffer = {};
        var cacheEntry = this.pwdCache.get(msg.signKeyId, msg.signKeyId);
        if (cacheEntry && cacheEntry.key) {
          this.signBuffer.key = cacheEntry.key;
          this.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
        } else {
          var key = this.keyring.getById(this.mvelo.LOCAL_KEYRING_ID).getKeyForSigning(msg.signKeyId);
          // add key in buffer
          this.signBuffer.key = key.signKey;
          this.signBuffer.keyid = msg.signKeyId;
          this.signBuffer.userid = key.userId;
          if (cacheEntry) {
            this.pwdCache.unlock(cacheEntry, this.signBuffer, function() {
              that.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
            });
          } else {
            // open password dialog
            this.pwdControl = sub.factory.get('pwdDialog');
            this.pwdControl.unlockKey({
              message: this.signBuffer,
              openPopup: false
            }, function(err) {
              if (err === 'pwd-dialog-cancel') {
                that.ports.editor.postMessage({event: 'hide-pwd-dialog'});
                return;
              }
              if (err) {
                // TODO: propagate error to sign dialog
              }
              // success
              that.ports.editor.postMessage({event: 'get-plaintext', action: 'sign'});
            });
            this.ports.editor.postMessage({event: 'show-pwd-dialog', id: this.pwdControl.id});
          }
        }
        break;
      case 'editor-plaintext':
        if (msg.action === 'encrypt') {
          var data = this.buildMail(msg.message, msg.attachments);
          this.model.encryptMessage(data, this.keyringId, this.keyidBuffer, function(err, msg) {
            var port = that.ports.editorCont || that.ports.editor;
            port.postMessage({event: 'encrypted-message', message: msg});
          });
        } else if (msg.action === 'sign') {
          this.model.signMessage(msg.message, this.signBuffer.key, function(err, msg) {
            that.ports.editor.postMessage({event: 'signed-message', message: msg});
          });
        } else {
          throw new Error('Unknown eframe action:', msg.action);
        }
        break;
      default:
        console.log('unknown event', msg);
    }
  };

  EditorController.prototype.encrypt = function(options, callback) {
    var that = this;
    this.initText = options.initText;
    this.getRecipients = options.getRecipients;
    this.keyringId = options.keyringId || this.mvelo.LOCAL_KEYRING_ID;
    this.encryptCallback = callback;
    this.mvelo.windows.openPopup('common/ui/editor/editor.html?id=' + this.id + '&editor_type=' + this.prefs.data().general.editor_type, {width: 820, height: 450, modal: false}, function(window) {
      that.editorPopup = window;
    });
  };

  EditorController.prototype.buildMail = function(message, attachments) {
    //var t0 = Date.now();
    var mainMessage = new this.mailbuild("multipart/mixed");
    var composedMessage;
    var hasAttachment;
    if (message) {
      var textMime = new this.mailbuild("text/plain")
        .setHeader("Content-Type", "text/plain; charset=utf-8")
        .addHeader("Content-Transfer-Encoding", "quoted-printable")
        .setContent(message);
      mainMessage.appendChild(textMime);
    }
    if (attachments && Object.keys(attachments).length > 0) {
      hasAttachment = true;
      for (var attachment in attachments) {
        var attachmentMime = new this.mailbuild("text/plain")
          .createChild(false, {filename: attachments[attachment].filename})
          //.setHeader("Content-Type", msg.attachments[attachment].type+"; charset=utf-8")
          .addHeader("Content-Transfer-Encoding", "base64")
          .addHeader("Content-Disposition", "attachment") // ; filename="+msg.attachments[attachment].filename
          .setContent(attachments[attachment].content);
        mainMessage.appendChild(attachmentMime);
      }
    }
    if (hasAttachment || this.pgpMIME) {
      composedMessage = mainMessage.build();
    } else {
      composedMessage = message;
    }
    //var t1 = Date.now();
    //console.log("Building mime message took " + (t1 - t0) + " milliseconds. Current time: " + t1);
    return composedMessage;
  };

  exports.EditorController = EditorController;

});
<|endoftext|>