<commit_before>(function () {
    'use strict';

    function addDieRollResults(response) {
        var DIE_SIZE = 40,
            MARGIN = 20,
            $canvas,
            dicePerRow;

        $canvas = $('#dieRollResults');

        $canvas.clearCanvas();

        dicePerRow = Math.floor(($canvas.width() - 2 * MARGIN) / (DIE_SIZE + MARGIN));
        response.dieRollResults.forEach(function (dieRollResult, index) {
            var column,
                i,
                j,
                props,
                row,
                vertices,
                x,
                y;

            column = index % dicePerRow;
            x = MARGIN + column * (DIE_SIZE + MARGIN) + DIE_SIZE / 2;
            row = Math.floor(index / dicePerRow);
            y = MARGIN + row * (DIE_SIZE + MARGIN) + DIE_SIZE / 2;

            if (dieRollResult.sides === 4) {
                $canvas.drawPolygon({
                    radius: DIE_SIZE / 2,
                    rounded: true,
                    sides: 3,
                    strokeStyle: 'black',
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 6) {
                $canvas.drawRect({
                    height: DIE_SIZE,
                    rounded: true,
                    strokeStyle: 'black',
                    width: DIE_SIZE,
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 8) {
                vertices = [
                    { x:  0.0000000, y: -0.8164966 },
                    { x:  0.7071068, y: -0.4082483 },
                    { x:  0.7071068, y:  0.4082483 },
                    { x:  0.0000000, y:  0.8164966 },
                    { x: -0.7071068, y:  0.4082483 },
                    { x: -0.7071068, y: -0.4082483 }
                ];
                vertices.forEach(function (vertex) {
                    vertex.x *= DIE_SIZE / 1.5;
                    vertex.y *= DIE_SIZE / 1.5;
                });

                // draw line through vertices 1-5 (outer edge)
                props = {
                    closed: true,
                    rounded: true,
                    strokeStyle: 'black'
                };
                j = 1;
                for (i = 0; i < 6; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw lines between the following vertices (to complete
                // rendering the edges of the other 3 facets):
                //
                //    1 -> 3; 1 -> 5; 3 -> 5
                props = {
                    strokeStyle: 'black'
                };
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[2].x, y2: y + vertices[2].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[4].x, y2: y + vertices[4].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[2].x, y1: y + vertices[2].y,
                    x2: x + vertices[4].x, y2: y + vertices[4].y
                }));

                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 12) {
                vertices = [
                    { x:  0.000000, y: -1.0922415 },
                    { x:  1.000000, y: -0.3660254 },
                    { x:  0.618034, y:  0.8090170 },
                    { x: -0.618034, y:  0.8090170 },
                    { x: -1.000000, y: -0.3660254 },
                    { x:  0.000000, y: -1.7102755 },
                    { x:  1.000000, y: -1.3660254 },
                    { x:  1.618034, y: -0.5352331 },
                    { x:  1.618034, y:  0.5352331 },
                    { x:  1.000000, y:  1.3660254 },
                    { x:  0.000000, y:  1.7102755 },
                    { x: -1.000000, y:  1.3660254 },
                    { x: -1.618034, y:  0.5352331 },
                    { x: -1.618034, y: -0.5352331 },
                    { x: -1.000000, y: -1.3660254 }
                ];
                vertices.forEach(function (vertex) {
                    vertex.x *= DIE_SIZE / 3;
                    vertex.y *= DIE_SIZE / 3;
                });

                // draw line through vertices 1-5 (edges of top facet)
                props = {
                    closed: true,
                    rounded: true,
                    strokeStyle: 'black'
                };
                j = 1;
                for (i = 0; i < 5; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw line through vertices 6-15 (outer edge)
                j = 1;
                for (i = 5; i < 15; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw lines between the following vertices (to complete
                // rendering the edges of the other 5 facets):
                //
                //    1 -> 6; 2 -> 8; 3 -> 10; 4 -> 12; 5 -> 14
                props = {
                    strokeStyle: 'black'
                };
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[5].x, y2: y + vertices[5].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[1].x, y1: y + vertices[1].y,
                    x2: x + vertices[7].x, y2: y + vertices[7].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[2].x, y1: y + vertices[2].y,
                    x2: x + vertices[9].x, y2: y + vertices[9].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[3].x, y1: y + vertices[3].y,
                    x2: x + vertices[11].x, y2: y + vertices[11].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[4].x, y1: y + vertices[4].y,
                    x2: x + vertices[13].x, y2: y + vertices[13].y
                }));

                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else {
                $canvas.drawEllipse({
                    height: DIE_SIZE,
                    strokeStyle: 'black',
                    width: DIE_SIZE,
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            }
        });
    }

    function addExpressionResult(response) {
        var $actionsCell,
            $expressionCanonicalTextCell,
            $expressionResultRow,
            $expressionResultTextCell,
            $expressionResultValueCell,
            $expressionTextCell,
            $reevaluateButton,
            $removeButton;

        $expressionTextCell = $('<td>').text(response.expression.text);
        $expressionCanonicalTextCell = $('<td>').text(response.expression.canonicalText);
        $expressionResultTextCell = $('<td>').text(response.expressionResult.text);
        $expressionResultValueCell = $('<td>').text(response.expressionResult.value.toString());

        $reevaluateButton = $('<button>').attr('name', 'reevaluate').text('Reevaluate').click(function () {
            evaluateExpression($expressionTextCell.text());
        });
        $removeButton = $('<button>').attr('name', 'remove').text('Remove').click(function (event) {
            $(event.target).closest('tr').remove();
        });
        $actionsCell = $('<td>').append($reevaluateButton, $removeButton);

        $expressionResultRow = $('<tr>').append(
            $expressionTextCell,
            $expressionCanonicalTextCell,
            $expressionResultTextCell,
            $expressionResultValueCell,
            $actionsCell
        );
        $('#expressionResults').prepend($expressionResultRow);
    }

    function clearExpressionText() {
        $('#expressionText').val('');
    }

    function evaluateExpression(expressionText) {
        var randomNumberGeneratorJson,
            requestBody;

        requestBody = {
            expression: {
                text: expressionText
            }
        };

        randomNumberGeneratorJson = $('#randomNumberGeneratorJson').val();
        if (randomNumberGeneratorJson) {
            requestBody.randomNumberGenerator = JSON.parse(randomNumberGeneratorJson);
        }

        $.postJSON('/expression/evaluate', requestBody, processResponse, processErrorResponse);
    }

    function getExpressionText() {
        var expressionText,
            roundingFunction;

        expressionText = $('#expressionText').val();
        roundingFunction = $('input[name="roundingMode"]:checked').val();
        if (roundingFunction) {
            expressionText = roundingFunction + '(' + expressionText + ')';
        }
        return expressionText;
    }

    function hideErrorMessage() {
        $('#errorMessage').invisible();
    }

    function hideHelp() {
        $('#help').hide();
    }

    function initialize() {
        hideHelp();
        hideErrorMessage();

        $('#expressionForm').submit(function (event) {
            evaluateExpression(getExpressionText());
            event.preventDefault();
        });
        $('#toggleHelp').click(toggleHelp);
        $('#removeAllResults').click(removeAllResults);
    }

    function installJQueryPlugins() {
        jQuery.fn.visible = function () {
            return this.css('visibility', 'visible');
        };

        jQuery.fn.invisible = function () {
            return this.css('visibility', 'hidden');
        };

        jQuery.extend({
            postJSON: function (url, data, successCallback, errorCallback) {
                return this.ajax({
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify(data),
                    dataType: 'json',
                    error: errorCallback,
                    success: successCallback,
                    type: 'POST',
                    url: url
                });
            }
        });
    }

    function processErrorResponse(jqxhr) {
        var responseBody = jqxhr.responseJSON;

        if (responseBody && responseBody.error) {
            showErrorMessage(responseBody.error.message);
        }
    }

    function processResponse(responseBody) {
        clearExpressionText();
        addDieRollResults(responseBody);
        addExpressionResult(responseBody);
        hideErrorMessage();
    }

    function removeAllResults() {
        $('#expressionResults').empty();
    }

    function showErrorMessage(message) {
        $('#errorMessage').text(message).visible();
    }

    function toggleHelp() {
        var $help,
            isHelpVisible,
            wasHelpVisible;

        $help = $('#help');
        wasHelpVisible = $help.is(':visible');
        $help.toggle(400);

        isHelpVisible = !wasHelpVisible;
        $('#toggleHelp').text(isHelpVisible ? 'hide help' : 'help');
    }

    function main() {
        installJQueryPlugins();
        initialize();
    }

    $(document).ready(main);
})();
<commit_msg>Improve graphical display of non-standard dice<commit_after>(function () {
    'use strict';

    function addDieRollResults(response) {
        var DIE_SIZE = 40,
            MARGIN = 20,
            $canvas,
            dicePerRow;

        $canvas = $('#dieRollResults');

        $canvas.clearCanvas();

        dicePerRow = Math.floor(($canvas.width() - 2 * MARGIN) / (DIE_SIZE + MARGIN));
        response.dieRollResults.forEach(function (dieRollResult, index) {
            var column,
                i,
                j,
                props,
                row,
                vertices,
                x,
                y;

            column = index % dicePerRow;
            x = MARGIN + column * (DIE_SIZE + MARGIN) + DIE_SIZE / 2;
            row = Math.floor(index / dicePerRow);
            y = MARGIN + row * (DIE_SIZE + MARGIN) + DIE_SIZE / 2;

            if (dieRollResult.sides === 4) {
                $canvas.drawPolygon({
                    radius: DIE_SIZE / 2,
                    rounded: true,
                    sides: 3,
                    strokeStyle: 'black',
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 6) {
                $canvas.drawRect({
                    height: DIE_SIZE,
                    rounded: true,
                    strokeStyle: 'black',
                    width: DIE_SIZE,
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 8) {
                vertices = [
                    { x:  0.0000000, y: -0.8164966 },
                    { x:  0.7071068, y: -0.4082483 },
                    { x:  0.7071068, y:  0.4082483 },
                    { x:  0.0000000, y:  0.8164966 },
                    { x: -0.7071068, y:  0.4082483 },
                    { x: -0.7071068, y: -0.4082483 }
                ];
                vertices.forEach(function (vertex) {
                    vertex.x *= DIE_SIZE / 1.5;
                    vertex.y *= DIE_SIZE / 1.5;
                });

                // draw line through vertices 1-5 (outer edge)
                props = {
                    closed: true,
                    rounded: true,
                    strokeStyle: 'black'
                };
                j = 1;
                for (i = 0; i < 6; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw lines between the following vertices (to complete
                // rendering the edges of the other 3 facets):
                //
                //    1 -> 3; 1 -> 5; 3 -> 5
                props = {
                    strokeStyle: 'black'
                };
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[2].x, y2: y + vertices[2].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[4].x, y2: y + vertices[4].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[2].x, y1: y + vertices[2].y,
                    x2: x + vertices[4].x, y2: y + vertices[4].y
                }));

                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else if (dieRollResult.sides === 12) {
                vertices = [
                    { x:  0.000000, y: -1.0922415 },
                    { x:  1.000000, y: -0.3660254 },
                    { x:  0.618034, y:  0.8090170 },
                    { x: -0.618034, y:  0.8090170 },
                    { x: -1.000000, y: -0.3660254 },
                    { x:  0.000000, y: -1.7102755 },
                    { x:  1.000000, y: -1.3660254 },
                    { x:  1.618034, y: -0.5352331 },
                    { x:  1.618034, y:  0.5352331 },
                    { x:  1.000000, y:  1.3660254 },
                    { x:  0.000000, y:  1.7102755 },
                    { x: -1.000000, y:  1.3660254 },
                    { x: -1.618034, y:  0.5352331 },
                    { x: -1.618034, y: -0.5352331 },
                    { x: -1.000000, y: -1.3660254 }
                ];
                vertices.forEach(function (vertex) {
                    vertex.x *= DIE_SIZE / 3;
                    vertex.y *= DIE_SIZE / 3;
                });

                // draw line through vertices 1-5 (edges of top facet)
                props = {
                    closed: true,
                    rounded: true,
                    strokeStyle: 'black'
                };
                j = 1;
                for (i = 0; i < 5; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw line through vertices 6-15 (outer edge)
                j = 1;
                for (i = 5; i < 15; i += 1) {
                    props['x' + j] = x + vertices[i].x;
                    props['y' + j] = y + vertices[i].y;
                    j += 1;
                }
                $canvas.drawLine(props);

                // draw lines between the following vertices (to complete
                // rendering the edges of the other 5 facets):
                //
                //    1 -> 6; 2 -> 8; 3 -> 10; 4 -> 12; 5 -> 14
                props = {
                    strokeStyle: 'black'
                };
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[0].x, y1: y + vertices[0].y,
                    x2: x + vertices[5].x, y2: y + vertices[5].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[1].x, y1: y + vertices[1].y,
                    x2: x + vertices[7].x, y2: y + vertices[7].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[2].x, y1: y + vertices[2].y,
                    x2: x + vertices[9].x, y2: y + vertices[9].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[3].x, y1: y + vertices[3].y,
                    x2: x + vertices[11].x, y2: y + vertices[11].y
                }));
                $canvas.drawLine($.extend(props, {
                    x1: x + vertices[4].x, y1: y + vertices[4].y,
                    x2: x + vertices[13].x, y2: y + vertices[13].y
                }));

                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            } else {
                $canvas.drawEllipse({
                    height: DIE_SIZE,
                    strokeStyle: 'black',
                    width: DIE_SIZE,
                    x: x,
                    y: y
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '0.5em',
                    text: dieRollResult.sides.toString(),
                    x: x + DIE_SIZE / 2,
                    y: y + DIE_SIZE / 2
                });
                $canvas.drawText({
                    fillStyle: 'black',
                    fontSize: '1em',
                    text: dieRollResult.value.toString(),
                    x: x,
                    y: y
                });
            }
        });
    }

    function addExpressionResult(response) {
        var $actionsCell,
            $expressionCanonicalTextCell,
            $expressionResultRow,
            $expressionResultTextCell,
            $expressionResultValueCell,
            $expressionTextCell,
            $reevaluateButton,
            $removeButton;

        $expressionTextCell = $('<td>').text(response.expression.text);
        $expressionCanonicalTextCell = $('<td>').text(response.expression.canonicalText);
        $expressionResultTextCell = $('<td>').text(response.expressionResult.text);
        $expressionResultValueCell = $('<td>').text(response.expressionResult.value.toString());

        $reevaluateButton = $('<button>').attr('name', 'reevaluate').text('Reevaluate').click(function () {
            evaluateExpression($expressionTextCell.text());
        });
        $removeButton = $('<button>').attr('name', 'remove').text('Remove').click(function (event) {
            $(event.target).closest('tr').remove();
        });
        $actionsCell = $('<td>').append($reevaluateButton, $removeButton);

        $expressionResultRow = $('<tr>').append(
            $expressionTextCell,
            $expressionCanonicalTextCell,
            $expressionResultTextCell,
            $expressionResultValueCell,
            $actionsCell
        );
        $('#expressionResults').prepend($expressionResultRow);
    }

    function clearExpressionText() {
        $('#expressionText').val('');
    }

    function evaluateExpression(expressionText) {
        var randomNumberGeneratorJson,
            requestBody;

        requestBody = {
            expression: {
                text: expressionText
            }
        };

        randomNumberGeneratorJson = $('#randomNumberGeneratorJson').val();
        if (randomNumberGeneratorJson) {
            requestBody.randomNumberGenerator = JSON.parse(randomNumberGeneratorJson);
        }

        $.postJSON('/expression/evaluate', requestBody, processResponse, processErrorResponse);
    }

    function getExpressionText() {
        var expressionText,
            roundingFunction;

        expressionText = $('#expressionText').val();
        roundingFunction = $('input[name="roundingMode"]:checked').val();
        if (roundingFunction) {
            expressionText = roundingFunction + '(' + expressionText + ')';
        }
        return expressionText;
    }

    function hideErrorMessage() {
        $('#errorMessage').invisible();
    }

    function hideHelp() {
        $('#help').hide();
    }

    function initialize() {
        hideHelp();
        hideErrorMessage();

        $('#expressionForm').submit(function (event) {
            evaluateExpression(getExpressionText());
            event.preventDefault();
        });
        $('#toggleHelp').click(toggleHelp);
        $('#removeAllResults').click(removeAllResults);
    }

    function installJQueryPlugins() {
        jQuery.fn.visible = function () {
            return this.css('visibility', 'visible');
        };

        jQuery.fn.invisible = function () {
            return this.css('visibility', 'hidden');
        };

        jQuery.extend({
            postJSON: function (url, data, successCallback, errorCallback) {
                return this.ajax({
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify(data),
                    dataType: 'json',
                    error: errorCallback,
                    success: successCallback,
                    type: 'POST',
                    url: url
                });
            }
        });
    }

    function processErrorResponse(jqxhr) {
        var responseBody = jqxhr.responseJSON;

        if (responseBody && responseBody.error) {
            showErrorMessage(responseBody.error.message);
        }
    }

    function processResponse(responseBody) {
        clearExpressionText();
        addDieRollResults(responseBody);
        addExpressionResult(responseBody);
        hideErrorMessage();
    }

    function removeAllResults() {
        $('#expressionResults').empty();
    }

    function showErrorMessage(message) {
        $('#errorMessage').text(message).visible();
    }

    function toggleHelp() {
        var $help,
            isHelpVisible,
            wasHelpVisible;

        $help = $('#help');
        wasHelpVisible = $help.is(':visible');
        $help.toggle(400);

        isHelpVisible = !wasHelpVisible;
        $('#toggleHelp').text(isHelpVisible ? 'hide help' : 'help');
    }

    function main() {
        installJQueryPlugins();
        initialize();
    }

    $(document).ready(main);
})();
<|endoftext|>


<commit_before>/* ===========================================================
 * pfShare v 1.0
 * ===========================================================
 *
 * License: MIT
 *
 * Copyright 2015 Pavel Fomin.
 * ========================================================== */
(function( $ ){

    $.fn.pfShare = function(options) {

        options = $.extend( {//Default settigs and privat methods
            url: getDefault.url(),
            title: getDefault.title(),
            description: getDefault.descr(),
            image: getDefault.image(),
            customClasses : [],
            socials : ['vk','fb','tw','gl']
        }, options);

        pluginTarget = $(this);
        socialInWork = [];

        init(options);
    };

    var init = function (options){
        var options = options;
        var targetClass = "." + pluginTarget[0].className;

        initSocials(options);

        if(socialInWork.length > 0){
            makeView(options);
            setHandlers(options);
        } else {
            console.error("No socials selected!");
        }
    }

   var getDefault = {
        url : function(){
            var output = window.location.protocol + "//" + window.location.hostname + window.location.pathname;
            return output
        },
        title : function(){
            var output = encodeURIComponent($("meta[name=title]").attr("content")) || "Title is not set!";
            return output;
        },

        descr : function(){
            var output = encodeURIComponent($("meta[name='description']").attr("content")) || "Description is not set"
            return output;
        },
        image : function(){
            var output = $("img").first().attr("src");
            return output;
        }
    }

    var initSocials = function(options){
        options.socials.forEach(function(item, i){
            if(shareMethods[item]){
                socialInWork[i] = item;
            }
        });
    }

    var makeView = function (options){
        pluginTarget.each(function( i ){
            var shareForm = '
';
            var socials = socialInWork;
            var customClasses = options.customClasses.join(" ");
            $(this).wrap('

');
                socials.forEach(function(social){
                    shareForm += '';
                })
                shareForm += ''
                shareForm += '
';
            $(this).closest(".share-wrapper").append( shareForm );
        });
    }

    var setHandlers = function(options){
        $(".button-share").on("click",function(){
            var type = $(this).data("type");
            shareMethods[type].call(this,options);
        });
    };

    var getParams = function(self,def){
      var param = {};
      param.url = self.closest(targetClass).data("url") || def.url;
      param.title = self.closest(targetClass).data("title") || def.title;
      param.description = self.closest(targetClass).data("descr") || def.description;
      param.image = self.closest(targetClass).data("image") || window.location.origin + self.closest(".share-wrapper").find("img").attr("src") || def.image;
      param.image_id = getImageId(self);
      return param;
    };

    var getImageId = function( self ){
        imageId = self.closest(".share-wrapper").data("id");
        return imageId;
    }

    var getHash = function(img_id){
        var hash = "image" + img_id;
        return hash;
    }

    var shareMethods = {
         vk : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://vk.com/share.php?";
                send += "url=" + params.url + "?image=" + params.image + "%23" + hash;
                send += "&title=" + params.title;
                send += "&description=" + params.description;
                send += "&image=" + params.image;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

         fb : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://facebook.com/share.php?s=100";
                send += "&p[url]=" + params.url + "?" + "image=" + params.image  + "%23" + hash;
                send += "&p[title]=" + params.title;
                send += "&p[summary]=" + params.description;
                send += "&p[images][0]=" + params.image;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

         tw : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://twitter.com/share?";
                    send += "url=" + params.url + "?" + "image=" + params.image  + "%23" +hash;
                    send += "&text=" + params.title;
                    send += "&counturl=" + params.url;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

        gl : function( options ) {
            var self = $(this);
            var def = options;
            var params = getParams(self,def);
            var hash = getHash(params.image_id);

            var send = "https://plus.google.com/share?hl=ru";
            send += "&url=" + params.url + "?" + "image=" + params.image + "%23" +hash;
            window.open(send, "", "toolbar=0,status=0,width=626,height=436");
            return this;
            }
    };
})( jQuery );
<commit_msg>removed global var<commit_after>/* ===========================================================
 * pfShare v 1.0
 * ===========================================================
 *
 * License: MIT
 *
 * Copyright 2015 Pavel Fomin.
 * ========================================================== */
(function( $ ){

    $.fn.pfShare = function(options) {

       var options = $.extend( {//Default settigs and privat methods
            url: getDefault.url(),
            title: getDefault.title(),
            description: getDefault.descr(),
            image: getDefault.image(),
            customClasses : [],
            socials : ['vk','fb','tw','gl']
        }, options);

       options.pluginTarget = $(this);
       options.socialInWork = [];

        init(options);
    };

    var init = function (options){

        initSocials(options);

        if(options.socialInWork.length > 0){
            makeView(options);
            setHandlers(options);
        } else {
            console.error("No socials selected!");
        }
    };

   var getDefault = {
        url : function(){
            var output = window.location.protocol + "//" + window.location.hostname + window.location.pathname;
            return output
        },
        title : function(){
            var output = encodeURIComponent($("meta[name=title]").attr("content")) || "Title is not set!";
            return output;
        },

        descr : function(){
            var output = encodeURIComponent($("meta[name='description']").attr("content")) || "Description is not set"
            return output;
        },
        image : function(){
            var output = $("img").first().attr("src");
            return output;
        }
    };

    var initSocials = function(options){
        options.socials.forEach(function(item, i){
            if(shareMethods[item]){
                options.socialInWork[i] = item;
            };
        });
    };

    var makeView = function (options){
        options.pluginTarget.each(function( i ){
            var shareForm = '
';
            var socials = options.socialInWork;
            var customClasses = options.customClasses.join(" ");
            $(this).wrap('

');
                socials.forEach(function(social){
                    shareForm += '';
                });
                shareForm += ''
                shareForm += '
';
            $(this).closest(".share-wrapper").append( shareForm );
        });
    };

    var setHandlers = function(options){
        $(".button-share").on("click",function(){
            var type = $(this).data("type");
            shareMethods[type].call(this,options);
        });
    };

    var getParams = function(self,def){
      var param = {};
      var targetClass = "." + def.pluginTarget[0].className;

      param.url = self.closest(targetClass).data("url") || def.url;
      param.title = self.closest(targetClass).data("title") || def.title;
      param.description = self.closest(targetClass).data("descr") || def.description;
      param.image = self.closest(targetClass).data("image") || window.location.origin + self.closest(".share-wrapper").find("img").attr("src") || def.image;
      param.image_id = getImageId(self);
      return param;
    };

    var getImageId = function( self ){
        imageId = self.closest(".share-wrapper").data("id");
        return imageId;
    }

    var getHash = function(img_id){
        var hash = "image" + img_id;
        return hash;
    }

    var shareMethods = {
         vk : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://vk.com/share.php?";
                send += "url=" + params.url + "?image=" + params.image + "%23" + hash;
                send += "&title=" + params.title;
                send += "&description=" + params.description;
                send += "&image=" + params.image;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

         fb : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://facebook.com/share.php?s=100";
                send += "&p[url]=" + params.url + "?" + "image=" + params.image  + "%23" + hash;
                send += "&p[title]=" + params.title;
                send += "&p[summary]=" + params.description;
                send += "&p[images][0]=" + params.image;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

         tw : function( options ) {
                var self = $(this);
                var def = options;
                var params = getParams(self,def);
                var hash = getHash(params.image_id);

                var send = "http://twitter.com/share?";
                    send += "url=" + params.url + "?" + "image=" + params.image  + "%23" +hash;
                    send += "&text=" + params.title;
                    send += "&counturl=" + params.url;
                window.open(send, "", "toolbar=0,status=0,width=626,height=436");
                return this;
            },

        gl : function( options ) {
            var self = $(this);
            var def = options;
            var params = getParams(self,def);
            var hash = getHash(params.image_id);

            var send = "https://plus.google.com/share?hl=ru";
            send += "&url=" + params.url + "?" + "image=" + params.image + "%23" +hash;
            window.open(send, "", "toolbar=0,status=0,width=626,height=436");
            return this;
            }
    };
})( jQuery );
<|endoftext|>


<commit_before>require.config({
    paths: {
        'domReady': '../bower_components/requirejs-domready/domReady',
        'angular': '../bower_components/angular/angular',
        'angular-route': '../bower_components/angular-route/angular-route',
        'angular-animate': '../bower_components/angular-animate/angular-animate',
        'snap': '../bower_components/Snap.svg/dist/snap.svg',
        'jquery': '../bower_components/jquery/jquery'
    },
    shim: {
        'angular': {
            exports: 'angular'
        },
        'angular-route': {
            deps: ['angular']
        },
        'angular-animate': {
            deps: ['angular']
        }
    },
    deps: ['./bootstrap']
});<commit_msg>Removed explicit dependency on jQuery.<commit_after>require.config({
    paths: {
        'domReady': '../bower_components/requirejs-domready/domReady',
        'angular': '../bower_components/angular/angular',
        'angular-route': '../bower_components/angular-route/angular-route',
        'angular-animate': '../bower_components/angular-animate/angular-animate',
        'snap': '../bower_components/Snap.svg/dist/snap.svg'
    },
    shim: {
        'angular': {
            exports: 'angular'
        },
        'angular-route': {
            deps: ['angular']
        },
        'angular-animate': {
            deps: ['angular']
        }
    },
    deps: ['./bootstrap']
});<|endoftext|>


<commit_before>var socket = io.connect('http://ec2-54-200-40-68.us-west-2.compute.amazonaws.com:8080');

// on connection to server, ask for user's name with an anonymous callback
socket.on('connect', function(){
	socket.emit('join');
});

socket.on('match', function (room) {
	$("#leavejoin").html('leave');
	$('#conversation').html("");
	$('#conversation').append('Found a friend!
');
	roomJoiner(room);
});
function roomJoiner(room) {
	console.log("Attempting to join: " + room);
	if(roomFlag) {
		webrtc.joinRoom(room);
	} else {
		console.log("Not yet ready to join");
		setTimeout(function() { roomJoiner(room); }, 1000);
	}
}
// listener, whenever the server emits 'updatechat', this updates the chat body
socket.on('updatechat', function (flag, data) {
	var sender = "Partner"
	if(flag) sender = "You"
	$('#conversation').append(''+ sender + ': ' + data + '
');
});

socket.on('notify', function (data) {
	$('#conversation').append('' + data + '
');
});

socket.on('rejoin', function () {
	webrtc.leaveRoom(webrtc.roomName);
	$('#conversation').append('Partner has left!
');
	socket.emit('join');
});

socket.on('webchat', function(data) {
	webrtc.join(data);
});

// on load of page
$(function(){
	// when the client clicks SEND
	$('#datasend').click( function() {
		var message = $('#data').val();
		$('#data').val('');
		// tell server to execute 'sendchat' and send along one parameter
		socket.emit('sendchat', message);
	});

	//when the client clicks leave
	$('#leavejoin').click( function() {

		$('#conversation').append('Left the room!
');
		if($(this).attr("value") == "leave") {
			socket.emit('leave');
			webrtc.leaveRoom(webrtc.room);
			$("#leavejoin").html('join');
		} else {
			socket.emit('join');
			$("#leavejoin").html('joining');
		}
	});

	// when the client hits ENTER on their keyboard
	$('#data').keypress(function(e) {
		if(e.which == 13) {
			$(this).blur();
			$('#datasend').focus().click();
		}
	});
});
<commit_msg>adding leave + ui fixes<commit_after>var socket = io.connect('http://ec2-54-200-40-68.us-west-2.compute.amazonaws.com:8080');

// on connection to server, ask for user's name with an anonymous callback
socket.on('connect', function(){
	socket.emit('join');
});

socket.on('match', function (room) {
	$("#leavejoin").html('leave');
	$('#conversation').html("");
	$('#conversation').append('Found a friend!
');
	roomJoiner(room);
});
function roomJoiner(room) {
	console.log("Attempting to join: " + room);
	if(roomFlag) {
		webrtc.joinRoom(room);
	} else {
		console.log("Not yet ready to join");
		setTimeout(function() { roomJoiner(room); }, 1000);
	}
}
// listener, whenever the server emits 'updatechat', this updates the chat body
socket.on('updatechat', function (flag, data) {
	var sender = "Partner"
	if(flag) sender = "You"
	$('#conversation').append(''+ sender + ': ' + data + '
');
});

socket.on('notify', function (data) {
	$('#conversation').append('' + data + '
');
});

socket.on('rejoin', function () {
	webrtc.leaveRoom(webrtc.roomName);
	$('#conversation').append('Partner has left!
');
	socket.emit('join');
	$("#leavejoin").attr('value', 'joining');
});

socket.on('webchat', function(data) {
	webrtc.join(data);
});

// on load of page
$(function(){
	// when the client clicks SEND
	$('#datasend').click( function() {
		var message = $('#data').val();
		$('#data').val('');
		// tell server to execute 'sendchat' and send along one parameter
		socket.emit('sendchat', message);
	});

	//when the client clicks leave
	$('#leavejoin').click( function() {

		$('#conversation').append('Left the room!
');
		if($(this).attr("value") == "leave") {
			socket.emit('leave');
			webrtc.leaveRoom(webrtc.room);
			$(this).attr('value', 'join');
		} else ($(this).attr("value") == "join"){
			socket.emit('join');
			$(this).attr('value', 'joining');
		}
	});

	// when the client hits ENTER on their keyboard
	$('#data').keypress(function(e) {
		if(e.which == 13) {
			$(this).blur();
			$('#datasend').focus().click();
		}
	});
});
<|endoftext|>


<commit_before>// +----------------------------------------------------------------------
// | ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2019 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: http://demo.thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitee 代码仓库：https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库：https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------

$(function () {

    window.$body = $('body');

    /*! 后台加密登录处理 */
    $body.find('[data-login-form]').map(function (that) {
        that = this;
        require(["md5"], function (md5) {
            $("form").vali(function (data) {
                data['password'] = md5.hash(md5.hash(data['password']) + data['uniqid']);
                $.form.load(location.href, data, "post", function (ret) {
                    if (parseInt(ret.code) !== 1) {
                        $(that).find('.verify.layui-hide').removeClass('layui-hide');
                        $(that).find('[data-captcha]').trigger('click');
                    }
                }, null, null, 'false');
            });
        });
    });

    /*! 登录图形验证码刷新 */
    $body.on('click', '[data-captcha]', function () {
        var type, token, $that, verifyField, uniqidField, captchaUrl;
        $that = $(this), captchaUrl = this.getAttribute('data-captcha') || '';
        if (captchaUrl.length < 5) return $.msg.tips('请设置验证码请求地址');
        uniqidField = this.getAttribute('data-field-uniqid') || 'uniqid';
        verifyField = this.getAttribute('data-field-verify') || 'verify';
        type = this.getAttribute('data-captcha-type') || 'captcha-type';
        token = this.getAttribute('data-captcha-token') || 'captcha-token';
        $.form.load(captchaUrl, {type: type, token: token}, 'post', function (ret) {
            $that.html('');
            $that.append($('').attr('src', ret.data.image));
            $that.append($('<input type="hidden">').attr('name', uniqidField || 'uniqid').val(ret.data.uniqid));
            if (ret.data.code) {
                $that.parents('form').find('[name=' + (verifyField || 'verify') + ']').attr('value', ret.data.code);
            } else {
                $that.parents('form').find('[name=' + (verifyField || 'verify') + ']').attr('value', '');
            }
            return false;
        }, false);
    });

    $('[data-captcha]').map(function () {
        $(this).trigger('click')
    });

});<commit_msg>Update login.js<commit_after>// +----------------------------------------------------------------------
// | ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2019 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: http://demo.thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitee 代码仓库：https://gitee.com/zoujingli/ThinkAdmin
// | github 代码仓库：https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------

$(function () {

    window.$body = $('body');

    /*! 后台加密登录处理 */
    $body.find('[data-login-form]').map(function (that) {
        that = this;
        require(["md5"], function (md5) {
            $("form").vali(function (data) {
                data['password'] = md5.hash(md5.hash(data['password']) + data['uniqid']);
                $.form.load(location.href, data, "post", function (ret) {
                    if (parseInt(ret.code) !== 1) {
                        $(that).find('.verify.layui-hide').removeClass('layui-hide');
                        $(that).find('[data-captcha]').trigger('click');
                    }
                }, null, null, 'false');
            });
        });
    });

    /*! 登录图形验证码刷新 */
    $body.on('click', '[data-captcha]', function () {
        var type, token, $that, verify, uniqid, captchaUrl;
        $that = $(this), captchaUrl = this.getAttribute('data-captcha') || '';
        if (captchaUrl.length < 5) return $.msg.tips('请设置验证码请求地址');
        uniqid = this.getAttribute('data-field-uniqid') || 'uniqid';
        verify = this.getAttribute('data-field-verify') || 'verify';
        type = this.getAttribute('data-captcha-type') || 'captcha-type';
        token = this.getAttribute('data-captcha-token') || 'captcha-token';
        $.form.load(captchaUrl, {type: type, token: token}, 'post', function (ret) {
            if (ret.code) {
                $that.html('');
                $that.append($('').attr('src', ret.data.image));
                $that.append($('<input type="hidden">').attr('name', uniqid).val(ret.data.uniqid));
                if (ret.data.code) {
                    $that.parents('form').find('[name=' + verify + ']').attr('value', ret.data.code);
                } else {
                    $that.parents('form').find('[name=' + verify + ']').attr('value', '');
                }
                return false;
            }
        }, false);
    });

    $('[data-captcha]').map(function () {
        $(this).trigger('click')
    });

});<|endoftext|>


<commit_before>'use strict';

angular.module('classBrowserUHApp').directive('pageSelect', function () {
    return {
        restrict: 'E',
        template: '<input type="text" class="select-page" ng-model="inputPage" ng-change="selectPage(inputPage)">',
        link: function (scope, element, attrs) {
            scope.$watch('currentPage', function (c) {
                scope.inputPage = c;
            });
        }
    }
}).directive('navBar', function () {
    return {
        restrict: 'E',
        templateUrl: 'views/navbar/navbar.html',
        controller: 'NavBarCtrl'
    };
}).directive('loadingIcon', function () {
    return {
        restrict: 'E',
        replace: true,
        template: '<img style="display: block; margin: 0 auto;" ng-if="isDataLoading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="/>'
    };
}).directive('errorMessageBox', function () {
    return {
        restrict: 'E',
        replace: true,
        template: '

Error! {{errorMessage}}

'
    };
}).directive('warningMessageBox', function () {
    return {
        restrict: 'E',
        template: '

Warning!

{{warningMessage}}

'
    };
}).directive('courseNumberValidator', function () {
    return {
        scope: {
            validValues: '=validValues'
        },
        link: function (scope, elm, attrs) {
            elm.bind('keypress', function (e) {
                var char = String.fromCharCode(e.which || e.charCode || e.keyCode), matches = [];
                angular.forEach(scope.validValues, function (value, key) {
                    if (char === value) matches.push(char);
                }, matches);
                if (matches.length == 0) {
                    e.preventDefault();
                    return false;
                }
            });
        }
    }
});<commit_msg>Removed loadingIcon directive.<commit_after>'use strict';

angular.module('classBrowserUHApp').directive('pageSelect', function () {
    return {
        restrict: 'E',
        template: '<input type="text" class="select-page" ng-model="inputPage" ng-change="selectPage(inputPage)">',
        link: function (scope, element, attrs) {
            scope.$watch('currentPage', function (c) {
                scope.inputPage = c;
            });
        }
    }
}).directive('navBar', function () {
    return {
        restrict: 'E',
        templateUrl: 'views/navbar/navbar.html',
        controller: 'NavBarCtrl'
    };
}).directive('errorMessageBox', function () {
    return {
        restrict: 'E',
        replace: true,
        template: '

Error! {{errorMessage}}

'
    };
}).directive('warningMessageBox', function () {
    return {
        restrict: 'E',
        template: '

Warning!

{{warningMessage}}

'
    };
}).directive('courseNumberValidator', function () {
    return {
        scope: {
            validValues: '=validValues'
        },
        link: function (scope, elm, attrs) {
            elm.bind('keypress', function (e) {
                var char = String.fromCharCode(e.which || e.charCode || e.keyCode), matches = [];
                angular.forEach(scope.validValues, function (value, key) {
                    if (char === value) matches.push(char);
                }, matches);
                if (matches.length == 0) {
                    e.preventDefault();
                    return false;
                }
            });
        }
    }
});<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react';
import { PropTypes } from 'prop-types';

import { Link } from 'react-router-dom';
import MediaQuery from 'react-responsive';
import _ from 'lodash';
import gql from 'graphql-tag';
import queryString from 'query-string';

import PageLayout from '../components/PageLayout';
import Loading from '../components/Loading';
import NearestLooMap from '../components/NearestLooMap';
import DismissableBox from '../components/DismissableBox';
import Notification from '../components/Notification';
import getGeolocation from '../getGeolocation';

import config from '../config';
import graphqlMappings from '../graphqlMappings';

import styles from './css/edit-loo-page.module.css';
import layout from '../components/css/layout.module.css';
import helpers from '../css/helpers.module.css';
import headings from '../css/headings.module.css';
import controls from '../css/controls.module.css';

import { useQuery, useMutation } from '@apollo/client';
import { loader } from 'graphql.macro';
import history from '../history';

const FIND_BY_ID = loader('./findLooById.graphql');
const UPDATE_LOO = loader('./updateLoo.graphql');

const SET_MAP_CENTER = gql`
  mutation SetMapCenter($lat: Number!, $lng: Number!) {
    updateCenter(lat: $lat, lng: $lng) @client
  }
`;

const getLooCachedId = (looId) => 'Loo:' + looId;

const AddEditPage = function (props) {
  const questionnaireMap = [
    {
      question: 'Attended?',
      property: 'attended',
    },
    {
      question: 'Baby changing?',
      property: 'babyChange',
    },
    {
      question: 'Automatic?',
      property: 'automatic',
    },
    {
      question: 'Radar key?',
      property: 'radar',
    },
  ];

  const optionsMap = graphqlMappings.looProps.definitions;

  // Presence of id indicates that we are editing
  const isEditing = Boolean(props.match.params.id);

  // Find the raw loo data for the given loo
  const { loading: loadingLooData, data: looData, error: looError } = useQuery(
    FIND_BY_ID,
    {
      variables: {
        id: props.match.params.id,
      },
      skip: !isEditing,
    }
  );

  // Set map center if lat and lng query parameters are present
  const [updateStoreCenter] = useMutation(SET_MAP_CENTER);

  const { lat, lng } = queryString.parse(props.location.search);

  useEffect(() => {
    if (isEditing || !lat || !lng) {
      return;
    }

    updateStoreCenter({
      variables: {
        lat: parseFloat(lat),
        lng: parseFloat(lng),
      },
    });
  }, [lat, lng, isEditing, updateStoreCenter]);

  // A temp state for the map center
  // We don't need to fetch this from the cached map state, because nearestLooMap
  // does that bit itself, and if it changes it'll tell us with the callback. This is just a
  // store for us so that we can send off the location with a loo report.
  const [mapCenter, setMapCenter] = useState();

  const [geolocation, setGeolocation] = useState();

  // Fetch the current geolocation
  useEffect(() => {
    getGeolocation((response) => {
      const { longitude, latitude } = response.coords;
      setGeolocation({ lng: longitude, lat: latitude });
    });
  }, []);

  // LooState is the temporary loo object that hold's the user's representation of the loo
  const [looState, setLooState] = useState();

  // Original is compared against LooState when saving changes
  const [original, setOriginal] = useState();

  useEffect(() => {
    if (!looData && isEditing) {
      return;
    }

    let looDataToUse = {};
    if (isEditing) {
      looDataToUse = looData.loo;
    }

    let tempLoo = {
      active: null,
      name: '',
      access: '',
      type: '',
      accessibleType: '',
      opening: '',
      notes: '',
      fee: '',
    };

    // Set questionnaire loo property defaults
    questionnaireMap.forEach((q) => {
      tempLoo[q.property] = '';
    });

    // Deep extend loo state to ensure we get all properties (since we can't guarantee
    // that `looData` will include them all)
    let newLoo = _.merge({}, tempLoo, looDataToUse);
    setLooState(newLoo);
    setMapCenter(newLoo.location);

    // Keep track of defaults so we only submit new information
    setOriginal({
      loo: _.cloneDeep(newLoo),
      center: getCenter(),
    });

    // Whatever you do, don't listen to eslint here. Putting questionnaireMap as a dependency
    // of useEffect will lead to infinite rerenders, crashing the app and browser
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loadingLooData]);

  const handleChange = (event) => {
    // Avoid state mutation
    let loo = _.cloneDeep(looState);

    // Sets nested loo property value
    _.set(loo, event.target.name, event.target.value);

    setLooState(loo);
  };

  const handleTriStateChange = (event) => {
    let val;
    switch (event.target.value) {
      case 'true':
        val = true;
        break;
      case 'false':
        val = false;
        break;
      default:
        val = event.target.value;
    }

    // Avoid state mutation
    var loo = _.cloneDeep(looState);

    _.set(loo, event.target.name, val);

    setLooState(loo);
  };

  const getNovelInput = () => {
    const before = _.cloneDeep(original.loo);
    const now = _.cloneDeep(looState);

    // Add the active state for which there's no user-facing form control as yet.
    now.active = true;

    // Add location
    before.location = {
      lng: original.center.lng,
      lat: original.center.lat,
    };

    let center = getCenter();
    now.location = {
      lng: center.lng,
      lat: center.lat,
    };

    // Keep only new or changed data, by comparing to the form's initial state
    const changes = onlyChanges(now, before);

    if (!isEditing) {
      // If we're a new loo, we always want location, regardless of whether it
      // has changed since the start of the form
      changes.location = now.location;
    }

    return changes;
  };

  const onMapCenterUpdate = (newCenter) => {
    setMapCenter(newCenter);
  };

  // Get the center to use for the loo
  const getCenter = () => {
    return (
      mapCenter ||
      (looData ? looData.loo.location : null) ||
      geolocation ||
      config.fallbackLocation
    );
  };

  const [
    updateLoo,
    { loading: saveLoading, data: saveResponse, error: saveError },
  ] = useMutation(UPDATE_LOO);

  if (saveError) {
    console.error('error saving:', saveError);
  }

  // Redirect to thanks page if successfully made changes
  if (saveResponse && saveResponse.submitReport.code === '200') {
    history.push(`/loos/${saveResponse.submitReport.loo.id}/thanks`);
  }

  const save = () => {
    const changes = getNovelInput();

    // Always associate geometry with a report, even if unchanged
    let center = getCenter();
    changes.location = {
      lng: center.lng,
      lat: center.lat,
    };

    // Set questionnaire options to null before transport.
    questionnaireMap.forEach((q) => {
      if (changes[q.property] === '') changes[q.property] = null;
    });

    if (isEditing) {
      let id = looData.loo.id;
      changes.id = id;

      // Evict the loo from the cache before updating - Apollo
      // is normally smart and can work out when something's changed, but
      // in this case it doesn't and stale data can persist without
      // a cache eviction
      let cache = props.cache;
      cache.evict(getLooCachedId(id));
    }

    updateLoo({
      variables: changes,
    });
  };

  const renderMain = () => {
    const loo = looState;
    const center = getCenter();

    return (

            {config.showBackButtons && (
              <button onClick={props.history.goBack} className={controls.btn}>
                Back
              </button>
            )}

        {isEditing ? (

Update This Toilet

        ) : (

Add This Toilet

        )}

        <MediaQuery maxWidth={config.viewport.mobile}>

{renderMap()}

        </MediaQuery>

        <Notification>

Align the crosshair with where you believe the toilet to be.

        </Notification>

        <DismissableBox
          persistKey="edit-welcome"
          title="Welcome Contributor!"
          content="Please fill in everything you can about the toilet below."
        />

          Toilet name
          <input
            name="name"
            type="text"
            className={controls.text}
            value={loo.name === null ? '' : loo.name}
            onChange={handleChange}
            data-testid="toilet-name"
          />

          Who can access?
          <select
            name="access"
            className={controls.dropdown}
            value={loo.access === null ? '' : loo.access}
            onChange={handleChange}
            data-testid="who-can-access"
          >
            <option value="">Unknown</option>
            {optionsMap.access.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Facilities
          <select
            name="type"
            className={controls.dropdown}
            value={loo.type === null ? '' : loo.type}
            onChange={handleChange}
            data-testid="facilities"
          >
            <option value="">Unknown</option>
            {optionsMap.type.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Accessible facilities
          <select
            name="accessibleType"
            className={controls.dropdown}
            value={loo.accessibleType === null ? '' : loo.accessibleType}
            onChange={handleChange}
            data-testid="accessible-facilities"
          >
            <option value="">Unknown</option>
            {optionsMap.type.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Opening hours
          <select
            name="opening"
            className={controls.dropdown}
            value={loo.opening === null ? '' : loo.opening}
            onChange={handleChange}
            data-testid="opening-hours"
          >
            <option value="">Unknown</option>
            {optionsMap.opening.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

              Yes

              No

              Don't know

          {questionnaireMap.map((q, index) => (
            <fieldset key={index} className={styles.questionnaireGroup}>
              <legend className={helpers.visuallyHidden}>{q.question}</legend>
              {q.question}
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value={true}
                aria-labelledby="yes"
                checked={loo[q.property] === true}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:yes`}
              />
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value={false}
                aria-labelledby="no"
                checked={loo[q.property] === false}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:no`}
              />
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value=""
                aria-labelledby="unknown"
                checked={loo[q.property] === ''}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:unknown`}
              />
            </fieldset>
          ))}

          Fee?
          <input
            name="fee"
            type="text"
            className={controls.text}
            value={loo.fee === null ? '' : loo.fee}
            placeholder="The amount e.g. £0.10"
            onChange={handleChange}
            data-testid="fee"
          />

          Any notes?
          <textarea
            name="notes"
            className={controls.text}
            value={loo.notes === null ? '' : loo.notes}
            onChange={handleChange}
            data-testid="notes"
          />

Data for this toilet

            Latitude
            <input
              type="text"
              name="geometry.coordinates.0"
              className={controls.text}
              value={center.lat}
              readOnly={true}
            />

            Longitude
            <input
              type="text"
              name="geometry.coordinates.1"
              className={controls.text}
              data-testid="loo-name"
              value={center.lng}
              readOnly={true}
            />

        {saveLoading && <Loading message={'Saving your changes...'} />}

        {// This message probably won't be seen, but just in case the redirect fails...
        saveResponse && saveResponse.submitReport.code === '200' && (
          <Notification>
            Successfully saved changes! Redirecting…
          </Notification>
        )}

        {// TODO better error message?
        (saveError ||
          (saveResponse && saveResponse.submitReport.code !== '200')) && (
          <Notification>
            Oops, there was an error saving your changes.
            {console.log(saveError, saveResponse)}
          </Notification>
        )}

          {isEditing ? (
            <input
              type="submit"
              className={controls.btn}
              onClick={save}
              value="Update the toilet"
              disabled={_.isEmpty(getNovelInput())}
            />
          ) : (
            <input
              type="submit"
              className={controls.btn}
              onClick={save}
              value="Add the toilet"
              data-testid="add-the-toilet"
            />
          )}

          {isEditing && (
            <Link
              to={`/loos/${props.match.params.id}/remove`}
              className={controls.btnCaution}
            >
              Remove the toilet
            </Link>
          )}

    );
  };

  const renderMap = () => {
    return (
      <NearestLooMap
        activeLoo={looData ? looData.loo : null}
        highlight={props.match.params.id}
        mapProps={{
          showLocation: false,
          showSearchControl: true,
          showLocateControl: false,
          preventDragging: false,
          minZoom: config.editMinZoom,
        }}
        onUpdateCenter={onMapCenterUpdate}
      />
    );
  };

  if ((isEditing && (loadingLooData || !looData)) || !looState) {
    return (
      <PageLayout
        main={<Loading message={'Fetching Toilet Data'} />}
        map={<Loading message={'Fetching Toilet Data'} />}
      />
    );
  }

  if (looError) {
    console.error(looError);
    return (
      <PageLayout
        main={<Loading message={'Error fetching toilet data'} />}
        map={<Loading message={'Error fetching toilet data'} />}
      />
    );
  }

  // Redirect to index if loo is not active (i.e. removed)
  if (!looData.loo.active) {
    history.push(`/`);
  }

  return <PageLayout main={renderMain()} map={renderMap()} />;
};

AddEditPage.propTypes = {
  cache: PropTypes.object,
};

/**
 * Only keep fields in loo that are different to their corresponding field in
 * from.
 *
 * This is used to only submit novel or differing information in reports.
 */
function onlyChanges(loo, from) {
  return _.transform(loo, (acc, value, key) => {
    if (_.isObject(value)) {
      // Deeply compare
      const nestedChanges = onlyChanges(value, from[key]);
      if (!_.isEmpty(nestedChanges)) {
        acc[key] = nestedChanges;
      }
    } else if (value !== from[key]) {
      // Only keep a value in loo if it is changed
      acc[key] = value;
    }
  });
}

export default AddEditPage;
<commit_msg>fix: crash on add page<commit_after>import React, { useState, useEffect } from 'react';
import { PropTypes } from 'prop-types';

import { Link } from 'react-router-dom';
import MediaQuery from 'react-responsive';
import _ from 'lodash';
import gql from 'graphql-tag';
import queryString from 'query-string';

import PageLayout from '../components/PageLayout';
import Loading from '../components/Loading';
import NearestLooMap from '../components/NearestLooMap';
import DismissableBox from '../components/DismissableBox';
import Notification from '../components/Notification';
import getGeolocation from '../getGeolocation';

import config from '../config';
import graphqlMappings from '../graphqlMappings';

import styles from './css/edit-loo-page.module.css';
import layout from '../components/css/layout.module.css';
import helpers from '../css/helpers.module.css';
import headings from '../css/headings.module.css';
import controls from '../css/controls.module.css';

import { useQuery, useMutation } from '@apollo/client';
import { loader } from 'graphql.macro';
import history from '../history';

const FIND_BY_ID = loader('./findLooById.graphql');
const UPDATE_LOO = loader('./updateLoo.graphql');

const SET_MAP_CENTER = gql`
  mutation SetMapCenter($lat: Number!, $lng: Number!) {
    updateCenter(lat: $lat, lng: $lng) @client
  }
`;

const getLooCachedId = (looId) => 'Loo:' + looId;

const AddEditPage = function (props) {
  const questionnaireMap = [
    {
      question: 'Attended?',
      property: 'attended',
    },
    {
      question: 'Baby changing?',
      property: 'babyChange',
    },
    {
      question: 'Automatic?',
      property: 'automatic',
    },
    {
      question: 'Radar key?',
      property: 'radar',
    },
  ];

  const optionsMap = graphqlMappings.looProps.definitions;

  // Presence of id indicates that we are editing
  const isEditing = Boolean(props.match.params.id);

  // Find the raw loo data for the given loo
  const { loading: loadingLooData, data: looData, error: looError } = useQuery(
    FIND_BY_ID,
    {
      variables: {
        id: props.match.params.id,
      },
      skip: !isEditing,
    }
  );

  // Set map center if lat and lng query parameters are present
  const [updateStoreCenter] = useMutation(SET_MAP_CENTER);

  const { lat, lng } = queryString.parse(props.location.search);

  useEffect(() => {
    if (isEditing || !lat || !lng) {
      return;
    }

    updateStoreCenter({
      variables: {
        lat: parseFloat(lat),
        lng: parseFloat(lng),
      },
    });
  }, [lat, lng, isEditing, updateStoreCenter]);

  // A temp state for the map center
  // We don't need to fetch this from the cached map state, because nearestLooMap
  // does that bit itself, and if it changes it'll tell us with the callback. This is just a
  // store for us so that we can send off the location with a loo report.
  const [mapCenter, setMapCenter] = useState();

  const [geolocation, setGeolocation] = useState();

  // Fetch the current geolocation
  useEffect(() => {
    getGeolocation((response) => {
      const { longitude, latitude } = response.coords;
      setGeolocation({ lng: longitude, lat: latitude });
    });
  }, []);

  // LooState is the temporary loo object that hold's the user's representation of the loo
  const [looState, setLooState] = useState();

  // Original is compared against LooState when saving changes
  const [original, setOriginal] = useState();

  useEffect(() => {
    if (!looData && isEditing) {
      return;
    }

    let looDataToUse = {};
    if (isEditing) {
      looDataToUse = looData.loo;
    }

    let tempLoo = {
      active: null,
      name: '',
      access: '',
      type: '',
      accessibleType: '',
      opening: '',
      notes: '',
      fee: '',
    };

    // Set questionnaire loo property defaults
    questionnaireMap.forEach((q) => {
      tempLoo[q.property] = '';
    });

    // Deep extend loo state to ensure we get all properties (since we can't guarantee
    // that `looData` will include them all)
    let newLoo = _.merge({}, tempLoo, looDataToUse);
    setLooState(newLoo);
    setMapCenter(newLoo.location);

    // Keep track of defaults so we only submit new information
    setOriginal({
      loo: _.cloneDeep(newLoo),
      center: getCenter(),
    });

    // Whatever you do, don't listen to eslint here. Putting questionnaireMap as a dependency
    // of useEffect will lead to infinite rerenders, crashing the app and browser
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loadingLooData]);

  const handleChange = (event) => {
    // Avoid state mutation
    let loo = _.cloneDeep(looState);

    // Sets nested loo property value
    _.set(loo, event.target.name, event.target.value);

    setLooState(loo);
  };

  const handleTriStateChange = (event) => {
    let val;
    switch (event.target.value) {
      case 'true':
        val = true;
        break;
      case 'false':
        val = false;
        break;
      default:
        val = event.target.value;
    }

    // Avoid state mutation
    var loo = _.cloneDeep(looState);

    _.set(loo, event.target.name, val);

    setLooState(loo);
  };

  const getNovelInput = () => {
    const before = _.cloneDeep(original.loo);
    const now = _.cloneDeep(looState);

    // Add the active state for which there's no user-facing form control as yet.
    now.active = true;

    // Add location
    before.location = {
      lng: original.center.lng,
      lat: original.center.lat,
    };

    let center = getCenter();
    now.location = {
      lng: center.lng,
      lat: center.lat,
    };

    // Keep only new or changed data, by comparing to the form's initial state
    const changes = onlyChanges(now, before);

    if (!isEditing) {
      // If we're a new loo, we always want location, regardless of whether it
      // has changed since the start of the form
      changes.location = now.location;
    }

    return changes;
  };

  const onMapCenterUpdate = (newCenter) => {
    setMapCenter(newCenter);
  };

  // Get the center to use for the loo
  const getCenter = () => {
    return (
      mapCenter ||
      (looData ? looData.loo.location : null) ||
      geolocation ||
      config.fallbackLocation
    );
  };

  const [
    updateLoo,
    { loading: saveLoading, data: saveResponse, error: saveError },
  ] = useMutation(UPDATE_LOO);

  if (saveError) {
    console.error('error saving:', saveError);
  }

  // Redirect to thanks page if successfully made changes
  if (saveResponse && saveResponse.submitReport.code === '200') {
    history.push(`/loos/${saveResponse.submitReport.loo.id}/thanks`);
  }

  const save = () => {
    const changes = getNovelInput();

    // Always associate geometry with a report, even if unchanged
    let center = getCenter();
    changes.location = {
      lng: center.lng,
      lat: center.lat,
    };

    // Set questionnaire options to null before transport.
    questionnaireMap.forEach((q) => {
      if (changes[q.property] === '') changes[q.property] = null;
    });

    if (isEditing) {
      let id = looData.loo.id;
      changes.id = id;

      // Evict the loo from the cache before updating - Apollo
      // is normally smart and can work out when something's changed, but
      // in this case it doesn't and stale data can persist without
      // a cache eviction
      let cache = props.cache;
      cache.evict(getLooCachedId(id));
    }

    updateLoo({
      variables: changes,
    });
  };

  const renderMain = () => {
    const loo = looState;
    const center = getCenter();

    return (

            {config.showBackButtons && (
              <button onClick={props.history.goBack} className={controls.btn}>
                Back
              </button>
            )}

        {isEditing ? (

Update This Toilet

        ) : (

Add This Toilet

        )}

        <MediaQuery maxWidth={config.viewport.mobile}>

{renderMap()}

        </MediaQuery>

        <Notification>

Align the crosshair with where you believe the toilet to be.

        </Notification>

        <DismissableBox
          persistKey="edit-welcome"
          title="Welcome Contributor!"
          content="Please fill in everything you can about the toilet below."
        />

          Toilet name
          <input
            name="name"
            type="text"
            className={controls.text}
            value={loo.name === null ? '' : loo.name}
            onChange={handleChange}
            data-testid="toilet-name"
          />

          Who can access?
          <select
            name="access"
            className={controls.dropdown}
            value={loo.access === null ? '' : loo.access}
            onChange={handleChange}
            data-testid="who-can-access"
          >
            <option value="">Unknown</option>
            {optionsMap.access.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Facilities
          <select
            name="type"
            className={controls.dropdown}
            value={loo.type === null ? '' : loo.type}
            onChange={handleChange}
            data-testid="facilities"
          >
            <option value="">Unknown</option>
            {optionsMap.type.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Accessible facilities
          <select
            name="accessibleType"
            className={controls.dropdown}
            value={loo.accessibleType === null ? '' : loo.accessibleType}
            onChange={handleChange}
            data-testid="accessible-facilities"
          >
            <option value="">Unknown</option>
            {optionsMap.type.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

          Opening hours
          <select
            name="opening"
            className={controls.dropdown}
            value={loo.opening === null ? '' : loo.opening}
            onChange={handleChange}
            data-testid="opening-hours"
          >
            <option value="">Unknown</option>
            {optionsMap.opening.map((option, index) => (
              <option key={index} value={option.value}>
                {option.name}
              </option>
            ))}
          </select>

              Yes

              No

              Don't know

          {questionnaireMap.map((q, index) => (
            <fieldset key={index} className={styles.questionnaireGroup}>
              <legend className={helpers.visuallyHidden}>{q.question}</legend>
              {q.question}
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value={true}
                aria-labelledby="yes"
                checked={loo[q.property] === true}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:yes`}
              />
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value={false}
                aria-labelledby="no"
                checked={loo[q.property] === false}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:no`}
              />
              <input
                name={q.property}
                className={styles.questionnaireCol}
                type="radio"
                value=""
                aria-labelledby="unknown"
                checked={loo[q.property] === ''}
                onChange={handleTriStateChange}
                data-testid={`${q.property}:unknown`}
              />
            </fieldset>
          ))}

          Fee?
          <input
            name="fee"
            type="text"
            className={controls.text}
            value={loo.fee === null ? '' : loo.fee}
            placeholder="The amount e.g. £0.10"
            onChange={handleChange}
            data-testid="fee"
          />

          Any notes?
          <textarea
            name="notes"
            className={controls.text}
            value={loo.notes === null ? '' : loo.notes}
            onChange={handleChange}
            data-testid="notes"
          />

Data for this toilet

            Latitude
            <input
              type="text"
              name="geometry.coordinates.0"
              className={controls.text}
              value={center.lat}
              readOnly={true}
            />

            Longitude
            <input
              type="text"
              name="geometry.coordinates.1"
              className={controls.text}
              data-testid="loo-name"
              value={center.lng}
              readOnly={true}
            />

        {saveLoading && <Loading message={'Saving your changes...'} />}

        {// This message probably won't be seen, but just in case the redirect fails...
        saveResponse && saveResponse.submitReport.code === '200' && (
          <Notification>
            Successfully saved changes! Redirecting…
          </Notification>
        )}

        {// TODO better error message?
        (saveError ||
          (saveResponse && saveResponse.submitReport.code !== '200')) && (
          <Notification>
            Oops, there was an error saving your changes.
            {console.log(saveError, saveResponse)}
          </Notification>
        )}

          {isEditing ? (
            <input
              type="submit"
              className={controls.btn}
              onClick={save}
              value="Update the toilet"
              disabled={_.isEmpty(getNovelInput())}
            />
          ) : (
            <input
              type="submit"
              className={controls.btn}
              onClick={save}
              value="Add the toilet"
              data-testid="add-the-toilet"
            />
          )}

          {isEditing && (
            <Link
              to={`/loos/${props.match.params.id}/remove`}
              className={controls.btnCaution}
            >
              Remove the toilet
            </Link>
          )}

    );
  };

  const renderMap = () => {
    return (
      <NearestLooMap
        activeLoo={looData ? looData.loo : null}
        highlight={props.match.params.id}
        mapProps={{
          showLocation: false,
          showSearchControl: true,
          showLocateControl: false,
          preventDragging: false,
          minZoom: config.editMinZoom,
        }}
        onUpdateCenter={onMapCenterUpdate}
      />
    );
  };

  if ((isEditing && (loadingLooData || !looData)) || !looState) {
    return (
      <PageLayout
        main={<Loading message={'Fetching Toilet Data'} />}
        map={<Loading message={'Fetching Toilet Data'} />}
      />
    );
  }

  if (looError) {
    console.error(looError);
    return (
      <PageLayout
        main={<Loading message={'Error fetching toilet data'} />}
        map={<Loading message={'Error fetching toilet data'} />}
      />
    );
  }

  // Redirect to index if loo is not active (i.e. removed)
  if (looData && !looData.loo.active) {
    history.push(`/`);
  }

  return <PageLayout main={renderMain()} map={renderMap()} />;
};

AddEditPage.propTypes = {
  cache: PropTypes.object,
};

/**
 * Only keep fields in loo that are different to their corresponding field in
 * from.
 *
 * This is used to only submit novel or differing information in reports.
 */
function onlyChanges(loo, from) {
  return _.transform(loo, (acc, value, key) => {
    if (_.isObject(value)) {
      // Deeply compare
      const nestedChanges = onlyChanges(value, from[key]);
      if (!_.isEmpty(nestedChanges)) {
        acc[key] = nestedChanges;
      }
    } else if (value !== from[key]) {
      // Only keep a value in loo if it is changed
      acc[key] = value;
    }
  });
}

export default AddEditPage;
<|endoftext|>


<commit_before>import {
  geoAlbers,
  geoAlbersUsa,
  geoAzimuthalEqualArea,
  geoAzimuthalEquidistant,
  geoConicConformal,
  geoConicEqualArea,
  geoConicEquidistant,
  geoEquirectangular,
  geoGnomonic,
  geoMercator,
  geoOrthographic,
  geoStereographic,
  geoTransverseMercator
} from 'd3-geo';

export var properties = [
  // standard properties in d3-geo
  'clipAngle',
  'clipExtent',
  'scale',
  'translate',
  'center',
  'rotate',
  'precision',

  // extended properties in d3-geo-projections
  'coefficient',
  'distance',
  'fraction',
  'lobes',
  'parallel',
  'radius',
  'ratio',
  'spacing',
  'tilt'
];

/**
 * Augment projections with their type and a copy method.
 */
function create(type, constructor) {
  return function projection() {
    var p = constructor();

    p.type = type;

    p.copy = p.copy || function() {
      var c = projection();
      properties.forEach(function(prop) {
        if (p.hasOwnProperty(prop)) c[prop](p[prop]());
      });
      return c;
    };

    return p;
  }
}

export function projection(type, proj) {
  return arguments.length > 1 ? (projections[type] = create(type, proj), this)
    : projections.hasOwnProperty(type) ? projections[type] : null;
}

var projections = {
  // base d3-geo projection types
  albers:               geoAlbers,
  albersusa:            geoAlbersUsa,
  azimuthalequalarea:   geoAzimuthalEqualArea,
  azimuthalequidistant: geoAzimuthalEquidistant,
  conicconformal:       geoConicConformal,
  conicequalarea:       geoConicEqualArea,
  conicequidistant:     geoConicEquidistant,
  equirectangular:      geoEquirectangular,
  gnomonic:             geoGnomonic,
  mercator:             geoMercator,
  orthographic:         geoOrthographic,
  stereographic:        geoStereographic,
  transversemercator:   geoTransverseMercator
};

for (var key in projections) {
  projection(key, projections[key]);
}
<commit_msg>Add missing semi-colon.<commit_after>import {
  geoAlbers,
  geoAlbersUsa,
  geoAzimuthalEqualArea,
  geoAzimuthalEquidistant,
  geoConicConformal,
  geoConicEqualArea,
  geoConicEquidistant,
  geoEquirectangular,
  geoGnomonic,
  geoMercator,
  geoOrthographic,
  geoStereographic,
  geoTransverseMercator
} from 'd3-geo';

export var properties = [
  // standard properties in d3-geo
  'clipAngle',
  'clipExtent',
  'scale',
  'translate',
  'center',
  'rotate',
  'precision',

  // extended properties in d3-geo-projections
  'coefficient',
  'distance',
  'fraction',
  'lobes',
  'parallel',
  'radius',
  'ratio',
  'spacing',
  'tilt'
];

/**
 * Augment projections with their type and a copy method.
 */
function create(type, constructor) {
  return function projection() {
    var p = constructor();

    p.type = type;

    p.copy = p.copy || function() {
      var c = projection();
      properties.forEach(function(prop) {
        if (p.hasOwnProperty(prop)) c[prop](p[prop]());
      });
      return c;
    };

    return p;
  };
}

export function projection(type, proj) {
  return arguments.length > 1 ? (projections[type] = create(type, proj), this)
    : projections.hasOwnProperty(type) ? projections[type] : null;
}

var projections = {
  // base d3-geo projection types
  albers:               geoAlbers,
  albersusa:            geoAlbersUsa,
  azimuthalequalarea:   geoAzimuthalEqualArea,
  azimuthalequidistant: geoAzimuthalEquidistant,
  conicconformal:       geoConicConformal,
  conicequalarea:       geoConicEqualArea,
  conicequidistant:     geoConicEquidistant,
  equirectangular:      geoEquirectangular,
  gnomonic:             geoGnomonic,
  mercator:             geoMercator,
  orthographic:         geoOrthographic,
  stereographic:        geoStereographic,
  transversemercator:   geoTransverseMercator
};

for (var key in projections) {
  projection(key, projections[key]);
}
<|endoftext|>


<commit_before>define([], function() {
  var GameServer = function(app, io) {
    this.app = app;
    this.game = app.game;
    this.playerManager = app.playerManager;
    this.config = app.config;
    this.io = io;
    this.sockets = {};
    this.md5 = require('MD5');
    this.tokens = {};
  };

  GameServer.prototype.getPlayerToken = function(player) {
    return this.hash(player.id + player.name + this.config.secretToken);
  };

  GameServer.prototype.hash = function(s){
    return this.md5(s);
  };

  GameServer.prototype.init = function() {
    this.nextStateUpdate = Date.now() + this.config.timeBetweenStateUpdates;
    this.io.on('connection', this._handleConnect.bind(this));
  };

  GameServer.prototype.getSocket = function(id) {
    return this.sockets[id];
  };

  GameServer.prototype.getTokens = function() {
    return this.tokens;
  };

  GameServer.prototype.isTimeToSendState = function() {
    return (Date.now() > this.nextStateUpdate);
  };

  GameServer.prototype.sendState = function() {
    var state = this.app.getState();

    // remove offline players that aren't on the board
    state.players = state.players.filter(function(player) {
      if (player.isOnline() || player.cellsOnGrid > 0 || player.highScore > 50) {
        return true;
      } else {
        return false;
      }
    });

    this.io.emit('state', state);
    this.nextStateUpdate = Date.now() + this.config.timeBetweenStateUpdates;
  };

  GameServer.prototype.sendStateToSocket = function(socket) {
    socket.emit('state', this.app.getState());
  };

  GameServer.prototype.setTokens = function(tokens) {
    this.tokens = tokens;
  };

  GameServer.prototype._broadcastPlayerConnect = function(socket, player) {
    socket.broadcast.emit('player_connect', {
      cellCount: this.game.grid.getLivingCellCount(),
      player: player.transmission()
    });

    console.log(player.name, 'joined.', this.playerManager.getOnlinePlayers().length, 'player(s) online.');
  };

  GameServer.prototype._handleConnect = function(socket) {
    socket.on('request_state', this._handleStateRequest.bind(this, socket));
    socket.on('request_player', this._handleRequestPlayer.bind(this, socket));
    socket.on('request_new_player', this._handleRequestNewPlayer.bind(this, socket));

    socket.emit('state', this.app.getState());
  };

  GameServer.prototype._handleDisconnect = function(socket, player, message) {
    player.setOnline(false);
    socket.broadcast.emit('player_disconnect', { playerId: player.id });

    console.log(player.name, 'left.', this.playerManager.getOnlinePlayers().length, 'player(s) online.');
  };

  GameServer.prototype._handleRequestPlayer = function(socket, message) {
    var transmission,
      token = message['token'],
      player = this.playerManager.getPlayer(this.tokens[token]);

    if (player) {
      player.setOnline(true);

      transmission = player.transmission();
      socket.emit('receive_new_player', { player: transmission, token: token });

      this._broadcastPlayerConnect(socket, player);

      socket.on('place_live_cells', this._handlePlaceLiveCells.bind(this));
      socket.on('disconnect', this._handleDisconnect.bind(this, socket, player));
    }
  };

  GameServer.prototype._handleRequestNewPlayer = function(socket, message) {
    var name = message.name.trim(),
      color = message.color,
      player,
      errorMessage = '',
      token;

    if (this.playerManager.getPlayerByName(name)) {
      errorMessage = 'The name ' + name + ' is already taken';
    } else if (name.length > 26) {
      errorMessage = 'Your name cannot be longer than 26 characters.';
    }

    if (errorMessage.length > 0) {
      socket.emit('new_player_error', { message: errorMessage });
      return false;
    }

    player = this.playerManager.createNewPlayer({
      name: name,
      color: color
    });
    token = this.getPlayerToken(player);

    this.tokens[token] = player.id;

    player.setOnline(true);

    socket.emit('receive_new_player', { player: player.transmission(), token: token });

    this._broadcastPlayerConnect(socket, player);

    socket.on('place_live_cells', this._handlePlaceLiveCells.bind(this));
    socket.on('disconnect', this._handleDisconnect.bind(this, socket, player));
  };

  GameServer.prototype._handlePlaceLiveCells = function(message) {
    var cells = message.cells,
      player = this.playerManager.getPlayer(message.playerId),
      token = this.getPlayerToken(player);

    if (token !== message.token) {
      return false;
    }

    player.setLastSeen(Date.now());

    if (this.game.canPlaceLiveCells(player, cells)) {
      this.game.placeCells(player, cells);
      this.io.emit('cells_placed', {
        cells: cells,
        cellCount: this.game.grid.getLivingCellCount(),
        player: player
      });
    } else {
      // do nothing for now
    }
  };

  GameServer.prototype._handleStateRequest = function(socket, message) {
    this.sendStateToSocket(socket);
  };

  return GameServer;
});<commit_msg>Now using .online instead of .isOnline() on a JSON object (not an instance)<commit_after>define([], function() {
  var GameServer = function(app, io) {
    this.app = app;
    this.game = app.game;
    this.playerManager = app.playerManager;
    this.config = app.config;
    this.io = io;
    this.sockets = {};
    this.md5 = require('MD5');
    this.tokens = {};
  };

  GameServer.prototype.getPlayerToken = function(player) {
    return this.hash(player.id + player.name + this.config.secretToken);
  };

  GameServer.prototype.hash = function(s){
    return this.md5(s);
  };

  GameServer.prototype.init = function() {
    this.nextStateUpdate = Date.now() + this.config.timeBetweenStateUpdates;
    this.io.on('connection', this._handleConnect.bind(this));
  };

  GameServer.prototype.getSocket = function(id) {
    return this.sockets[id];
  };

  GameServer.prototype.getTokens = function() {
    return this.tokens;
  };

  GameServer.prototype.isTimeToSendState = function() {
    return (Date.now() > this.nextStateUpdate);
  };

  GameServer.prototype.sendState = function() {
    var state = this.app.getState();

    // remove offline players that aren't on the board
    state.players = state.players.filter(function(player) {
      if (player.online || player.cellsOnGrid > 0 || player.highScore > 50) {
        return true;
      } else {
        return false;
      }
    });

    this.io.emit('state', state);
    this.nextStateUpdate = Date.now() + this.config.timeBetweenStateUpdates;
  };

  GameServer.prototype.sendStateToSocket = function(socket) {
    socket.emit('state', this.app.getState());
  };

  GameServer.prototype.setTokens = function(tokens) {
    this.tokens = tokens;
  };

  GameServer.prototype._broadcastPlayerConnect = function(socket, player) {
    socket.broadcast.emit('player_connect', {
      cellCount: this.game.grid.getLivingCellCount(),
      player: player.transmission()
    });

    console.log(player.name, 'joined.', this.playerManager.getOnlinePlayers().length, 'player(s) online.');
  };

  GameServer.prototype._handleConnect = function(socket) {
    socket.on('request_state', this._handleStateRequest.bind(this, socket));
    socket.on('request_player', this._handleRequestPlayer.bind(this, socket));
    socket.on('request_new_player', this._handleRequestNewPlayer.bind(this, socket));

    socket.emit('state', this.app.getState());
  };

  GameServer.prototype._handleDisconnect = function(socket, player, message) {
    player.setOnline(false);
    socket.broadcast.emit('player_disconnect', { playerId: player.id });

    console.log(player.name, 'left.', this.playerManager.getOnlinePlayers().length, 'player(s) online.');
  };

  GameServer.prototype._handleRequestPlayer = function(socket, message) {
    var transmission,
      token = message['token'],
      player = this.playerManager.getPlayer(this.tokens[token]);

    if (player) {
      player.setOnline(true);

      transmission = player.transmission();
      socket.emit('receive_new_player', { player: transmission, token: token });

      this._broadcastPlayerConnect(socket, player);

      socket.on('place_live_cells', this._handlePlaceLiveCells.bind(this));
      socket.on('disconnect', this._handleDisconnect.bind(this, socket, player));
    }
  };

  GameServer.prototype._handleRequestNewPlayer = function(socket, message) {
    var name = message.name.trim(),
      color = message.color,
      player,
      errorMessage = '',
      token;

    if (this.playerManager.getPlayerByName(name)) {
      errorMessage = 'The name ' + name + ' is already taken';
    } else if (name.length > 26) {
      errorMessage = 'Your name cannot be longer than 26 characters.';
    }

    if (errorMessage.length > 0) {
      socket.emit('new_player_error', { message: errorMessage });
      return false;
    }

    player = this.playerManager.createNewPlayer({
      name: name,
      color: color
    });
    token = this.getPlayerToken(player);

    this.tokens[token] = player.id;

    player.setOnline(true);

    socket.emit('receive_new_player', { player: player.transmission(), token: token });

    this._broadcastPlayerConnect(socket, player);

    socket.on('place_live_cells', this._handlePlaceLiveCells.bind(this));
    socket.on('disconnect', this._handleDisconnect.bind(this, socket, player));
  };

  GameServer.prototype._handlePlaceLiveCells = function(message) {
    var cells = message.cells,
      player = this.playerManager.getPlayer(message.playerId),
      token = this.getPlayerToken(player);

    if (token !== message.token) {
      return false;
    }

    player.setLastSeen(Date.now());

    if (this.game.canPlaceLiveCells(player, cells)) {
      this.game.placeCells(player, cells);
      this.io.emit('cells_placed', {
        cells: cells,
        cellCount: this.game.grid.getLivingCellCount(),
        player: player
      });
    } else {
      // do nothing for now
    }
  };

  GameServer.prototype._handleStateRequest = function(socket, message) {
    this.sendStateToSocket(socket);
  };

  return GameServer;
});<|endoftext|>