<commit_before>"use strict";

var bdyNamesUrl = "../get-bdy-names";
var metadataUrl = "../get-metadata";
var dataUrl = "../get-data";

var map;
var info;
var themer;
var geojsonLayer;

var numClasses = 7; // number of classes (i.e colours) in map theme
var minZoom = 4;
var maxZoom = 16;
var currentZoomLevel = 0;

var statsArray = [];
var currentStat;
var boundaryZooms;
var currentStats;
var boundaryOverride = "";

var currentBoundary = "";
var currentStatId = "";

var highlightColour = "#ffff00"
var colourRamp;
var colourRange = ["#1a1a1a", "#e45427"]; // dark grey > orange/red
//var colourRange = ["#1a1a1a", "#DD4132"]; // dark grey > red
//var colourRange = ["#1a1a1a", "#92B558"]; // dark grey > green

// get querystring values
// code from http://forum.jquery.com/topic/getting-value-from-a-querystring
// get querystring as an array split on "&"
var querystring = location.search.replace('?', '').split('&');

// declare object
var queryObj = {};

// loop through each name-value pair and populate object
for (var i = 0; i < querystring.length; i++) {
    // get name and value
    var name = querystring[i].split('=')[0];
    // populate object
    queryObj[name] = querystring[i].split('=')[1];
}

//// get/set values from querystring
//if (!queryObj["census"]) {
//    census = "2016";
//} else {
//    census = queryObj["stats"];
//    // TODO: check census value is valid
//}

// get/set values from querystring

// auto-boundary override (for screenshots only! will create performance issues. e.g showing SA1's nationally!)
if (queryObj["b"] !== undefined) {
    boundaryOverride = queryObj["b"].toLowerCase();
}

// start zoom level
if (!queryObj["z"]) {
    currentZoomLevel = 11;
} else {
    currentZoomLevel = queryObj["z"];
}

//// number of classes to theme the map - DOESN'T WORK YET
//if (!queryObj["n"]) {
//    numClasses = 7;
//} else {
//    numClasses = queryObj["n"];
//}

// get the stat(s) - can include basic equations using + - * / and ()  e.g. B23 * (B45 + B678)
if (!queryObj["stats"]) {
    statsArray = ["b3", "b1", "b2"]; // total_persons

} else {
    statsArray = encodeURIComponent(queryObj["stats"].toLowerCase()).split("%2C");
    // TODO: handle maths operators as well as plain stats
}

function init() {
    // initial stat is the first one in the querystring
    currentStatId = statsArray[0]

    // create colour ramp
    colourRamp = new Rainbow();
    colourRamp.setNumberRange(1, numClasses);
    colourRamp.setSpectrum(colourRange[0], colourRange[1]);

    //Initialize the map on the "map" div - only use canvas if supported
    var elem = document.createElement( "canvas" );

    if ( elem.getContext && elem.getContext( "2d" ) ) {
        map = new L.Map('map', { preferCanvas: true });
    } else {
       map = new L.Map('map', { preferCanvas: false });
    }

    // map = new L.Map('map', { preferCanvas: false }); // canvas slows Safari down versus Chrome (IE & edge are untested)

    // acknowledge the data provider
    map.attributionControl.addAttribution('Census data © ABS');

    // create pane for map labels - a non-interactive pane (i.e. no mouse events)
    map.createPane('labels');

    // This pane is above markers but below popups
    map.getPane('labels').style.zIndex = 650;

    // Layers in this pane are non-interactive and do not obscure mouse/touch events
    map.getPane('labels').style.pointerEvents = 'none';

    // load CartoDB labels
    L.tileLayer('http://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_only_labels/{z}/{x}/{y}.png', {
        attribution : '© OpenStreetMap © CartoDB',
        subdomains : 'abcd',
        minZoom : minZoom,
        maxZoom : maxZoom,
        pane: 'labels'
    }).addTo(map);

    // set the view to a given center and zoom
    map.setView(new L.LatLng(-33.85, 151.15), currentZoomLevel);

    // get bookmarks
    var bmStorage = {
        getAllItems : function (callback) {
            $.getJSON('bookmarks.json',
                function (json) {
                    callback(json);
            });
        }
    };

    // add bookmark control to map
    var bm = new L.Control.Bookmarks({
        position : 'topleft',
        localStorage : false,
        storage : bmStorage
    }).addTo(map);

    // add control that shows info on mouseover
    info = L.control();
    info.onAdd = function () {
        this._div = L.DomUtil.create('div', 'info');
        this.update();
        return this._div;
    };
    info.update = function (props) {
        var infoStr;

        if (props) {
            if (currentStat.maptype === "values") {
                infoStr = '
' + props.name + '
' +
                                '' + props[currentStatId].toLocaleString(['en-AU']) + ' ' + currentStat.type + '';
            } else {
                infoStr = '
' + props.name + '
' +
                                '' + props.percent.toFixed(1).toLocaleString(['en-AU']) + '%
' +
                                props[currentStatId].toLocaleString(['en-AU']) + ' of ' + props.population.toLocaleString(['en-AU']) + ' ' + currentStat.type;
            }
        } else {
            infoStr ='pick a boundary'
        }

        this._div.innerHTML = infoStr;
    };
    info.addTo(map);

    // add radio buttons to choose stat to theme the map
    themer = L.control({
        position : 'bottomright'
    });

    themer.onAdd = function () {
        this._div = L.DomUtil.create('div', 'info themer');
        this.update();
        return this._div;
    };

    themer.update = function (radioButtons) {
        this._div.innerHTML = radioButtons;

        // event to trigger the map theme change
        $("input:radio[name=stat]").click(function () {
            currentStatId = $(this).val();

            // update stat metadata and map data
            getCurrentStatMetadata();
            getData();
        });
    };
    themer.addTo(map);
    themer.update('L O A D I N G . . .');

    // get a new set of data when map panned or zoomed
    map.on('moveend', function () {
        getCurrentStatMetadata();
        getData();
    });

    // get list of boundaries and the zoom levels they display at
    // and get stats metadata, including map theme classes
    $.when(
        $.getJSON(bdyNamesUrl + "?min=" +  + minZoom.toString() + "&max=" + maxZoom.toString()),
        $.getJSON(metadataUrl + "?n=" +  + numClasses.toString() + "&stats=" + statsArray.join())
    ).done(function(bdysResponse, metadataResponse) {
        if (!boundaryOverride){
            boundaryZooms = bdysResponse[0];
        } else {
            // create array of zoom levels with the override boundary id
            boundaryZooms = {};
            for (var j = minZoom; j <= maxZoom; j++) {
                boundaryZooms[j.toString()] = boundaryOverride;
            }
        }

        // get the initial stat's metadata
        currentStats = metadataResponse[0].stats;
        getCurrentStatMetadata();

        // create the radio buttons
        setRadioButtons();

        // get the first lot of data
        getData();
    });
}

function setRadioButtons() {
    var radioButtons = '
Active stat
';

    for (var i = 0; i < currentStats.length; i++){
        var value = currentStats[i].id;
        var description = currentStats[i].description;

        if (value === currentStatId) {
            radioButtons += '
<input id="r' + i.toString() + '" type="radio" name="stat" value="' + value + '" checked="checked">' + description + '
';
        } else {
            radioButtons += '
<input id="r' + i.toString() + '" type="radio" name="stat" value="' + value + '">' + description + '
';
        }
     }

    themer.update(radioButtons);
}

function getCurrentStatMetadata() {
    // loop through the stats to get the current one
    for (var i = 0; i < currentStats.length; i++) {
        if (currentStats[i].id === currentStatId) {
            currentStat = currentStats[i];
        }
    }
}

//// format a number for display based on the number of digits or decimal places
//function formatNumber(number) {
//    var s = number.toString();
//
//    var output;
//
//    if (s.indexOf('.') > 3) output = parseInt(s.split("."));
//    if (s.indexOf('.') > 1) output = parseInt(s.split("."));
//
//    while (s.length < s.indexOf('.') + 4) s += '0';
//
//    return output;
//}

function getData() {

    console.time("got boundaries");

    // get new zoom level and boundary
    currentZoomLevel = map.getZoom();
    currentBoundary = boundaryZooms[currentZoomLevel.toString()];

    //restrict to the zoom levels that have data
    if (currentZoomLevel < minZoom) {
        currentZoomLevel = minZoom;
    }
    if (currentZoomLevel > maxZoom) {
        currentZoomLevel = maxZoom;
    }

    // get map extents
    var bb = map.getBounds();
    var sw = bb.getSouthWest();
    var ne = bb.getNorthEast();

    // build URL
    var ua = [];
    ua.push(dataUrl);
    ua.push("?ml=");
    ua.push(sw.lng.toString());
    ua.push("&mb=");
    ua.push(sw.lat.toString());
    ua.push("&mr=");
    ua.push(ne.lng.toString());
    ua.push("&mt=");
    ua.push(ne.lat.toString());
    ua.push("&s=");
    ua.push(currentStat.id);
    ua.push("&t=");
    ua.push(currentStat.table);
    ua.push("&b=");
    ua.push(currentBoundary);
    ua.push("&m=");
    ua.push(currentStat.maptype);
    ua.push("&z=");
    ua.push((currentZoomLevel).toString());

    var requestString = ua.join('');

//    console.log(requestString);

    //Fire off AJAX request
    $.getJSON(requestString, gotData);
}

function gotData(json) {
    console.timeEnd("got boundaries");
    console.time("parsed GeoJSON");

    if (json !== null) {
        if(geojsonLayer !== undefined) {
            geojsonLayer.clearLayers();
        }

        geojsonLayer = L.geoJson(json, {
            style : style,
            onEachFeature : onEachFeature
        }).addTo(map);
    } else {
        alert("No data returned!")
    }

    console.timeEnd("parsed GeoJSON");
}

function style(feature) {
    var renderVal;

    if (currentStat.maptype === "values") {
        renderVal = parseInt(feature.properties[currentStatId]);
    } else {
        renderVal = parseInt(feature.properties.percent);
    }

    return {
        weight : 2,
        opacity : 1.0,
        color : getColor(renderVal),
        fillOpacity : 1.0,
        fillColor : getColor(renderVal)
    };
}

// get color depending on ratio of count versus max value
function getColor(d) {
    var classes = currentStat[currentBoundary];

    var colour = d > classes[6] ? colourRamp.colourAt(7) :
                 d > classes[5] ? colourRamp.colourAt(6) :
                 d > classes[4] ? colourRamp.colourAt(5) :
                 d > classes[3] ? colourRamp.colourAt(4) :
                 d > classes[2] ? colourRamp.colourAt(3) :
                 d > classes[1] ? colourRamp.colourAt(2) :
                                  colourRamp.colourAt(1);

    return "#" + colour;
}

function onEachFeature(feature, layer) {
    layer.on({
        mouseover : highlightFeature,
        mouseout : resetHighlight
//        onclick : zoomToFeature
    });
}

function highlightFeature(e) {
    var layer = e.target;

    // console.log(layer);

    layer.setStyle({
        weight : 2.5,
        opacity : 0.8,
        color : highlightColour
    });

//    if (!L.Browser.ie && !L.Browser.edge && !L.Browser.opera) {
    layer.bringToFront();
//    }

    info.update(layer.feature.properties);
}

function resetHighlight(e) {
    geojsonLayer.resetStyle(e.target);
    info.update();
}

//function zoomToFeature(e) {
//    map.fitBounds(e.target.getBounds());
//}

//// fix for Apple Magic Mouse jumpiness
//var lastScroll = new Date().getTime();
//L.Map.ScrollWheelZoom.prototype._onWheelScroll = function (e) {
//  if (new Date().getTime() - lastScroll < 600) {
//    e.preventDefault();
//    return;
//  }
//  var delta = L.DomEvent.getWheelDelta(e);
//  var debounce = this._map.options.wheelDebounceTime;
//
//  if (delta >= -0.15 && delta <= 0.15) {
//    e.preventDefault();
//    return;
//  }
//  if (delta <= -0.25) delta = -0.25;
//  if (delta >= 0.25) delta = 0.25;
//  this._delta += delta;
//  this._lastMousePos = this._map.mouseEventToContainerPoint(e);
//
//  if (!this._startTime) {
//      this._startTime = +new Date();
//  }
//
//  var left = Math.max(debounce - (+new Date() - this._startTime), 0);
//
//  clearTimeout(this._timer);
//  lastScroll = new Date().getTime();
//  this._timer = setTimeout(L.bind(this._performZoom, this), left);
//
//  L.DomEvent.stop(e);
//}<commit_msg>working on legend<commit_after>"use strict";

var bdyNamesUrl = "../get-bdy-names";
var metadataUrl = "../get-metadata";
var dataUrl = "../get-data";

var map;
var info;
var legend;
var geojsonLayer;

var numClasses = 7; // number of classes (i.e colours) in map theme
var minZoom = 4;
var maxZoom = 16;
var currentZoomLevel = 0;

var statsArray = [];
var currentStat;
var boundaryZooms;
var currentStats;
var boundaryOverride = "";

var currentBoundary = "";
var currentStatId = "";

var highlightColour = "#ffff00"
var colourRamp;
var colourRange = ["#1a1a1a", "#e45427"]; // dark grey > orange/red
//var colourRange = ["#1a1a1a", "#DD4132"]; // dark grey > red
//var colourRange = ["#1a1a1a", "#92B558"]; // dark grey > green

// get querystring values
// code from http://forum.jquery.com/topic/getting-value-from-a-querystring
// get querystring as an array split on "&"
var querystring = location.search.replace('?', '').split('&');

// declare object
var queryObj = {};

// loop through each name-value pair and populate object
for (var i = 0; i < querystring.length; i++) {
    // get name and value
    var name = querystring[i].split('=')[0];
    // populate object
    queryObj[name] = querystring[i].split('=')[1];
}

//// get/set values from querystring
//if (!queryObj["census"]) {
//    census = "2016";
//} else {
//    census = queryObj["stats"];
//    // TODO: check census value is valid
//}

// get/set values from querystring

// auto-boundary override (for screenshots only! will create performance issues. e.g showing SA1's nationally!)
if (queryObj["b"] !== undefined) {
    boundaryOverride = queryObj["b"].toLowerCase();
}

// start zoom level
if (!queryObj["z"]) {
    currentZoomLevel = 11;
} else {
    currentZoomLevel = queryObj["z"];
}

//// number of classes to theme the map - DOESN'T WORK YET
//if (!queryObj["n"]) {
//    numClasses = 7;
//} else {
//    numClasses = queryObj["n"];
//}

// get the stat(s) - can include basic equations using + - * / and ()  e.g. B23 * (B45 + B678)
if (!queryObj["stats"]) {
    statsArray = ["b3", "b1", "b2"]; // total_persons

} else {
    statsArray = encodeURIComponent(queryObj["stats"].toLowerCase()).split("%2C");
    // TODO: handle maths operators as well as plain stats
}

function init() {
    // initial stat is the first one in the querystring
    currentStatId = statsArray[0]

    // create colour ramp
    colourRamp = new Rainbow();
    colourRamp.setNumberRange(1, numClasses);
    colourRamp.setSpectrum(colourRange[0], colourRange[1]);

    //Initialize the map on the "map" div - only use canvas if supported
    var elem = document.createElement( "canvas" );

    if ( elem.getContext && elem.getContext( "2d" ) ) {
        map = new L.Map('map', { preferCanvas: true });
    } else {
       map = new L.Map('map', { preferCanvas: false });
    }

    // map = new L.Map('map', { preferCanvas: false }); // canvas slows Safari down versus Chrome (IE & edge are untested)

    // acknowledge the data provider
    map.attributionControl.addAttribution('Census data © ABS');

    // create pane for map labels - a non-interactive pane (i.e. no mouse events)
    map.createPane('labels');

    // This pane is above markers but below popups
    map.getPane('labels').style.zIndex = 650;

    // Layers in this pane are non-interactive and do not obscure mouse/touch events
    map.getPane('labels').style.pointerEvents = 'none';

    // load CartoDB labels
    L.tileLayer('http://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_only_labels/{z}/{x}/{y}.png', {
        attribution : '© OpenStreetMap © CartoDB',
        subdomains : 'abcd',
        minZoom : minZoom,
        maxZoom : maxZoom,
        pane: 'labels'
    }).addTo(map);

    // set the view to a given center and zoom
    map.setView(new L.LatLng(-33.85, 151.15), currentZoomLevel);

    // get bookmarks
    var bmStorage = {
        getAllItems : function (callback) {
            $.getJSON('bookmarks.json',
                function (json) {
                    callback(json);
            });
        }
    };

    // add bookmark control to map
    var bm = new L.Control.Bookmarks({
        position : 'topleft',
        localStorage : false,
        storage : bmStorage
    }).addTo(map);

    // add control that shows info on mouseover
    info = L.control();
    info.onAdd = function () {
        this._div = L.DomUtil.create('div', 'info');
        this.update();
        return this._div;
    };
    info.update = function (props) {
        var infoStr;

        if (props) {
            if (currentStat.maptype === "values") {
                infoStr = '
' + props.name + '
' +
                                '' + props[currentStatId].toLocaleString(['en-AU']) + ' ' + currentStat.type + '';
            } else {
                infoStr = '
' + props.name + '
' +
                                '' + props.percent.toFixed(1).toLocaleString(['en-AU']) + '%
' +
                                props[currentStatId].toLocaleString(['en-AU']) + ' of ' + props.population.toLocaleString(['en-AU']) + ' ' + currentStat.type;
            }
        } else {
            infoStr ='pick a boundary'
        }

        this._div.innerHTML = infoStr;
    };
    info.addTo(map);

    //Create a legend control
    legend = L.control({ position: 'bottomright' });

    legend.onAdd = function (map) {
        var div = L.DomUtil.create('div', 'info legend'),
            grades = themeGrades,
            labels = [],
            from, to;

        for (var i = 0; i < numClasses; i++) {
            from = currentStat[currentBoundary][i];
            to = currentStat[currentBoundary][i + 1];

            labels.push(
                ' ' + from + (to ? '–' + to : '+'));
        }

        div.innerHTML = "
Daily water use
" +
                        "
<select id='selectStat' class='dropdown'>" +
                           "<option value='person'>per person</option>" +
                           "<option value='household'>per household</option>" +
                        "</select>
" +
                        "
" + labels.join('
') + '
';
        return div;
    };

    legend.addTo(map);

    //Change map theme when legend dropdown changes
    $('#selectStat').change(function () {
        var selection = this.value;

        switch(selection)
        {
            case "person":
                currStat = "l_pp_day_2009";
                themeGrades = [0, 50, 100, 150, 200, 250, 300, 350];
                break;
            case "household":
                currStat = "l_hh_day_2009";
                themeGrades = [0, 100, 200, 300, 400, 500, 600, 700];
                break;
            default:
                currStat = "l_pp_day_2009";
                themeGrades = [0, 50, 100, 150, 200, 250, 300, 350];
        }

        //Display the boundaries
        loadGeoJson(json);

        //Update the legend
        labels = []

        for (var i = 0; i < themeGrades.length; i++) {
            from = themeGrades[i];
            to = themeGrades[i + 1];

            labels.push(
                ' ' +
                from + (to ? '–' + to : '+'));
        }

        var data = labels.join('
');

        $("#mapLegend").hide().html(data).fadeIn('fast');

    });

    // add radio buttons to choose stat to theme the map
    themer = L.control({
        position : 'bottomright'
    });

    themer.onAdd = function () {
        this._div = L.DomUtil.create('div', 'info themer');
        this.update();
        return this._div;
    };

    themer.update = function (radioButtons) {
        this._div.innerHTML = radioButtons;

        // event to trigger the map theme change
        $("input:radio[name=stat]").click(function () {
            currentStatId = $(this).val();

            // update stat metadata and map data
            getCurrentStatMetadata();
            getData();
        });
    };
    themer.addTo(map);
    themer.update('L O A D I N G . . .');

    // get a new set of data when map panned or zoomed
    map.on('moveend', function () {
        getCurrentStatMetadata();
        getData();
    });

    // get list of boundaries and the zoom levels they display at
    // and get stats metadata, including map theme classes
    $.when(
        $.getJSON(bdyNamesUrl + "?min=" +  + minZoom.toString() + "&max=" + maxZoom.toString()),
        $.getJSON(metadataUrl + "?n=" +  + numClasses.toString() + "&stats=" + statsArray.join())
    ).done(function(bdysResponse, metadataResponse) {
        if (!boundaryOverride){
            boundaryZooms = bdysResponse[0];
        } else {
            // create array of zoom levels with the override boundary id
            boundaryZooms = {};
            for (var j = minZoom; j <= maxZoom; j++) {
                boundaryZooms[j.toString()] = boundaryOverride;
            }
        }

        // get the initial stat's metadata
        currentStats = metadataResponse[0].stats;
        getCurrentStatMetadata();

        // create the radio buttons
        setRadioButtons();

        // get the first lot of data
        getData();
    });
}

function setRadioButtons() {
    var radioButtons = '
Active stat
';

    for (var i = 0; i < currentStats.length; i++){
        var value = currentStats[i].id;
        var description = currentStats[i].description;

        if (value === currentStatId) {
            radioButtons += '
<input id="r' + i.toString() + '" type="radio" name="stat" value="' + value + '" checked="checked">' + description + '
';
        } else {
            radioButtons += '
<input id="r' + i.toString() + '" type="radio" name="stat" value="' + value + '">' + description + '
';
        }
     }

    themer.update(radioButtons);
}

function getCurrentStatMetadata() {
    // loop through the stats to get the current one
    for (var i = 0; i < currentStats.length; i++) {
        if (currentStats[i].id === currentStatId) {
            currentStat = currentStats[i];
        }
    }
}

//// format a number for display based on the number of digits or decimal places
//function formatNumber(number) {
//    var s = number.toString();
//
//    var output;
//
//    if (s.indexOf('.') > 3) output = parseInt(s.split("."));
//    if (s.indexOf('.') > 1) output = parseInt(s.split("."));
//
//    while (s.length < s.indexOf('.') + 4) s += '0';
//
//    return output;
//}

function getData() {

    console.time("got boundaries");

    // get new zoom level and boundary
    currentZoomLevel = map.getZoom();
    currentBoundary = boundaryZooms[currentZoomLevel.toString()];

    //restrict to the zoom levels that have data
    if (currentZoomLevel < minZoom) {
        currentZoomLevel = minZoom;
    }
    if (currentZoomLevel > maxZoom) {
        currentZoomLevel = maxZoom;
    }

    // get map extents
    var bb = map.getBounds();
    var sw = bb.getSouthWest();
    var ne = bb.getNorthEast();

    // build URL
    var ua = [];
    ua.push(dataUrl);
    ua.push("?ml=");
    ua.push(sw.lng.toString());
    ua.push("&mb=");
    ua.push(sw.lat.toString());
    ua.push("&mr=");
    ua.push(ne.lng.toString());
    ua.push("&mt=");
    ua.push(ne.lat.toString());
    ua.push("&s=");
    ua.push(currentStat.id);
    ua.push("&t=");
    ua.push(currentStat.table);
    ua.push("&b=");
    ua.push(currentBoundary);
    ua.push("&m=");
    ua.push(currentStat.maptype);
    ua.push("&z=");
    ua.push((currentZoomLevel).toString());

    var requestString = ua.join('');

//    console.log(requestString);

    //Fire off AJAX request
    $.getJSON(requestString, gotData);
}

function gotData(json) {
    console.timeEnd("got boundaries");
    console.time("parsed GeoJSON");

    if (json !== null) {
        if(geojsonLayer !== undefined) {
            geojsonLayer.clearLayers();
        }

        geojsonLayer = L.geoJson(json, {
            style : style,
            onEachFeature : onEachFeature
        }).addTo(map);
    } else {
        alert("No data returned!")
    }

    console.timeEnd("parsed GeoJSON");
}

function style(feature) {
    var renderVal;

    if (currentStat.maptype === "values") {
        renderVal = parseInt(feature.properties[currentStatId]);
    } else {
        renderVal = parseInt(feature.properties.percent);
    }

    return {
        weight : 2,
        opacity : 1.0,
        color : getColor(renderVal),
        fillOpacity : 1.0,
        fillColor : getColor(renderVal)
    };
}

// get color depending on ratio of count versus max value
function getColor(d) {
    var classes = currentStat[currentBoundary];

    var colour = d > classes[6] ? colourRamp.colourAt(7) :
                 d > classes[5] ? colourRamp.colourAt(6) :
                 d > classes[4] ? colourRamp.colourAt(5) :
                 d > classes[3] ? colourRamp.colourAt(4) :
                 d > classes[2] ? colourRamp.colourAt(3) :
                 d > classes[1] ? colourRamp.colourAt(2) :
                                  colourRamp.colourAt(1);

    return "#" + colour;
}

function onEachFeature(feature, layer) {
    layer.on({
        mouseover : highlightFeature,
        mouseout : resetHighlight
//        onclick : zoomToFeature
    });
}

function highlightFeature(e) {
    var layer = e.target;

    // console.log(layer);

    layer.setStyle({
        weight : 2.5,
        opacity : 0.8,
        color : highlightColour
    });

//    if (!L.Browser.ie && !L.Browser.edge && !L.Browser.opera) {
    layer.bringToFront();
//    }

    info.update(layer.feature.properties);
}

function resetHighlight(e) {
    geojsonLayer.resetStyle(e.target);
    info.update();
}

//function zoomToFeature(e) {
//    map.fitBounds(e.target.getBounds());
//}

//// fix for Apple Magic Mouse jumpiness
//var lastScroll = new Date().getTime();
//L.Map.ScrollWheelZoom.prototype._onWheelScroll = function (e) {
//  if (new Date().getTime() - lastScroll < 600) {
//    e.preventDefault();
//    return;
//  }
//  var delta = L.DomEvent.getWheelDelta(e);
//  var debounce = this._map.options.wheelDebounceTime;
//
//  if (delta >= -0.15 && delta <= 0.15) {
//    e.preventDefault();
//    return;
//  }
//  if (delta <= -0.25) delta = -0.25;
//  if (delta >= 0.25) delta = 0.25;
//  this._delta += delta;
//  this._lastMousePos = this._map.mouseEventToContainerPoint(e);
//
//  if (!this._startTime) {
//      this._startTime = +new Date();
//  }
//
//  var left = Math.max(debounce - (+new Date() - this._startTime), 0);
//
//  clearTimeout(this._timer);
//  lastScroll = new Date().getTime();
//  this._timer = setTimeout(L.bind(this._performZoom, this), left);
//
//  L.DomEvent.stop(e);
//}<|endoftext|>


<commit_before>"use strict";

var restUrl = "../get-data";
var map = null;
var info = null;
var geojsonLayer = null;

var minZoom = 4;
var maxZoom = 16;
var zoomLevel = 10;

var currentStat;
var currentStatType;

var colours = ['#f6d2a9','#f5b78e','#f19c7c','#ea8171','#dd686c','#ca5268','#b13f64']

function init() {
	//Initialize the map on the "map" div
    map = new L.Map('map', { preferCanvas: true });

	// acknowledge the data provider
	map.attributionControl.addAttribution('Census data © ABS');

    // load CARTO basemap tiles
	var tiles = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
			attribution : '© OpenStreetMap © CartoDB',
			subdomains : 'abcd',
			minZoom : minZoom,
			maxZoom : maxZoom
		}).addTo(map);

	// set the view to a given center and zoom
	map.setView(new L.LatLng(-33.85, 151.15), zoomLevel);

    // add bookmark control to map
    // get bookmarks
	var storage = {
		getAllItems : function (callback) {
			$.getJSON('bookmarks.json',
				function (json) {
				callback(json);
			});
		}
	};
	var bmControl = new L.Control.Bookmarks({
        position : 'topleft',
        localStorage : false,
        storage : storage
    }).addTo(map);

	// add control that shows info on mouseover
	info = L.control();
	info.onAdd = function (map) {
		this._div = L.DomUtil.create('div', 'info');
		this.update();
		return this._div;
	};
	info.update = function (props) {
        this._div.innerHTML = (props ? '$' + props.dollardiff.toLocaleString(['en-AU']) + ' difference' : 'pick a hex');
    };
    info.addTo(map);

    // add radio buttons to choose stat to theme the map
    var themer = L.control({
        position : 'bottomright'
    });
    themer.onAdd = function (map) {
        var div = L.DomUtil.create('div', 'info themer');
        div.innerHTML = '
Profitability
Metric
' +
            '
<input id="radio1" type="radio" name="radio" value="count">Policies
' +
            '
<input id="radio2" type="radio" name="radio" value="ratestrength" checked="checked">Rate strength
' +
            '
<input id="radio3" type="radio" name="radio" value="dollardiff">$ Difference
'
        return div;
    };
    themer.addTo(map);

    // event to trigger the map theme to change
    $("input:radio[name=radio]").click(function () {
        valueType = $(this).val();
        //Reload the boundaries - NEEDS TO BE REPLACED WITH A MORE EFFICIENT WAY
        getBoundaries();
    });

    //Get a new set of boundaries when map panned or zoomed
    //TO DO: Handle map movement due to popup
    map.on('moveend', function (e) {
        // map.closePopup();
        getBoundaries();
    });

    // map.on('zoomend', function (e) {
    //     map.closePopup();
    //     //getBoundaries();
    // });

    //Get the first set of boundaries
    getBoundaries();
}

function getBoundaries() {

	console.time("got boundaries");

	//Get zoom level
	zoomLevel = map.getZoom();
	//    console.log("Zoom level = " + zoomLevel.toString());

	//restrict to the zoom levels that have data
	if (zoomLevel < minZoom)
		zoomLevel = minZoom;
	if (zoomLevel > maxZoom)
		zoomLevel = maxZoom;

	//Get map extents
	var bb = map.getBounds();
	var sw = bb.getSouthWest();
	var ne = bb.getNorthEast();

	//Build URL with querystring - selects census bdy attributes, stats and the census boundary geometries as minimised GeoJSON objects
	var ua = [];
	ua.push(restUrl);
	ua.push("?ml=");
	ua.push(sw.lng.toString());
	ua.push("&mb=");
	ua.push(sw.lat.toString());
	ua.push("&mr=");
	ua.push(ne.lng.toString());
	ua.push("&mt=");
	ua.push(ne.lat.toString());
	ua.push("&z=");
	ua.push((zoomLevel).toString());
	//    ua.push("&t=");
	//    ua.push(valueType);

	var reqStr = ua.join('');

	//Fire off AJAX request
	$.getJSON(reqStr, loadBdysNew);
}

function loadBdysNew(json) {
	console.timeEnd("got boundaries");
	console.time("parsed GeoJSON");

	if (json !== null) {
        // L.glify.shapes({
        //     map: map,
        //     click: function (feature, details) {
        //         //set up a standalone popup (use a popup as a layer)
        //         /*L.popup()
        //          .setLatLng(point)
        //          .setContent("You clicked the point at longitude:" + point.lng + ', latitude:' + point.lat)
        //          .openOn(map);*/
        //         console.log('hello');
        //     },
        //     data: json
        // });

        try {
			geojsonLayer.clearLayers();
		} catch (err) {
			//dummy
		}

		// TO FIX: ERRORS NOT BEING TRAPPED
		// try {
			geojsonLayer = L.geoJson(json, {
					style : style,
					onEachFeature : onEachFeature
				}).addTo(map);
		// } catch (err) {
		// 	alert("Couldn't get data!");
		// }
	}

	console.timeEnd("parsed GeoJSON");
}

function style(feature) {

	var renderVal;
	var colours;

	switch (valueType) {
	case "count":
		colours = countColours;
		renderVal = parseInt(feature.properties.count);
		break;
    case "dollardiff":
        colours = rateStrengthColours;
        renderVal = parseInt(feature.properties.dollardiff);
        break;
    case "ratestrength":
        colours = rateStrengthColours;
		renderVal = parseFloat(feature.properties.ratestrength);
		break;
	default:
		colours = rateStrengthColours;
		renderVal = parseFloat(feature.properties.ratestrength);
	}

	return {
		weight : 0,
		opacity : 0.0,
		color : '#666',
		fillOpacity : getOpacity(parseInt(feature.properties.count)),
		fillColor : getColor(renderVal, colours)
	};

    // fillOpacity : getOpacity(renderVal),

}

// get color depending on ratio of count versus max value
function getColor(d, colours) {

    var zoomDiff = 13 - zoomLevel;

    switch (valueType) {
        case "count":
            if (zoomDiff > 0) {
                d = d / Math.pow(4, zoomDiff);
            }

            return d > 500 ? colours[6] :
                d > 200 ? colours[5] :
                    d > 100 ? colours[4] :
                        d > 50 ? colours[3] :
                            d > 25 ? colours[2] :
                                d > 0 ? colours[1] :
                                    colours[0];
            break;

        case "dollardiff":
            // if (zoomDiff > 0) {
            //     d = d / Math.pow(4, zoomDiff);
            // }

            // var colour;
            //
            // if (d >= -25000 && d < -16000) {
            //     colour = colours[6]
            // } else if (d >= -16000 && d < -8000) {
            //     colour = colours[5]
            // } else if (d >= -8000 && d < 0) {
            //     colour = colours[4]
            // } else if (d = 0) {
            //     colour = colours[3]
            // } else if (d > 0 && d < 15000) {
            //     colour = colours[2]
            // } else if (d >= 12500 && d < 30000) {
            //     colour = colours[1]
            // } else {
            //     colour = colours[0]
            // }
            //
            // return colour;

            // 41226, "min": -24078
            //

            var values = dollarDiffMinMax[zoomLevel.toString()];

            return d > values[0] ? colours[6] :
                d > values[1] ? colours[5] :
                    d > values[2] ? colours[4] :
                        d > values[3] ? colours[3] :
                            d > values[4] ? colours[2] :
                                d > values[5] ? colours[1] :
                                    colours[0];
            break;

    case "ratestrength":
        return d > 1.7 ? colours[0] :
            d > 1.5 ? colours[1] :
            d > 1.3 ? colours[2] :
            d > 1.1 ? colours[3] :
            d > 0.9 ? colours[4] :
            d > 0.7 ? colours[5] :
                      colours[6];
        break;
	default:
        return d > 1.7 ? colours[0] :
            d > 1.5 ? colours[1] :
                d > 1.3 ? colours[2] :
                    d > 1.1 ? colours[3] :
                        d > 0.9 ? colours[4] :
                            d > 0.7 ? colours[5] :
                                colours[6];
	}
}

// get color depending on ratio of count versus max value
function getOpacity(d) {

	switch (valueType) {
	case "count":
		return d > 500 ? 0.7 :
		d > 250 ? 0.6 :
		d > 100 ? 0.5 :
		d > 50 ? 0.4 :
		d > 25 ? 0.3 :
		d > 0 ? 0.2 :
				0.1;
		break;
	// case "difference":
	//     zoomDiff = 11 - zoomLevel;
	//     if (zoomDiff > 0) {
	 //        d = d / Math.pow(4, zoomDiff)
	//     }
	//
	// 	return d > 500 ? 0.7 :
	// 	d > 200 ? 0.6 :
	// 	d > 100 ? 0.5 :
	// 	d > 50 ? 0.4 :
	// 	d > 25 ? 0.3 :
	// 	d > 0 ? 0.2 :
	// 	        0.1;
	// 	break;
	default:
		return d > 500 ? 0.7 :
		d > 200 ? 0.6 :
		d > 100 ? 0.5 :
		d > 50 ? 0.4 :
		d > 25 ? 0.3 :
		d > 0 ? 0.2 :
				0.1;
	}
}

function onEachFeature(feature, layer) {
	layer.on({
        mouseover : highlightFeature,
        mouseout : resetHighlight
	});
}

function highlightFeature(e) {
	var layer = e.target;

	// console.log(layer);

	layer.setStyle({
		weight : 2,
		opacity : 0.9,
		fillOpacity : 0.7
	});

	if (!L.Browser.ie && !L.Browser.opera) {
		layer.bringToFront();
	}

	info.update(layer.feature.properties);
}

function resetHighlight(e) {
	geojsonLayer.resetStyle(e.target);
	info.update();
}

// function zoomToFeature(e) {
// 	map.fitBounds(e.target.getBounds());
// }<commit_msg>added querystring support<commit_after>"use strict";

var restUrl = "../get-data";
var map;
var info;
var geojsonLayer;

var minZoom = 4;
var maxZoom = 16;
var zoomLevel = 10;

var census;
var stats;
var currentStat;
var currentStatType;

var colours = ['#f6d2a9','#f5b78e','#f19c7c','#ea8171','#dd686c','#ca5268','#b13f64']

// get querystring values

//Code from http://forum.jquery.com/topic/getting-value-from-a-querystring
// ***this goes on the global scope
// get querystring as an array split on "&"
var querystring = location.search.replace('?', '').split('&');

// declare object
var queryObj = {};

// loop through each name-value pair and populate object
for (var i = 0; i < querystring.length; i++) {
    // get name and value
    var name = querystring[i].split('=')[0];
    var value = querystring[i].split('=')[1];
    // populate object
    queryObj[name] = value;
}

// get/set values from querystring
if (queryObj["census"] === undefined) {
  census = "2016";
} else {
  census = queryObj["stats"];
  // TODO: check census value is valid
}
if (queryObj["stats"] === undefined) {
  statArray = ["B3"]; // total_persons
} else {
  statArray = queryObj["stats"].split(",");
}

function init() {
	//Initialize the map on the "map" div
    map = new L.Map('map', { preferCanvas: true });

	// acknowledge the data provider
	map.attributionControl.addAttribution('Census data © ABS');

    // load CARTO basemap tiles
	var tiles = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
			attribution : '© OpenStreetMap © CartoDB',
			subdomains : 'abcd',
			minZoom : minZoom,
			maxZoom : maxZoom
		}).addTo(map);

	// set the view to a given center and zoom
	map.setView(new L.LatLng(-33.85, 151.15), zoomLevel);

    // add bookmark control to map
    // get bookmarks
	var storage = {
		getAllItems : function (callback) {
			$.getJSON('bookmarks.json',
				function (json) {
				callback(json);
			});
		}
	};
	var bmControl = new L.Control.Bookmarks({
        position : 'topleft',
        localStorage : false,
        storage : storage
    }).addTo(map);

	// add control that shows info on mouseover
	info = L.control();
	info.onAdd = function (map) {
		this._div = L.DomUtil.create('div', 'info');
		this.update();
		return this._div;
	};
	info.update = function (props) {
        this._div.innerHTML = (props ? '$' + props.dollardiff.toLocaleString(['en-AU']) + ' difference' : 'pick a hex');
    };
    info.addTo(map);

    // add radio buttons to choose stat to theme the map
    var themer = L.control({
        position : 'bottomright'
    });
    themer.onAdd = function (map) {
        var div = L.DomUtil.create('div', 'info themer');
        div.innerHTML = '
Profitability
Metric
' +
            '
<input id="radio1" type="radio" name="radio" value="count">Policies
' +
            '
<input id="radio2" type="radio" name="radio" value="ratestrength" checked="checked">Rate strength
' +
            '
<input id="radio3" type="radio" name="radio" value="dollardiff">$ Difference
'
        return div;
    };
    themer.addTo(map);

    // event to trigger the map theme to change
    $("input:radio[name=radio]").click(function () {
        valueType = $(this).val();
        //Reload the boundaries - NEEDS TO BE REPLACED WITH A MORE EFFICIENT WAY
        getBoundaries();
    });

    //Get a new set of boundaries when map panned or zoomed
    //TO DO: Handle map movement due to popup
    map.on('moveend', function (e) {
        // map.closePopup();
        getBoundaries();
    });

    // map.on('zoomend', function (e) {
    //     map.closePopup();
    //     //getBoundaries();
    // });

    //Get the first set of boundaries
    getBoundaries();
}

function getBoundaries() {

	console.time("got boundaries");

	// get zoom level
	zoomLevel = map.getZoom();
	//    console.log("Zoom level = " + zoomLevel.toString());

	//restrict to the zoom levels that have data
	if (zoomLevel < minZoom) {
		zoomLevel = minZoom;
	}
	if (zoomLevel > maxZoom) {
		zoomLevel = maxZoom;
    }

	// get map extents
	var bb = map.getBounds();
	var sw = bb.getSouthWest();
	var ne = bb.getNorthEast();

	// build URL with querystring
	var ua = [];
	ua.push(restUrl);
	ua.push("?ml=");
	ua.push(sw.lng.toString());
	ua.push("&mb=");
	ua.push(sw.lat.toString());
	ua.push("&mr=");
	ua.push(ne.lng.toString());
	ua.push("&mt=");
	ua.push(ne.lat.toString());
	ua.push("&z=");
	ua.push((zoomLevel).toString());
	//    ua.push("&t=");
	//    ua.push(valueType);

	var reqStr = ua.join('');

	//Fire off AJAX request
	$.getJSON(reqStr, loadBdysNew);
}

function loadBdysNew(json) {
	console.timeEnd("got boundaries");
	console.time("parsed GeoJSON");

	if (json !== null) {
        if(geojsonLayer !== undefined) {
			geojsonLayer.clearLayers();
		}

        geojsonLayer = L.geoJson(json, {
            style : style,
            onEachFeature : onEachFeature
        }).addTo(map);
	} else {
	    alert("No data returned!")
	}

	console.timeEnd("parsed GeoJSON");
}

function style(feature) {

	var renderVal;
	var colours;

	switch (valueType) {
	case "count":
		colours = countColours;
		renderVal = parseInt(feature.properties.count);
		break;
    case "dollardiff":
        colours = rateStrengthColours;
        renderVal = parseInt(feature.properties.dollardiff);
        break;
    case "ratestrength":
        colours = rateStrengthColours;
		renderVal = parseFloat(feature.properties.ratestrength);
		break;
	default:
		colours = rateStrengthColours;
		renderVal = parseFloat(feature.properties.ratestrength);
	}

	return {
		weight : 0,
		opacity : 0.0,
		color : '#666',
		fillOpacity : getOpacity(parseInt(feature.properties.count)),
		fillColor : getColor(renderVal, colours)
	};

    // fillOpacity : getOpacity(renderVal),

}

// get color depending on ratio of count versus max value
function getColor(d, colours) {

    var zoomDiff = 13 - zoomLevel;

    switch (valueType) {
        case "count":
            if (zoomDiff > 0) {
                d = d / Math.pow(4, zoomDiff);
            }

            return d > 500 ? colours[6] :
                d > 200 ? colours[5] :
                    d > 100 ? colours[4] :
                        d > 50 ? colours[3] :
                            d > 25 ? colours[2] :
                                d > 0 ? colours[1] :
                                    colours[0];
            break;

        case "dollardiff":
            // if (zoomDiff > 0) {
            //     d = d / Math.pow(4, zoomDiff);
            // }

            // var colour;
            //
            // if (d >= -25000 && d < -16000) {
            //     colour = colours[6]
            // } else if (d >= -16000 && d < -8000) {
            //     colour = colours[5]
            // } else if (d >= -8000 && d < 0) {
            //     colour = colours[4]
            // } else if (d = 0) {
            //     colour = colours[3]
            // } else if (d > 0 && d < 15000) {
            //     colour = colours[2]
            // } else if (d >= 12500 && d < 30000) {
            //     colour = colours[1]
            // } else {
            //     colour = colours[0]
            // }
            //
            // return colour;

            // 41226, "min": -24078
            //

            var values = dollarDiffMinMax[zoomLevel.toString()];

            return d > values[0] ? colours[6] :
                d > values[1] ? colours[5] :
                    d > values[2] ? colours[4] :
                        d > values[3] ? colours[3] :
                            d > values[4] ? colours[2] :
                                d > values[5] ? colours[1] :
                                    colours[0];
            break;

    case "ratestrength":
        return d > 1.7 ? colours[0] :
            d > 1.5 ? colours[1] :
            d > 1.3 ? colours[2] :
            d > 1.1 ? colours[3] :
            d > 0.9 ? colours[4] :
            d > 0.7 ? colours[5] :
                      colours[6];
        break;
	default:
        return d > 1.7 ? colours[0] :
            d > 1.5 ? colours[1] :
                d > 1.3 ? colours[2] :
                    d > 1.1 ? colours[3] :
                        d > 0.9 ? colours[4] :
                            d > 0.7 ? colours[5] :
                                colours[6];
	}
}

// get color depending on ratio of count versus max value
function getOpacity(d) {

	switch (valueType) {
	case "count":
		return d > 500 ? 0.7 :
		d > 250 ? 0.6 :
		d > 100 ? 0.5 :
		d > 50 ? 0.4 :
		d > 25 ? 0.3 :
		d > 0 ? 0.2 :
				0.1;
		break;
	// case "difference":
	//     zoomDiff = 11 - zoomLevel;
	//     if (zoomDiff > 0) {
	 //        d = d / Math.pow(4, zoomDiff)
	//     }
	//
	// 	return d > 500 ? 0.7 :
	// 	d > 200 ? 0.6 :
	// 	d > 100 ? 0.5 :
	// 	d > 50 ? 0.4 :
	// 	d > 25 ? 0.3 :
	// 	d > 0 ? 0.2 :
	// 	        0.1;
	// 	break;
	default:
		return d > 500 ? 0.7 :
		d > 200 ? 0.6 :
		d > 100 ? 0.5 :
		d > 50 ? 0.4 :
		d > 25 ? 0.3 :
		d > 0 ? 0.2 :
				0.1;
	}
}

function onEachFeature(feature, layer) {
	layer.on({
        mouseover : highlightFeature,
        mouseout : resetHighlight
	});
}

function highlightFeature(e) {
	var layer = e.target;

	// console.log(layer);

	layer.setStyle({
		weight : 2,
		opacity : 0.9,
		fillOpacity : 0.7
	});

	if (!L.Browser.ie && !L.Browser.opera) {
		layer.bringToFront();
	}

	info.update(layer.feature.properties);
}

function resetHighlight(e) {
	geojsonLayer.resetStyle(e.target);
	info.update();
}

// function zoomToFeature(e) {
// 	map.fitBounds(e.target.getBounds());
// }<|endoftext|>


<commit_before>$(function() {

    for (var y = 0; y < 14; y++) {
        var row = $('<div/>', {
                'class': 'row'
            }).appendTo('.map');
        for (var x = 0; x < 14; x++) {
            var field = $('<div/>', {
                'data-x': x,
                'data-y': y,
                'class': 'field'
            }).appendTo(row);
            $('<div/>', {
                'class': 'field_back'
            }).appendTo(field);
            $('<div/>', {
                'class': 'field_content'
            }).appendTo(field);
            $('<div/>', {
                'class': 'field_holder'
            }).appendTo(field);
        }
    };

    var Game = function (socket) {
        this.socket = socket;
        this.id = null;
        this.player = null;
        this.phase = null;
        this.units = null;
    };

    Game.prototype.send = function (t, params) {
        socket.emit("command", {
            type: t,
            params: params
        });
    };

    Game.prototype.skip = function () {
        socket.emit("command", {
            type: 'skip'
        });
    };

    Game.prototype.update = function (snapshot) {
        $('.field_content').html('').removeClass().addClass('field_content');
        $('.field').removeClass().addClass('field').unbind('click')
                .removeData('from').removeData('whereCouldAttack').removeData('whereCanMove');
        $('body').unbind('keypress');
        this.id = snapshot.myId;
        this.player = snapshot.players[this.id];
        this.phase = snapshot.world.phase;
        for (var i in snapshot.players) {
            for (var v in snapshot.players[i].units) {
                var unit = snapshot.players[i].units[v];
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"] .field_content')
                    .addClass('unit')
                    .addClass(i == this.id ? unit.type : 'unknownUnit')
                    .parents('.field').addClass('with_unit');
            }
        }
        this[this.phase](snapshot);
    };

    Game.prototype.hasUnitHere = function (x, y) {
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.location[0] == x && unit.location[1] == y) {
                return true;
            }
        }
        return false;
    };

    Game.prototype.planning_phase = function (snapshot) {
        $('.field.with_unit').addClass('can_move');
        var unit = null;
        for (var v in this.player.unitsToPlace) {
            if (this.player.unitsToPlace[v] > 0) {
                unit = v;
                break;
            }
        }
        var voidFields = this.player.freeCells;
        if (unit) {
            var field = voidFields.splice(Math.floor(Math.random() * voidFields.length), 1)[0];
            this.placeUnit(unit, field);
        }
    };

    Game.prototype.move_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        var me = this;
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.whereCanMove.length) {
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"]')
                    .addClass('can_move')
                    .data('whereCanMove', unit.whereCanMove)
                    .click(function () {
                        $('.field').removeClass('move_from').removeClass('move_to').removeData('from');
                        $(this).addClass('move_from');
                        var whereCanMove = $(this).data('whereCanMove');
                        for (var i = 0; i < whereCanMove.length; i++) {
                            var p = whereCanMove[i];
                            $('.field[data-x="' + p[0] + '"][data-y="' + p[1] + '"]').addClass('move_to')
                                .data('from', [$(this).data('x'), $(this).data('y')])
                                .click(function () {
                                    me.send('move', { 'from': $(this).data('from'), 'to': [$(this).data('x'), $(this).data('y')] });
                                    $('.field').unbind('click');
                                });
                        }
                    });
            }
        }
    };

    Game.prototype.attack_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        var me = this;
        var skip = true;
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.whereCanAttack.length) {
                skip = false;
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"]')
                    .addClass('can_move')
                    .data('whereCanMove', unit.whereCanAttack)
                    .data('whereCouldAttack', unit.whereCouldAttack)
                    .click(function () {
                        $('.field').removeClass('move_from').removeClass('move_to').removeClass('could_attack').removeData('from');
                        var whereCouldAttack = $(this).data('whereCouldAttack');
                        for (var i = 0; i < whereCouldAttack.length; i++) {
                            var place = whereCouldAttack[i];
                            $('.field[data-x="' + place[0] + '"][data-y="' + place[1] + '"]')
                                .addClass('could_attack');
                        }
                        $(this).addClass('move_from');
                        var whereCanMove = $(this).data('whereCanMove');
                        for (var i = 0; i < whereCanMove.length; i++) {
                            var p = whereCanMove[i];
                            $('.field[data-x="' + p[0] + '"][data-y="' + p[1] + '"]').addClass('move_to')
                                .data('from', [$(this).data('x'), $(this).data('y')])
                                .click(function () {
                                    me.send('attack', { 'from': $(this).data('from'), 'to': [$(this).data('x'), $(this).data('y')] });
                                    $('.field').unbind('click');
                                    $('body').unbind('keypress');
                                });
                        }
                    });
            }
        }
        if (skip) {
            me.skip();
            $('.field').unbind('click');
            $('body').unbind('keypress');
        } else {
            $("body").keypress(function(event) {
                // w
                if (event.charCode == 119) {
                    me.skip();
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                }
            });
        }
    };

    Game.prototype.support_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        if (!this.player.supportCells || !this.player.supportCells.length) {
            me.skip();
            $('.field').unbind('click');
            $('body').unbind('keypress');
        } else {
            $("body").keypress(function(event) {
                // w
                if (event.charCode == 119) {
                    me.skip();
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                }
            });
        }
        var me = this;
        for (var v in this.player.supportCells) {
            var place = this.player.supportCells[v];
            $('.field[data-x="' + place[0] + '"][data-y="' + place[1] + '"]')
                .addClass('can_move')
                .click(function () {
                    me.send('support', { 'target': [$(this).data('x'), $(this).data('y')] });
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                });
        }
    };

    Game.prototype.placeUnit = function (unit, place) {
        this.send('place', { 'location': place, 'type': unit });
    };

    var socket = io.connect(":" + LocalConfig.port);
    var game = new Game(socket);

    socket.on("update", function (snapshot) {
        console.log(snapshot);
        game.update(snapshot);
    });
});

<commit_msg>skip results phase on client<commit_after>$(function() {

    for (var y = 0; y < 14; y++) {
        var row = $('<div/>', {
                'class': 'row'
            }).appendTo('.map');
        for (var x = 0; x < 14; x++) {
            var field = $('<div/>', {
                'data-x': x,
                'data-y': y,
                'class': 'field'
            }).appendTo(row);
            $('<div/>', {
                'class': 'field_back'
            }).appendTo(field);
            $('<div/>', {
                'class': 'field_content'
            }).appendTo(field);
            $('<div/>', {
                'class': 'field_holder'
            }).appendTo(field);
        }
    };

    var Game = function (socket) {
        this.socket = socket;
        this.id = null;
        this.player = null;
        this.phase = null;
        this.units = null;
    };

    Game.prototype.send = function (t, params) {
        socket.emit("command", {
            type: t,
            params: params
        });
    };

    Game.prototype.skip = function () {
        socket.emit("command", {
            type: 'skip'
        });
    };

    Game.prototype.update = function (snapshot) {
        $('.field_content').html('').removeClass().addClass('field_content');
        $('.field').removeClass().addClass('field').unbind('click')
                .removeData('from').removeData('whereCouldAttack').removeData('whereCanMove');
        $('body').unbind('keypress');
        this.id = snapshot.myId;
        this.player = snapshot.players[this.id];
        this.phase = snapshot.world.phase;
        for (var i in snapshot.players) {
            for (var v in snapshot.players[i].units) {
                var unit = snapshot.players[i].units[v];
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"] .field_content')
                    .addClass('unit')
                    .addClass(i == this.id ? unit.type : 'unknownUnit')
                    .parents('.field').addClass('with_unit');
            }
        }
        this[this.phase](snapshot);
    };

    Game.prototype.hasUnitHere = function (x, y) {
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.location[0] == x && unit.location[1] == y) {
                return true;
            }
        }
        return false;
    };

    Game.prototype.planning_phase = function (snapshot) {
        $('.field.with_unit').addClass('can_move');
        var unit = null;
        for (var v in this.player.unitsToPlace) {
            if (this.player.unitsToPlace[v] > 0) {
                unit = v;
                break;
            }
        }
        var voidFields = this.player.freeCells;
        if (unit) {
            var field = voidFields.splice(Math.floor(Math.random() * voidFields.length), 1)[0];
            this.placeUnit(unit, field);
        }
    };

    Game.prototype.move_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        var me = this;
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.whereCanMove.length) {
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"]')
                    .addClass('can_move')
                    .data('whereCanMove', unit.whereCanMove)
                    .click(function () {
                        $('.field').removeClass('move_from').removeClass('move_to').removeData('from');
                        $(this).addClass('move_from');
                        var whereCanMove = $(this).data('whereCanMove');
                        for (var i = 0; i < whereCanMove.length; i++) {
                            var p = whereCanMove[i];
                            $('.field[data-x="' + p[0] + '"][data-y="' + p[1] + '"]').addClass('move_to')
                                .data('from', [$(this).data('x'), $(this).data('y')])
                                .click(function () {
                                    me.send('move', { 'from': $(this).data('from'), 'to': [$(this).data('x'), $(this).data('y')] });
                                    $('.field').unbind('click');
                                });
                        }
                    });
            }
        }
    };

    Game.prototype.attack_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        var me = this;
        var skip = true;
        for (var v in this.player.units) {
            var unit = this.player.units[v];
            if (unit.whereCanAttack.length) {
                skip = false;
                $('.field[data-x="' + unit.location[0] + '"][data-y="' + unit.location[1] + '"]')
                    .addClass('can_move')
                    .data('whereCanMove', unit.whereCanAttack)
                    .data('whereCouldAttack', unit.whereCouldAttack)
                    .click(function () {
                        $('.field').removeClass('move_from').removeClass('move_to').removeClass('could_attack').removeData('from');
                        var whereCouldAttack = $(this).data('whereCouldAttack');
                        for (var i = 0; i < whereCouldAttack.length; i++) {
                            var place = whereCouldAttack[i];
                            $('.field[data-x="' + place[0] + '"][data-y="' + place[1] + '"]')
                                .addClass('could_attack');
                        }
                        $(this).addClass('move_from');
                        var whereCanMove = $(this).data('whereCanMove');
                        for (var i = 0; i < whereCanMove.length; i++) {
                            var p = whereCanMove[i];
                            $('.field[data-x="' + p[0] + '"][data-y="' + p[1] + '"]').addClass('move_to')
                                .data('from', [$(this).data('x'), $(this).data('y')])
                                .click(function () {
                                    me.send('attack', { 'from': $(this).data('from'), 'to': [$(this).data('x'), $(this).data('y')] });
                                    $('.field').unbind('click');
                                    $('body').unbind('keypress');
                                });
                        }
                    });
            }
        }
        if (skip) {
            me.skip();
            $('.field').unbind('click');
            $('body').unbind('keypress');
        } else {
            $("body").keypress(function(event) {
                // w
                if (event.charCode == 119) {
                    me.skip();
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                }
            });
        }
    };

    Game.prototype.support_phase = function (snapshot) {
        if (snapshot.world.currentPlayerId != this.id) {
            return;
        }
        if (!this.player.supportCells || !this.player.supportCells.length) {
            me.skip();
            $('.field').unbind('click');
            $('body').unbind('keypress');
        } else {
            $("body").keypress(function(event) {
                // w
                if (event.charCode == 119) {
                    me.skip();
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                }
            });
        }
        var me = this;
        for (var v in this.player.supportCells) {
            var place = this.player.supportCells[v];
            $('.field[data-x="' + place[0] + '"][data-y="' + place[1] + '"]')
                .addClass('can_move')
                .click(function () {
                    me.send('support', { 'target': [$(this).data('x'), $(this).data('y')] });
                    $('.field').unbind('click');
                    $('body').unbind('keypress');
                });
        }
    };

    Game.prototype.battle_results_phase = function (snapshot) {
        me.skip();
    };

    Game.prototype.placeUnit = function (unit, place) {
        this.send('place', { 'location': place, 'type': unit });
    };

    var socket = io.connect(":" + LocalConfig.port);
    var game = new Game(socket);

    socket.on("update", function (snapshot) {
        console.log(snapshot);
        game.update(snapshot);
    });
});

<|endoftext|>


<commit_before>const EVENT_DOM_READY = 'DOMContentLoaded'

let callbacks = []

const onReady = () => {
  callbacks.forEach((fn) => {
    fn.call()
  })
  document.removeEventListener(EVENT_DOM_READY, onReady)
}

export default function ready(fn) {
  callbacks.push(fn)
  if (document.readyState !== 'loading') {
    onReady.call()
  } else {
    document.addEventListener(EVENT_DOM_READY, onReady)
  }
}
<commit_msg>made event const camelcase<commit_after>const eventReady = 'DOMContentLoaded'

let callbacks = []

const onReady = () => {
  callbacks.forEach((fn) => {
    fn.call()
  })
  document.removeEventListener(eventReady, onReady)
}

export default function ready(fn) {
  callbacks.push(fn)
  if (document.readyState !== 'loading') {
    onReady.call()
  } else {
    document.addEventListener(eventReady, onReady)
  }
}
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../../utils/Class');

/**
 * @classdesc
 * [description]
 *
 * @class RandomDataGenerator
 * @memberOf Phaser.Math
 * @constructor
 * @since 3.0.0
 *
 * @param {array} [seeds] - [description]
 */
var RandomDataGenerator = new Class({

    initialize:

    function RandomDataGenerator (seeds)
    {
        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#c
         * @type {number}
         * @default 1
         * @private
         * @since 3.0.0
         */
        this.c = 1;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s0
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s0 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s1
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s1 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s2
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s2 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#n
         * @type {number}
         * @default 0
         * @private
         * @since 3.2.0
         */
        this.n = 0;

        /**
         * [description]
         *
         * @name Phaser.Math.RandomDataGenerator#sign
         * @type {array}
         * @since 3.0.0
         */
        this.sign = [ -1, 1 ];

        if (seeds)
        {
            this.init(seeds);
        }
    },

    /**
     * Private random helper.
     *
     * @method Phaser.Math.RandomDataGenerator#rnd
     * @since 3.0.0
     * @private
     *
     * @return {number} [description]
     */
    rnd: function ()
    {
        var t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32

        this.c = t | 0;
        this.s0 = this.s1;
        this.s1 = this.s2;
        this.s2 = t - this.c;

        return this.s2;
    },

    /**
     * Internal method that creates a seed hash.
     *
     * @method Phaser.Math.RandomDataGenerator#hash
     * @since 3.0.0
     * @private
     *
     * @param {[type]} data - [description]
     *
     * @return {number} The hashed value.
     */
    hash: function (data)
    {
        var h;
        var n = this.n;

        data = data.toString();

        for (var i = 0; i < data.length; i++)
        {
            n += data.charCodeAt(i);
            h = 0.02519603282416938 * n;
            n = h >>> 0;
            h -= n;
            h *= n;
            n = h >>> 0;
            h -= n;
            n += h * 0x100000000;// 2^32
        }

        this.n = n;

        return (n >>> 0) * 2.3283064365386963e-10;// 2^-32
    },

    /**
     * [description]
     *
     * @method Phaser.Math.RandomDataGenerator#init
     * @since 3.0.0
     *
     * @param {string|array} seeds - [description]
     */
    init: function (seeds)
    {
        if (typeof seeds === 'string')
        {
            this.state(seeds);
        }
        else
        {
            this.sow(seeds);
        }
    },

    /**
     * Reset the seed of the random data generator.
     *
     * _Note_: the seed array is only processed up to the first `undefined` (or `null`) value, should such be present.
     *
     * @method Phaser.Math.RandomDataGenerator#sow
     * @since 3.0.0
     *
     * @param {any[]} seeds - The array of seeds: the `toString()` of each value is used.
     */
    sow: function (seeds)
    {
        // Always reset to default seed
        this.n = 0xefc8249d;
        this.s0 = this.hash(' ');
        this.s1 = this.hash(' ');
        this.s2 = this.hash(' ');
        this.c = 1;

        if (!seeds)
        {
            return;
        }

        // Apply any seeds
        for (var i = 0; i < seeds.length && (seeds[i] != null); i++)
        {
            var seed = seeds[i];

            this.s0 -= this.hash(seed);
            this.s0 += ~~(this.s0 < 0);
            this.s1 -= this.hash(seed);
            this.s1 += ~~(this.s1 < 0);
            this.s2 -= this.hash(seed);
            this.s2 += ~~(this.s2 < 0);
        }
    },

    /**
     * Returns a random integer between 0 and 2^32.
     *
     * @method Phaser.Math.RandomDataGenerator#integer
     * @since 3.0.0
     *
     * @return {number} A random integer between 0 and 2^32.
     */
    integer: function ()
    {
        // 2^32
        return this.rnd() * 0x100000000;
    },

    /**
     * Returns a random real number between 0 and 1.
     *
     * @method Phaser.Math.RandomDataGenerator#frac
     * @since 3.0.0
     *
     * @return {number} A random real number between 0 and 1.
     */
    frac: function ()
    {
        // 2^-53
        return this.rnd() + (this.rnd() * 0x200000 | 0) * 1.1102230246251565e-16;
    },

    /**
     * Returns a random real number between 0 and 2^32.
     *
     * @method Phaser.Math.RandomDataGenerator#real
     * @since 3.0.0
     *
     * @return {number} A random real number between 0 and 2^32.
     */
    real: function ()
    {
        return this.integer() + this.frac();
    },

    /**
     * Returns a random integer between and including min and max.
     *
     * @method Phaser.Math.RandomDataGenerator#integerInRange
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    integerInRange: function (min, max)
    {
        return Math.floor(this.realInRange(0, max - min + 1) + min);
    },

    /**
     * Returns a random integer between and including min and max.
     * This method is an alias for RandomDataGenerator.integerInRange.
     *
     * @method Phaser.Math.RandomDataGenerator#between
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    between: function (min, max)
    {
        return Math.floor(this.realInRange(0, max - min + 1) + min);
    },

    /**
     * Returns a random real number between min and max.
     *
     * @method Phaser.Math.RandomDataGenerator#realInRange
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    realInRange: function (min, max)
    {
        return this.frac() * (max - min) + min;
    },

    /**
     * Returns a random real number between -1 and 1.
     *
     * @method Phaser.Math.RandomDataGenerator#normal
     * @since 3.0.0
     *
     * @return {number} A random real number between -1 and 1.
     */
    normal: function ()
    {
        return 1 - (2 * this.frac());
    },

    /**
     * Returns a valid RFC4122 version4 ID hex string from https://gist.github.com/1308368
     *
     * @method Phaser.Math.RandomDataGenerator#uuid
     * @since 3.0.0
     *
     * @return {string} A valid RFC4122 version4 ID hex string
     */
    uuid: function ()
    {
        var a = '';
        var b = '';

        for (b = a = ''; a++ < 36; b += ~a % 5 | a * 3 & 4 ? (a ^ 15 ? 8 ^ this.frac() * (a ^ 20 ? 16 : 4) : 4).toString(16) : '-')
        {
            // eslint-disable-next-line no-empty
        }

        return b;
    },

    /**
     * Returns a random element from within the given array.
     *
     * @method Phaser.Math.RandomDataGenerator#pick
     * @since 3.0.0
     *
     * @param {array} array - The array to pick a random element from.
     *
     * @return {any} A random member of the array.
     */
    pick: function (array)
    {
        return array[this.integerInRange(0, array.length - 1)];
    },

    /**
     * Returns a sign to be used with multiplication operator.
     *
     * @method Phaser.Math.RandomDataGenerator#sign
     * @since 3.0.0
     *
     * @return {number} -1 or +1.
     */
    sign: function ()
    {
        return this.pick(this.sign);
    },

    /**
     * Returns a random element from within the given array, favoring the earlier entries.
     *
     * @method Phaser.Math.RandomDataGenerator#weightedPick
     * @since 3.0.0
     *
     * @param {array} array - The array to pick a random element from.
     *
     * @return {any} A random member of the array.
     */
    weightedPick: function (array)
    {
        return array[~~(Math.pow(this.frac(), 2) * (array.length - 1) + 0.5)];
    },

    /**
     * Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified.
     *
     * @method Phaser.Math.RandomDataGenerator#timestamp
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random timestamp between min and max.
     */
    timestamp: function (min, max)
    {
        return this.realInRange(min || 946684800000, max || 1577862000000);
    },

    /**
     * Returns a random angle between -180 and 180.
     *
     * @method Phaser.Math.RandomDataGenerator#angle
     * @since 3.0.0
     *
     * @return {number} A random number between -180 and 180.
     */
    angle: function ()
    {
        return this.integerInRange(-180, 180);
    },

    /**
     * Returns a random rotation in radians, between -3.141 and 3.141
     *
     * @method Phaser.Math.RandomDataGenerator#rotation
     * @since 3.0.0
     *
     * @return {number} A random number between -3.141 and 3.141
     */
    rotation: function ()
    {
        return this.realInRange(-3.1415926, 3.1415926);
    },

    /**
     * Gets or Sets the state of the generator. This allows you to retain the values
     * that the generator is using between games, i.e. in a game save file.
     *
     * To seed this generator with a previously saved state you can pass it as the
     * `seed` value in your game config, or call this method directly after Phaser has booted.
     *
     * Call this method with no parameters to return the current state.
     *
     * If providing a state it should match the same format that this method
     * returns, which is a string with a header `!rnd` followed by the `c`,
     * `s0`, `s1` and `s2` values respectively, each comma-delimited.
     *
     * @method Phaser.Math.RandomDataGenerator#state
     * @since 3.0.0
     *
     * @param {string} [state] - Generator state to be set.
     *
     * @return {string} The current state of the generator.
     */
    state: function (state)
    {
        if (typeof state === 'string' && state.match(/^!rnd/))
        {
            state = state.split(',');

            this.c = parseFloat(state[1]);
            this.s0 = parseFloat(state[2]);
            this.s1 = parseFloat(state[3]);
            this.s2 = parseFloat(state[4]);
        }

        return [ '!rnd', this.c, this.s0, this.s1, this.s2 ].join(',');
    }

});

module.exports = RandomDataGenerator;
<commit_msg>Fix property name conflict in Phaser.Math.RandomDataGenerator#sign<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../../utils/Class');

/**
 * @classdesc
 * [description]
 *
 * @class RandomDataGenerator
 * @memberOf Phaser.Math
 * @constructor
 * @since 3.0.0
 *
 * @param {array} [seeds] - [description]
 */
var RandomDataGenerator = new Class({

    initialize:

    function RandomDataGenerator (seeds)
    {
        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#c
         * @type {number}
         * @default 1
         * @private
         * @since 3.0.0
         */
        this.c = 1;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s0
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s0 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s1
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s1 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#s2
         * @type {number}
         * @default 0
         * @private
         * @since 3.0.0
         */
        this.s2 = 0;

        /**
         * Internal var.
         *
         * @name Phaser.Math.RandomDataGenerator#n
         * @type {number}
         * @default 0
         * @private
         * @since 3.2.0
         */
        this.n = 0;

        /**
         * [description]
         *
         * @name Phaser.Math.RandomDataGenerator#sign
         * @type {array}
         * @since 3.0.0
         */
        this.signs = [ -1, 1 ];

        if (seeds)
        {
            this.init(seeds);
        }
    },

    /**
     * Private random helper.
     *
     * @method Phaser.Math.RandomDataGenerator#rnd
     * @since 3.0.0
     * @private
     *
     * @return {number} [description]
     */
    rnd: function ()
    {
        var t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32

        this.c = t | 0;
        this.s0 = this.s1;
        this.s1 = this.s2;
        this.s2 = t - this.c;

        return this.s2;
    },

    /**
     * Internal method that creates a seed hash.
     *
     * @method Phaser.Math.RandomDataGenerator#hash
     * @since 3.0.0
     * @private
     *
     * @param {[type]} data - [description]
     *
     * @return {number} The hashed value.
     */
    hash: function (data)
    {
        var h;
        var n = this.n;

        data = data.toString();

        for (var i = 0; i < data.length; i++)
        {
            n += data.charCodeAt(i);
            h = 0.02519603282416938 * n;
            n = h >>> 0;
            h -= n;
            h *= n;
            n = h >>> 0;
            h -= n;
            n += h * 0x100000000;// 2^32
        }

        this.n = n;

        return (n >>> 0) * 2.3283064365386963e-10;// 2^-32
    },

    /**
     * [description]
     *
     * @method Phaser.Math.RandomDataGenerator#init
     * @since 3.0.0
     *
     * @param {string|array} seeds - [description]
     */
    init: function (seeds)
    {
        if (typeof seeds === 'string')
        {
            this.state(seeds);
        }
        else
        {
            this.sow(seeds);
        }
    },

    /**
     * Reset the seed of the random data generator.
     *
     * _Note_: the seed array is only processed up to the first `undefined` (or `null`) value, should such be present.
     *
     * @method Phaser.Math.RandomDataGenerator#sow
     * @since 3.0.0
     *
     * @param {any[]} seeds - The array of seeds: the `toString()` of each value is used.
     */
    sow: function (seeds)
    {
        // Always reset to default seed
        this.n = 0xefc8249d;
        this.s0 = this.hash(' ');
        this.s1 = this.hash(' ');
        this.s2 = this.hash(' ');
        this.c = 1;

        if (!seeds)
        {
            return;
        }

        // Apply any seeds
        for (var i = 0; i < seeds.length && (seeds[i] != null); i++)
        {
            var seed = seeds[i];

            this.s0 -= this.hash(seed);
            this.s0 += ~~(this.s0 < 0);
            this.s1 -= this.hash(seed);
            this.s1 += ~~(this.s1 < 0);
            this.s2 -= this.hash(seed);
            this.s2 += ~~(this.s2 < 0);
        }
    },

    /**
     * Returns a random integer between 0 and 2^32.
     *
     * @method Phaser.Math.RandomDataGenerator#integer
     * @since 3.0.0
     *
     * @return {number} A random integer between 0 and 2^32.
     */
    integer: function ()
    {
        // 2^32
        return this.rnd() * 0x100000000;
    },

    /**
     * Returns a random real number between 0 and 1.
     *
     * @method Phaser.Math.RandomDataGenerator#frac
     * @since 3.0.0
     *
     * @return {number} A random real number between 0 and 1.
     */
    frac: function ()
    {
        // 2^-53
        return this.rnd() + (this.rnd() * 0x200000 | 0) * 1.1102230246251565e-16;
    },

    /**
     * Returns a random real number between 0 and 2^32.
     *
     * @method Phaser.Math.RandomDataGenerator#real
     * @since 3.0.0
     *
     * @return {number} A random real number between 0 and 2^32.
     */
    real: function ()
    {
        return this.integer() + this.frac();
    },

    /**
     * Returns a random integer between and including min and max.
     *
     * @method Phaser.Math.RandomDataGenerator#integerInRange
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    integerInRange: function (min, max)
    {
        return Math.floor(this.realInRange(0, max - min + 1) + min);
    },

    /**
     * Returns a random integer between and including min and max.
     * This method is an alias for RandomDataGenerator.integerInRange.
     *
     * @method Phaser.Math.RandomDataGenerator#between
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    between: function (min, max)
    {
        return Math.floor(this.realInRange(0, max - min + 1) + min);
    },

    /**
     * Returns a random real number between min and max.
     *
     * @method Phaser.Math.RandomDataGenerator#realInRange
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random number between min and max.
     */
    realInRange: function (min, max)
    {
        return this.frac() * (max - min) + min;
    },

    /**
     * Returns a random real number between -1 and 1.
     *
     * @method Phaser.Math.RandomDataGenerator#normal
     * @since 3.0.0
     *
     * @return {number} A random real number between -1 and 1.
     */
    normal: function ()
    {
        return 1 - (2 * this.frac());
    },

    /**
     * Returns a valid RFC4122 version4 ID hex string from https://gist.github.com/1308368
     *
     * @method Phaser.Math.RandomDataGenerator#uuid
     * @since 3.0.0
     *
     * @return {string} A valid RFC4122 version4 ID hex string
     */
    uuid: function ()
    {
        var a = '';
        var b = '';

        for (b = a = ''; a++ < 36; b += ~a % 5 | a * 3 & 4 ? (a ^ 15 ? 8 ^ this.frac() * (a ^ 20 ? 16 : 4) : 4).toString(16) : '-')
        {
            // eslint-disable-next-line no-empty
        }

        return b;
    },

    /**
     * Returns a random element from within the given array.
     *
     * @method Phaser.Math.RandomDataGenerator#pick
     * @since 3.0.0
     *
     * @param {array} array - The array to pick a random element from.
     *
     * @return {any} A random member of the array.
     */
    pick: function (array)
    {
        return array[this.integerInRange(0, array.length - 1)];
    },

    /**
     * Returns a sign to be used with multiplication operator.
     *
     * @method Phaser.Math.RandomDataGenerator#sign
     * @since 3.0.0
     *
     * @return {number} -1 or +1.
     */
    sign: function ()
    {
        return this.pick(this.signs);
    },

    /**
     * Returns a random element from within the given array, favoring the earlier entries.
     *
     * @method Phaser.Math.RandomDataGenerator#weightedPick
     * @since 3.0.0
     *
     * @param {array} array - The array to pick a random element from.
     *
     * @return {any} A random member of the array.
     */
    weightedPick: function (array)
    {
        return array[~~(Math.pow(this.frac(), 2) * (array.length - 1) + 0.5)];
    },

    /**
     * Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified.
     *
     * @method Phaser.Math.RandomDataGenerator#timestamp
     * @since 3.0.0
     *
     * @param {number} min - The minimum value in the range.
     * @param {number} max - The maximum value in the range.
     *
     * @return {number} A random timestamp between min and max.
     */
    timestamp: function (min, max)
    {
        return this.realInRange(min || 946684800000, max || 1577862000000);
    },

    /**
     * Returns a random angle between -180 and 180.
     *
     * @method Phaser.Math.RandomDataGenerator#angle
     * @since 3.0.0
     *
     * @return {number} A random number between -180 and 180.
     */
    angle: function ()
    {
        return this.integerInRange(-180, 180);
    },

    /**
     * Returns a random rotation in radians, between -3.141 and 3.141
     *
     * @method Phaser.Math.RandomDataGenerator#rotation
     * @since 3.0.0
     *
     * @return {number} A random number between -3.141 and 3.141
     */
    rotation: function ()
    {
        return this.realInRange(-3.1415926, 3.1415926);
    },

    /**
     * Gets or Sets the state of the generator. This allows you to retain the values
     * that the generator is using between games, i.e. in a game save file.
     *
     * To seed this generator with a previously saved state you can pass it as the
     * `seed` value in your game config, or call this method directly after Phaser has booted.
     *
     * Call this method with no parameters to return the current state.
     *
     * If providing a state it should match the same format that this method
     * returns, which is a string with a header `!rnd` followed by the `c`,
     * `s0`, `s1` and `s2` values respectively, each comma-delimited.
     *
     * @method Phaser.Math.RandomDataGenerator#state
     * @since 3.0.0
     *
     * @param {string} [state] - Generator state to be set.
     *
     * @return {string} The current state of the generator.
     */
    state: function (state)
    {
        if (typeof state === 'string' && state.match(/^!rnd/))
        {
            state = state.split(',');

            this.c = parseFloat(state[1]);
            this.s0 = parseFloat(state[2]);
            this.s1 = parseFloat(state[3]);
            this.s2 = parseFloat(state[4]);
        }

        return [ '!rnd', this.c, this.s0, this.s1, this.s2 ].join(',');
    }

});

module.exports = RandomDataGenerator;
<|endoftext|>


<commit_before>const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");

module.exports = {
  entry: './src/index.tsx',
  plugins: [
    new CleanWebpackPlugin(),
    new ExtractTextWebpackPlugin("styles.css"),
    new HtmlWebpackPlugin({
      title: "Pink",
      inject: false,
      template: require('html-webpack-template'),
      appMountId: 'app',
      mobile: true,
    }),
  ],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: ExtractTextWebpackPlugin.extract(['css-loader'])
    },{
      test: /\.js$/,
      use: ['babel-loader'],
      exclude: /node_modules/
    },{
      test: /\.tsx?$/,
      use: ['ts-loader'],
      exclude: /node_modules/
    }]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js']
  },
  // externals: {
  //   'react': 'React',
  //   'react-dom': 'ReactDOM'
  // }
};<commit_msg>使用外部依赖引用react<commit_after>const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");

module.exports = {
  entry: './src/index.tsx',
  plugins: [
    new CleanWebpackPlugin(),
    new ExtractTextWebpackPlugin("styles.css"),
    new HtmlWebpackPlugin({
      title: "Pink",
      inject: false,
      template: require('html-webpack-template'),
      appMountId: 'app',
      mobile: true,
      scripts: [
        '//unpkg.com/react@16/umd/react.development.js',
        '//unpkg.com/react-dom@16/umd/react-dom.development.js'
      ]
    }),
  ],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: ExtractTextWebpackPlugin.extract(['css-loader'])
    },{
      test: /\.js$/,
      use: ['babel-loader'],
      exclude: /node_modules/
    },{
      test: /\.tsx?$/,
      use: ['ts-loader'],
      exclude: /node_modules/
    }]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js']
  },
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
};<|endoftext|>